* test(#4462): expose whitespace workstream scope * fix(#4462): normalize the workstream environment scope * docs(#4462): add changeset for #4509 * fix(#4462): normalize sibling planning scope readers * test(#4462): cover workstream normalization properties * fix(#4462): share normalized workstream resolution --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
@@ -2948,6 +2948,28 @@ describe('phase add --ws workstream-scoped allocation vs sibling git worktrees (
|
||||
);
|
||||
});
|
||||
|
||||
test('whitespace-only GSD_WORKSTREAM uses the root sibling horizon (#4462)', () => {
|
||||
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4462-blank-ws-'));
|
||||
activeDirs.push(repoDir);
|
||||
initWsRepo(repoDir, 2, 'ws-alpha', 39);
|
||||
addSiblingAtHead(repoDir);
|
||||
|
||||
const result = runGsdTools(
|
||||
['query', 'phase.add', 'Root next'],
|
||||
repoDir,
|
||||
{ GSD_WORKSTREAM: ' ' },
|
||||
);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.phase_number, 3);
|
||||
assert.strictEqual(output.directory, '.planning/phases/03-root-next');
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(repoDir, '.planning', 'workstreams', ' ')),
|
||||
'an effectively-empty env value must not mint a whitespace-named workstream',
|
||||
);
|
||||
});
|
||||
|
||||
test('phase next-decimal --ws is unaffected (planningDir-scoped already)', () => {
|
||||
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4225-dec-'));
|
||||
activeDirs.push(repoDir);
|
||||
@@ -6203,6 +6225,22 @@ describe('#2028 — phase complete milestone-end + workstream guard', () => {
|
||||
assert.match(result.error || '', /workstream|--ws/i, 'error should name the workstream requirement');
|
||||
});
|
||||
|
||||
test('refuses to write root when GSD_WORKSTREAM is whitespace only', (t) => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'workstreams', 'alpha'), { recursive: true });
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# State\n');
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap\n\n### Phase 1: A\n**Goal:** x\n');
|
||||
const previous = process.env.GSD_WORKSTREAM;
|
||||
t.after(() => {
|
||||
if (previous === undefined) delete process.env.GSD_WORKSTREAM;
|
||||
else process.env.GSD_WORKSTREAM = previous;
|
||||
});
|
||||
process.env.GSD_WORKSTREAM = ' \t ';
|
||||
|
||||
const result = runGsdTools('phase complete 1', tmpDir);
|
||||
assert.equal(result.success, false, 'a whitespace workstream must not write the shared root');
|
||||
assert.match(result.error || '', /workstream|--ws/i);
|
||||
});
|
||||
|
||||
// An explicit --ws satisfies the guard (it sets GSD_WORKSTREAM upstream) AND
|
||||
// targets that workstream — the write must land in the workstream's own
|
||||
// STATE.md/ROADMAP.md, leaving root untouched.
|
||||
|
||||
Reference in New Issue
Block a user