* fix(#4456): forward --ws to every downstream new-milestone.md call
new-milestone.md's Step 1 parses --ws <name> into GSD_WS, but each
workflow step's bash fence is a separate shell invocation — GSD_WS set in
Step 1 never survived to Steps 5, 6, or 7. Four call sites never
forwarded it: init.new-milestone (both calls), state.milestone-switch,
and both phases.clear branches. Under GSD_WORKSTREAM env or a stored
session pointer differing from the explicitly requested --ws, every
downstream operation silently operated on the wrong workstream (or root)
instead of the one the caller asked for.
Confirmed --ws is a universally-parsed CLI flag (gsd-core/bin/gsd-tools.cjs:
4867, resolveActiveWorkstream) — stripped from argv and written into
process.env.GSD_WORKSTREAM for the rest of that process, so appending it to
ANY gsd_run query call works uniformly. Fixed by persisting GSD_WS to
.planning/.gsd-ws-arg right after Step 1 parses it (mirroring the
established .gsd-outgoing-milestone round-trip idiom this same file
already uses for the identical cross-fence problem), reading it back in
each later step, and appending it unquoted (matching the ${GSD_WS}
splicing convention documented in workstream-flag.md). Cleaned up after
its last use in Step 7.
Bundled, in-scope fixes found while implementing the above (per this
repo's no-defer policy):
- Step 6's phase-archive `git add .planning/milestones/ .planning/phases/`
hardcoded literal ROOT paths — both directories are workstream-scoped
(matching cmdMilestoneComplete's established #1911 precedent), so under
a workstream this staged nothing real. Added phases_dir/archive_dir
fields to cmdInitNewMilestone and resolved through them instead.
- Step 6's milestone-start commit hardcoded .planning/STATE.md — also
workstream-scoped, so it would commit the wrong (or a stale) file under
a workstream. Resolved through init.new-milestone's existing state_path
field instead; PROJECT.md correctly stays a literal-shaped-but-resolved
root path (shared, per the #4455 follow-up already merged).
- cmdInitNewMilestone's config_path field: config.json is ALSO a shared
file (marked `# Shared` in workstream-flag.md's directory diagram, same
as PROJECT.md) but was resolved via the workstream-aware planningDir —
fixed alongside cmdInitNewProject's identical instance of the same bug
(found via grep, matching the precedent from the #4455 follow-up of
fixing every occurrence of an identically-evidenced bug uniformly).
Verified: direct CLI invocation confirms phases_dir/archive_dir/state_path
resolve into the workstream while project_path/config_path stay root under
GSD_WORKSTREAM=alpha. Manual bash-fence execution of every modified fence
(Step 1 parse+persist, Step 5 forwarding, both phases.clear branches, the
git add fence, Step 7's forward+cleanup, the commit fence) confirms correct
behavior in both flat and --ws modes, including two flags composing
together (--archive-version + --ws; --reset-phase-numbers + --ws).
Rewrote the pre-existing "step 6: commit stages PROJECT.md" test, which
asserted the literal (buggy) --files string verbatim — it now asserts the
resolved paths via a JSON-returning stub, and gained isolated per-test tmp
dirs (the prior version ran with no explicit cwd, at real risk of writing
a stray .gsd-ws-arg into this repo's own .planning/ once Step 1's fence
started performing a real write).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(#4456): Steps 9/10 also commit workstream-scoped files via literal root paths
A fresh isolated code-review pass on the first version of this fix found
the identical bug in two more places, missed in the initial sweep:
- Step 9's requirements commit (`gsd_run query commit ... --files
.planning/REQUIREMENTS.md`) and Step 10's roadmap commit (`--files
.planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md`)
both hardcoded literal ROOT paths for files that are workstream-scoped.
- Worse: `.planning/.gsd-ws-arg` was being deleted at the end of Step 7,
but Steps 9 and 10 run AFTER Step 7 and still needed to re-read it —
the round-trip mechanism this fix builds was already gone before its
two remaining consumers ran.
Fixed by moving the `.gsd-ws-arg` cleanup to Step 10 (its true last
consumer, after the roadmap commit) and adding the same
fetch-then-_gsd_field-extract pattern already used in Step 6 to Steps 9
and 10, resolving `requirements_path`/`roadmap_path`/`state_path` through
`init.new-milestone $GSD_WS_ARG` instead of literal paths.
Also fixed (MEDIUM, same review pass): Step 1's `.gsd-ws-arg` write had
no `2>/dev/null || true`, unlike every other round-trip write in this
same file — brought into line with the established idiom.
Verified: reproduced the pre-fix bug directly (Step 9/10 fences echoing
the literal root paths regardless of --ws), confirmed both fences now
resolve the workstream-scoped paths correctly, and confirmed the
round-trip file survives Step 7 and is only removed after Step 10.
Updated the Step 7 test that previously asserted premature cleanup
(inverted to assert the file survives); added new coverage for Steps 9
and 10 in both flat and --ws modes.
Two remaining LOW/pre-existing findings from the same review pass,
deliberately left as-is: `phase_archive_path` (src/init.cts, untouched by
this diff) resolves via the same root-only `getLatestCompletedMilestone`
this fix's earlier commit already declined to touch, for the same
genuine-product-intent-ambiguity reason (workstream-scoped vs
project-pooled "latest completed milestone" is not resolvable from the
code alone). `.planning/research/` staying root-scoped in the #222
self-heal prose is consistent with the existing (unchanged) `research_dir`
field, not a new inconsistency.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(#4456): revert wrong config_path change; fix isolated-cwd test env
gsd-test caught two real regressions from this fix's earlier commits:
1. config_path is NOT shared like PROJECT.md. The prior commit's
grep-and-replace ("fix six more functions with the identical bug")
also touched cmdInitExecutePhase's config_path (a fourth call site
beyond the two I'd manually checked) — but tests/init.test.cjs's
pre-existing, ADR-0006-governed "init handlers honor GSD_WORKSTREAM"
coverage explicitly asserts config_path IS workstream-scoped for
execute-phase/plan-phase/phase-op/milestone-op. workstream-flag.md's
"# Shared" marking for config.json is stale (the same class of
staleness already found for milestones/ during the #4455 follow-up);
ADR-0006 plus its real, passing tests is the authoritative source.
Reverted config_path to the plain workstream-aware planningDir(cwd)
in all four functions it was wrongly changed in.
2. Isolating cwd to a tmpDir (needed once Step 1's fence started
performing a real .gsd-ws-arg write) broke the runtime-launcher
preamble's own gsd-tools.cjs discovery — no git repo at an isolated
tmpDir, no global gsd_run on the CI bench's PATH. Fixed by passing
RUNTIME_DIR explicitly in every isolated-cwd test's env, matching
the preamble's own documented override precedence.
Verified: direct CLI invocation confirms execute-phase's config_path is
workstream-scoped again under GSD_WORKSTREAM=wsx; the RUNTIME_DIR fix
confirmed against a stripped PATH (no global gsd_run), matching the
bench condition that surfaced the original failure.
Emitted-Drift-Ack-Growth: new-milestone.md — #4456 forwards --ws to every downstream gsd_run call across 7 fences (Steps 1/5/6x3/7/9/10), adding a persisted round-trip file plus resolved-path fetches that replace several hardcoded literal paths
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* docs(#4456): backfill changeset PR number and correct final scope
pr: 0 -> pr: 4545, and removed the changeset's claim that config.json
is a shared file -- that was the change this same PR later reverted
after gsd-test caught it contradicting ADR-0006's established,
workstream-scoped config_path contract.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(#4456): baseline the 10 new SC2086 findings from --ws forwarding
The lint-tests CI job failed with a hard exit 1. Diagnosis (not assumed):
the log's two `fatal: ambiguous argument 'origin/next...HEAD'` git errors
(lines 244/248) are a red herring — both belong to
lint-removed-but-needed.cjs, which prints its own "could not resolve
origin/next, skipping" message and exits gracefully, exactly like the
already-handled two-dot-form error from lint-fix-has-regression-tests
earlier in the same log. Neither contributes to the actual failure.
The real cause is lint-workflow-shellcheck: this fix's new fences append
$GSD_WS_ARG unquoted to gsd_run calls (deliberately, so it splits into 0
or 2 argv tokens — the same idiom gsd-core/workflows/verify-work.md
already uses for ${GSD_WS} and already has baselined). ShellCheck
correctly flags each as SC2086, and lint-workflow-shellcheck.cjs's
baseline is a deliberate ratchet (#4109) requiring new findings to be
explicitly accepted, not auto-passed. new-milestone.md previously had
zero baselined SC2086 findings, so all 10 new (correct, intentional)
occurrences were reported as new and failed the gate.
Added 10 {file, code, message} entries to
scripts/lint-workflow-shellcheck-baseline.json for
gsd-core/workflows/new-milestone.md's SC2086 findings, matching the
established, already-accepted precedent for the identical pattern in
verify-work.md. No source or workflow file changed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>