fix(#4342): run the bug-167 routing test in a fixture project, not the developer's (#4387)

The test called runGsdTools with its default cwd — the test process's own
working directory — and an inherited HOME, so the child read the checkout's
real .planning/ and the developer's real ~/.gsd/defaults.json. testEnvBase()
blanks the config-LOCATION env keys but sandboxes neither cwd nor HOME.

On a checkout that has workstreams with no active pointer, `init.progress`
exits non-zero and the FIRST assertion fails, so the routing comparison the
test exists for was never evaluated:

  init.progress failed: Error: init.progress requires a workstream in
  workstream mode — no active workstream is set ... Available workstreams: alpha

Reproduced byte-for-byte by adding .planning/workstreams/alpha/ to the
checkout: red on next, green here.

The invariant under test — `query <cmd>` and `<cmd>` returning identical
payloads — is independent of project state, so a plain createTempProject()
fixture is enough, with HOME/USERPROFILE pointed at it (the idiom runGsdTools's
own doc comment prescribes).

Two assertions pin the sandbox deterministically rather than conditionally:
the fixture HAS a .planning/ and the repo checkout does not, so dropping the
cwd override fails on every lane — CI included, where the ambient state that
exposed the bug is absent.

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Michel Moreira
2026-09-07 11:34:37 -03:00
committed by GitHub
parent ae40529d31
commit 2388e6ab34

View File

@@ -962,20 +962,48 @@ describe('CommandRoutingHub — exitReason? field on InvalidArgs (#1644 / amendm
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { runGsdTools } = require('./helpers.cjs');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
test('bug #167: query meta-command prefixes direct gsd-tools calls', () => {
const direct = runGsdTools(['init.progress']);
// #4342: this ran gsd-tools with runGsdTools's default cwd — the checkout's own
// working directory — and an inherited HOME, so the child read the developer's
// real .planning/config.json and ~/.gsd/defaults.json. On a machine whose real
// project enables workstream mode with no active workstream, `init.progress`
// exits non-zero and the FIRST assertion fails, so the routing comparison this
// test exists for was never reached. testEnvBase() blanks the config-LOCATION
// env keys but sandboxes neither cwd nor HOME.
//
// The routing invariant is about `query <cmd>` and `<cmd>` agreeing, which is
// independent of project state — so the fixture only has to be a project whose
// state is known and unaffected by the developer's.
test('bug #167: query meta-command prefixes direct gsd-tools calls', (t) => {
const fixture = createTempProject('gsd-4342-routing-');
t.after(() => cleanup(fixture));
// HOME/USERPROFILE point at the fixture so ~/.gsd/defaults.json resolves
// inside it (absent) rather than in the developer's home — the idiom
// runGsdTools's own doc comment prescribes for exactly this.
const sandbox = { HOME: fixture, USERPROFILE: fixture };
const direct = runGsdTools(['init.progress'], fixture, sandbox);
assert.equal(direct.success, true, `init.progress failed: ${direct.error || direct.output}`);
const meta = runGsdTools(['query', 'init.progress']);
const meta = runGsdTools(['query', 'init.progress'], fixture, sandbox);
assert.equal(meta.success, true, `query init.progress failed: ${meta.error || meta.output}`);
const directPayload = JSON.parse(direct.output);
assert.deepEqual(
JSON.parse(meta.output),
JSON.parse(direct.output),
directPayload,
'query-prefixed and direct invocations should return identical init.progress payloads'
);
// Pin the sandbox itself, deterministically rather than conditionally: the
// fixture HAS a .planning/ and the repo checkout does NOT, so if the cwd
// override is ever dropped this fails on every lane — including CI, where the
// ambient state that exposed the bug is absent.
assert.equal(directPayload.planning_exists, true,
'the child must run in the fixture project, not in the checkout');
assert.equal(directPayload.phase_count, 0,
'the fixture has no phases — a non-zero count means a real project was read');
});
});
}