From ada79bee97c839f285915d409cb778821113b710 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Fri, 17 Jul 2026 06:48:13 -0400 Subject: [PATCH] fix(#2308): make new-milestone workstream-aware; stop clobbering shared PROJECT.md (#2338) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(#2308): make new-milestone workstream-aware; stop clobbering shared PROJECT.md Step 4 rewrote the `## Current Milestone` heading in the shared root PROJECT.md unconditionally. references/workstream-flag.md marks PROJECT.md `# Shared`, and per-workstream milestone state already lives in the workstream's own STATE.md / ROADMAP.md / REQUIREMENTS.md. With parallel milestones — the sanctioned design — whichever workstream ran new-milestone last silently won the shared heading. Step 4 is now skipped when a workstream is active; step 6 no longer stages PROJECT.md in that mode (cmdCommit returns nothing_to_commit rather than failing when a staged path is unchanged). Also fixes a second defect found while diagnosing this, same root cause (the workflow was workstream-unaware): step 1 parsed only --reset-phase-numbers and the milestone name, so GSD_WS was never set — yet ${GSD_WS} was interpolated at the routing lines. It always expanded to empty, so `/gsd:new-milestone --ws x` suggested `/gsd:discuss-phase [N]` with the workstream scope silently dropped, violating the routing-propagation contract. Step 1 now parses --ws using the established idiom from verify-work.md. Guard is keyed on GSD_WS, not $GSD_WORKSTREAM: the runtime launcher does not export the latter and it is only priority 2 of 5 in resolution, so it would miss the --ws flag case that is the actual repro. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01SLufH5sDuqA1AiEGu45cuA * test(#2308): regenerate install goldens for the new-milestone workflow change gsd-core/workflows/ ships as an installed artifact, so new-milestone.md's content hash is pinned in all 18 runtime golden fixtures. Only that hash changed. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01SLufH5sDuqA1AiEGu45cuA * fix(#2308): address review — inert step-6 guard, dropped Evolution repair, tautological tests Independent review found the first pass was partly cosmetic: 1. The step-6 `if [ -n "$GSD_WS" ]` branch was INERT. GSD_WS is assigned in step 1's shell and each step's bash block runs in its own shell — this file already proves it, since step 5 round-trips OUTGOING_MILESTONE through a file for exactly that reason (#2288). The guard read an unset variable, always took the flat branch, and staged PROJECT.md anyway. Rather than re-deriving GSD_WS in step 6, the branch is removed entirely: step 4 Part A's guard is what protects the shared heading, so post-guard the only content PROJECT.md can carry is Part B's idempotent Evolution backfill — which must be staged, not stranded. A regression test now asserts no cross-step GSD_WS branch returns. 2. Skipping ALL of step 4 also dropped the `## Evolution` structural repair — a shared, idempotent backfill that is not workstream state. A pre-Evolution project running only `--ws` would never get the section that transition and complete-milestone expect. Step 4 is now split: Part A (milestone-state write) is workstream-guarded; Part B (Evolution) always runs. 3. The tests were tautological prose-pinning — including one asserting a comment mentions "#2308". The step-6 test asserted the guard's TEXT was present, so it passed on the inert guard it existed to catch. Replaced with executable tests that extract the step-1 and step-6 fences and run them under bash with stubbed gsd_run, asserting real parse and --files behavior. 4. --ws is now stripped from the milestone name (step 1 previously left "--ws search" in the remaining text), and documented in argument-hint, help/modes/full.md, and docs/COMMANDS.md. 5. Changeset no longer overstates: --ws reaches the prose guard and routing hints only, not the SDK calls (state.milestone-switch/phases.clear/init.new-milestone still take no ${GSD_WS} — out of scope here). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01SLufH5sDuqA1AiEGu45cuA * chore(#2308): regenerate SKILL.md, goldens, and size baseline for the argument-hint change skills/gsd-new-milestone/SKILL.md is generated from commands/gsd/new-milestone.md, so documenting --ws in the argument-hint made it stale (caught by lint:ci's gen-plugin-skills --check). Regenerated it plus the install goldens and workflow size baseline, since commands/, skills/, and gsd-core/workflows/ all ship. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01SLufH5sDuqA1AiEGu45cuA * docs(#2308): backfill PR number 2338 into changeset Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01SLufH5sDuqA1AiEGu45cuA --------- Co-authored-by: Claude Opus 4.8 (1M context) --- .changeset/serene-herons-rally.md | 5 + commands/gsd/new-milestone.md | 2 +- docs/COMMANDS.md | 2 + gsd-core/workflows/help/modes/full.md | 2 + gsd-core/workflows/new-milestone.md | 26 ++- skills/gsd-new-milestone/SKILL.md | 2 +- .../golden-install-parity/antigravity.json | 4 +- .../golden-install-parity/augment.json | 6 +- .../golden-install-parity/claude-local.json | 6 +- .../golden-install-parity/claude.json | 6 +- .../fixtures/golden-install-parity/cline.json | 4 +- .../golden-install-parity/codebuddy.json | 6 +- .../fixtures/golden-install-parity/codex.json | 4 +- .../golden-install-parity/copilot.json | 6 +- .../golden-install-parity/cursor.json | 4 +- .../golden-install-parity/hermes.json | 6 +- .../fixtures/golden-install-parity/kilo.json | 6 +- .../fixtures/golden-install-parity/kimi.json | 4 +- .../golden-install-parity/opencode.json | 6 +- tests/fixtures/golden-install-parity/pi.json | 4 +- .../fixtures/golden-install-parity/qwen.json | 6 +- .../fixtures/golden-install-parity/trae.json | 4 +- .../golden-install-parity/windsurf.json | 4 +- .../fixtures/golden-install-parity/zcode.json | 8 +- tests/new-milestone-clear-phases.test.cjs | 161 +++++++++++++++++- tests/workflow-size-baseline.json | 2 +- 26 files changed, 240 insertions(+), 56 deletions(-) create mode 100644 .changeset/serene-herons-rally.md diff --git a/.changeset/serene-herons-rally.md b/.changeset/serene-herons-rally.md new file mode 100644 index 000000000..2f2356975 --- /dev/null +++ b/.changeset/serene-herons-rally.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 2338 +--- +**`/gsd:new-milestone --ws ` no longer overwrites the shared PROJECT.md milestone heading** — in workstream mode the shared `.planning/PROJECT.md` had its `## Current Milestone` heading rewritten with one workstream's milestone, so with parallel workstreams whichever ran last silently won the shared heading. The milestone-state write in Step 4 is now skipped when a workstream is active, and the commit no longer stages PROJECT.md. The `--ws` flag is also now parsed into `${GSD_WS}`, which previously expanded to empty and silently dropped workstream scope from the suggested next-step routing hints. diff --git a/commands/gsd/new-milestone.md b/commands/gsd/new-milestone.md index fe75900d3..1c0501adf 100644 --- a/commands/gsd/new-milestone.md +++ b/commands/gsd/new-milestone.md @@ -1,7 +1,7 @@ --- name: gsd:new-milestone description: Start a new milestone cycle — update PROJECT.md and route to requirements -argument-hint: "[milestone name, e.g., 'v1.1 Notifications']" +argument-hint: "[milestone name, e.g., 'v1.1 Notifications'] [--ws ]" allowed-tools: - Read - Write diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 1f50dc0f2..65b2761ef 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -483,6 +483,7 @@ Start next version cycle. |----------|----------|-------------| | `name` | No | Milestone name | | `--reset-phase-numbers` | No | Restart the new milestone at Phase 1 and archive old phase dirs before roadmapping | +| `--ws ` | No | Scope the milestone to a workstream; skips the shared `PROJECT.md` write | **Prerequisites:** Previous milestone completed **Produces:** Updated `PROJECT.md`, new `REQUIREMENTS.md`, new `ROADMAP.md` @@ -491,6 +492,7 @@ Start next version cycle. /gsd-new-milestone # Interactive /gsd-new-milestone "v2.0 Mobile" # Named milestone /gsd-new-milestone --reset-phase-numbers "v2.0 Mobile" # Restart milestone numbering at 1 +/gsd-new-milestone --ws search "v2.0 Search" # Scope to a workstream ``` --- diff --git a/gsd-core/workflows/help/modes/full.md b/gsd-core/workflows/help/modes/full.md index 534777bdf..a3748f1dd 100644 --- a/gsd-core/workflows/help/modes/full.md +++ b/gsd-core/workflows/help/modes/full.md @@ -250,11 +250,13 @@ Start a new milestone through unified flow. - Requirements definition with scoping - Roadmap creation with phase breakdown - Optional `--reset-phase-numbers` flag restarts numbering at Phase 1 and archives old phase dirs first for safety +- Optional `--ws ` flag scopes the milestone to a workstream and skips the shared `PROJECT.md` write Mirrors `/gsd:new-project` flow for brownfield projects (existing PROJECT.md). Usage: `/gsd:new-milestone "v2.0 Features"` Usage: `/gsd:new-milestone --reset-phase-numbers "v2.0 Features"` +Usage: `/gsd:new-milestone --ws search "v2.0 Search"` **`/gsd:complete-milestone `** Archive completed milestone and prepare for next version. diff --git a/gsd-core/workflows/new-milestone.md b/gsd-core/workflows/new-milestone.md index b48d5c5f3..d6d53aefb 100644 --- a/gsd-core/workflows/new-milestone.md +++ b/gsd-core/workflows/new-milestone.md @@ -22,10 +22,20 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo ## 1. Load Context Parse `$ARGUMENTS` before doing anything else: -- `--reset-phase-numbers` flag → opt into restarting roadmap phase numbering at `1` -- remaining text → use as milestone name if present -If the flag is absent, keep the current behavior of continuing phase numbering from the previous milestone. +- `--reset-phase-numbers` flag → opt into restarting roadmap phase numbering at `1`. If absent, keep the current behavior of continuing phase numbering from the previous milestone. +- `--ws ` flag → active workstream scope, parsed into `GSD_WS` +- remaining text, with `--ws ` stripped → use as milestone name if present, captured into `MILESTONE_ARG` + +Parse `GSD_WS` and `MILESTONE_ARG` using the established idiom (see `verify-work.md`): + +```bash +GSD_WS="" +echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+') +MILESTONE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs) +``` + +`GSD_WS` must chain to every downstream routing suggestion in this workflow (Step 4's shared-file guard, and the `/gsd:discuss-phase`/`/gsd:plan-phase` routing hints below) per the routing-propagation contract in `references/workstream-flag.md` — never let it silently drop. - Read PROJECT.md (existing project, validated requirements, decisions) - Read MILESTONES.md (what shipped previously) @@ -135,6 +145,10 @@ AskUserQuestion: ## 4. Update PROJECT.md +PROJECT.md is shared across workstreams (`references/workstream-flag.md` marks it `# Shared` in the directory diagram). This step has two independently-scoped parts — only Part A is workstream-guarded. + +**Part A — milestone-state write (skip when a workstream is active).** Skip Part A if `GSD_WS` is non-empty (parsed in Step 1). The active workstream's own `.planning/workstreams//STATE.md`/`ROADMAP.md`/`REQUIREMENTS.md` already carry this milestone's state. Writing a `## Current Milestone` heading here would clobber the shared file, and with parallel milestones across workstreams, whichever workstream runs `new-milestone` last would silently win the shared heading (#2308). In flat mode (`GSD_WS` empty), run Part A exactly as before: + Add/update: ```markdown @@ -150,7 +164,7 @@ Add/update: Update Active requirements section and "Last updated" footer. -Ensure the `## Evolution` section exists in PROJECT.md. If missing (projects created before this feature), add it before the footer: +**Part B — Evolution structural repair (always runs, regardless of `GSD_WS`).** `## Evolution` is a shared, idempotent structural section, not workstream state — a pre-Evolution project must be backfilled whether or not a workstream is active, so this part is NOT covered by Part A's skip. Ensure the `## Evolution` section exists in PROJECT.md. If missing (projects created before this feature), add it before the footer: ```markdown ## Evolution @@ -247,6 +261,8 @@ Stage the phase archive move + source removal so they land in the same commit as git add .planning/milestones/ .planning/phases/ 2>/dev/null || true ``` +Stage PROJECT.md in both modes. Step 4's Part A guard — not this commit — is what protects the shared `## Current Milestone` heading (#2308): when a workstream is active Part A never writes it, so the only change PROJECT.md can carry here is Part B's idempotent `## Evolution` backfill, which must be committed rather than stranded as a dangling edit. Do NOT reintroduce a `[ -n "$GSD_WS" ]` branch around this commit: `GSD_WS` is set in Step 1's shell and each step's bash block runs in its own shell (the same reason Step 5 round-trips `OUTGOING_MILESTONE` through a file), so such a guard reads an unset variable, always takes the flat-mode branch, and only appears to work. + ```bash gsd_run query commit "docs: start milestone v[X.Y] [Name]" --files .planning/PROJECT.md .planning/STATE.md ``` @@ -659,7 +675,7 @@ Also: `/gsd:plan-phase [N] ${GSD_WS}` — skip discussion, plan directly -- [ ] PROJECT.md updated with Current Milestone section +- [ ] PROJECT.md updated with Current Milestone section (skipped when a workstream is active — shared file, see Step 4) - [ ] STATE.md reset for new milestone - [ ] MILESTONE-CONTEXT.md consumed and deleted (if existed) - [ ] Research completed (if selected) — 4 parallel agents, milestone-aware diff --git a/skills/gsd-new-milestone/SKILL.md b/skills/gsd-new-milestone/SKILL.md index 1566bad62..586b039f8 100644 --- a/skills/gsd-new-milestone/SKILL.md +++ b/skills/gsd-new-milestone/SKILL.md @@ -1,7 +1,7 @@ --- name: gsd-new-milestone description: "Start a new milestone cycle — update PROJECT.md and route to requirements" -argument-hint: "[milestone name, e.g., 'v1.1 Notifications']" +argument-hint: "[milestone name, e.g., 'v1.1 Notifications'] [--ws ]" allowed-tools: - Read - Write diff --git a/tests/fixtures/golden-install-parity/antigravity.json b/tests/fixtures/golden-install-parity/antigravity.json index 9f9d0ff2b..e97d690f5 100644 --- a/tests/fixtures/golden-install-parity/antigravity.json +++ b/tests/fixtures/golden-install-parity/antigravity.json @@ -250,7 +250,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "be05e56b2c5ee2c0", - "gsd-core/workflows/help/modes/full.md": "bf7b148215259e6e", + "gsd-core/workflows/help/modes/full.md": "80d03a09b561766b", "gsd-core/workflows/help/modes/topic.md": "0bf9ab39d7044d69", "gsd-core/workflows/import.md": "41c62ae199209a68", "gsd-core/workflows/inbox.md": "a448220c548f27bc", @@ -263,7 +263,7 @@ "gsd-core/workflows/map-codebase.md": "c81444eb0b2e038b", "gsd-core/workflows/milestone-summary.md": "c4620853fe489126", "gsd-core/workflows/mvp-phase.md": "5f0e355ff4583d67", - "gsd-core/workflows/new-milestone.md": "949e6046ebdfe0e0", + "gsd-core/workflows/new-milestone.md": "7505d35307065c10", "gsd-core/workflows/new-project.md": "f336b2da5c3963f0", "gsd-core/workflows/new-workspace.md": "21bfae58b9cc2990", "gsd-core/workflows/next.md": "8beefb4335dd7c41", diff --git a/tests/fixtures/golden-install-parity/augment.json b/tests/fixtures/golden-install-parity/augment.json index 579dacf28..9dfeb2144 100644 --- a/tests/fixtures/golden-install-parity/augment.json +++ b/tests/fixtures/golden-install-parity/augment.json @@ -67,7 +67,7 @@ "commands/gsd-mempalace-recall.md": "38716c0983a3ef9c", "commands/gsd-milestone-summary.md": "ff5e11e6b33d4b5e", "commands/gsd-mvp-phase.md": "ffc5905aed8f450a", - "commands/gsd-new-milestone.md": "e9ddaec1dd29d023", + "commands/gsd-new-milestone.md": "82d075372a862d9a", "commands/gsd-new-project.md": "56552a324cff669e", "commands/gsd-next.md": "d9f43d56e1bca2ca", "commands/gsd-ns-context.md": "011c44e7aa46e64a", @@ -321,7 +321,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "924860e1f07defb0", "gsd-core/workflows/help/modes/default.md": "08a02976c0c5cc50", - "gsd-core/workflows/help/modes/full.md": "b26853b9911ad97b", + "gsd-core/workflows/help/modes/full.md": "110608f0352bb0a3", "gsd-core/workflows/help/modes/topic.md": "5c160093f3cbf35d", "gsd-core/workflows/import.md": "3d3fa603ceb8bc9f", "gsd-core/workflows/inbox.md": "437f981ef9ae7b26", @@ -334,7 +334,7 @@ "gsd-core/workflows/map-codebase.md": "83d750aade709983", "gsd-core/workflows/milestone-summary.md": "e0cdfddbd39a9043", "gsd-core/workflows/mvp-phase.md": "344dd300e0cc1e74", - "gsd-core/workflows/new-milestone.md": "bc020226377af22a", + "gsd-core/workflows/new-milestone.md": "83bf5593e94c3377", "gsd-core/workflows/new-project.md": "5ffeef49fe73013e", "gsd-core/workflows/new-workspace.md": "017688423110ed66", "gsd-core/workflows/next.md": "13fb800f2472d970", diff --git a/tests/fixtures/golden-install-parity/claude-local.json b/tests/fixtures/golden-install-parity/claude-local.json index 67b8cf9b2..ee9947361 100644 --- a/tests/fixtures/golden-install-parity/claude-local.json +++ b/tests/fixtures/golden-install-parity/claude-local.json @@ -66,7 +66,7 @@ "commands/gsd-mempalace-recall.md": "38716c0983a3ef9c", "commands/gsd-milestone-summary.md": "908509042caf5beb", "commands/gsd-mvp-phase.md": "1ef0d7c2871be49a", - "commands/gsd-new-milestone.md": "8ce4861325cd3862", + "commands/gsd-new-milestone.md": "0ec387159e1f7182", "commands/gsd-new-project.md": "d68b36481d09bfd4", "commands/gsd-next.md": "e976ddf80b6cb425", "commands/gsd-ns-context.md": "011c44e7aa46e64a", @@ -320,7 +320,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "be05e56b2c5ee2c0", - "gsd-core/workflows/help/modes/full.md": "8381c11db152a8a6", + "gsd-core/workflows/help/modes/full.md": "2388596fd5bcbd07", "gsd-core/workflows/help/modes/topic.md": "6e42db16f1568be9", "gsd-core/workflows/import.md": "cc21f3da36403ed3", "gsd-core/workflows/inbox.md": "91aac6360e1a8672", @@ -333,7 +333,7 @@ "gsd-core/workflows/map-codebase.md": "b11ca99a885e93ef", "gsd-core/workflows/milestone-summary.md": "99636900c216c8d2", "gsd-core/workflows/mvp-phase.md": "254baac57e85dca7", - "gsd-core/workflows/new-milestone.md": "841285575c6e795f", + "gsd-core/workflows/new-milestone.md": "33f43349346e86df", "gsd-core/workflows/new-project.md": "26907d3cf3630ed0", "gsd-core/workflows/new-workspace.md": "26615bf710f0a324", "gsd-core/workflows/next.md": "1193222c5618d3db", diff --git a/tests/fixtures/golden-install-parity/claude.json b/tests/fixtures/golden-install-parity/claude.json index b21c3b0c1..4d67c9049 100644 --- a/tests/fixtures/golden-install-parity/claude.json +++ b/tests/fixtures/golden-install-parity/claude.json @@ -249,7 +249,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "be05e56b2c5ee2c0", - "gsd-core/workflows/help/modes/full.md": "91c6683e5b5ff4f8", + "gsd-core/workflows/help/modes/full.md": "3d55c7266241f79d", "gsd-core/workflows/help/modes/topic.md": "6e42db16f1568be9", "gsd-core/workflows/import.md": "6e8acff8c3918795", "gsd-core/workflows/inbox.md": "91aac6360e1a8672", @@ -262,7 +262,7 @@ "gsd-core/workflows/map-codebase.md": "a45a9aa5c10aeeed", "gsd-core/workflows/milestone-summary.md": "ad8e69fcb3463ce4", "gsd-core/workflows/mvp-phase.md": "344dd300e0cc1e74", - "gsd-core/workflows/new-milestone.md": "7ec178aa9f921569", + "gsd-core/workflows/new-milestone.md": "fef60fedb5db9eb7", "gsd-core/workflows/new-project.md": "0ea70b800c86fde1", "gsd-core/workflows/new-workspace.md": "04356c9b675da7c9", "gsd-core/workflows/next.md": "ef30b829a6d5c5f6", @@ -384,7 +384,7 @@ "skills/gsd-mempalace-recall/SKILL.md": "c3f412d5a64f3921", "skills/gsd-milestone-summary/SKILL.md": "33d51a325d314f3a", "skills/gsd-mvp-phase/SKILL.md": "f9a1348c6c297579", - "skills/gsd-new-milestone/SKILL.md": "ac99ffb8a966fe9d", + "skills/gsd-new-milestone/SKILL.md": "24c97f2814d30501", "skills/gsd-new-project/SKILL.md": "e4d930027074d3b6", "skills/gsd-next/SKILL.md": "13e394affe675498", "skills/gsd-ns-context/SKILL.md": "3102e7ad9b60f182", diff --git a/tests/fixtures/golden-install-parity/cline.json b/tests/fixtures/golden-install-parity/cline.json index 91d41a7fc..94de68000 100644 --- a/tests/fixtures/golden-install-parity/cline.json +++ b/tests/fixtures/golden-install-parity/cline.json @@ -253,7 +253,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "924860e1f07defb0", "gsd-core/workflows/help/modes/default.md": "86f7a14ad06e2f6f", - "gsd-core/workflows/help/modes/full.md": "a53e85333cf61c36", + "gsd-core/workflows/help/modes/full.md": "e4f1ae9d0902dda0", "gsd-core/workflows/help/modes/topic.md": "5c160093f3cbf35d", "gsd-core/workflows/import.md": "96ec687c5cfe84ac", "gsd-core/workflows/inbox.md": "437f981ef9ae7b26", @@ -266,7 +266,7 @@ "gsd-core/workflows/map-codebase.md": "14c4995c73a671c7", "gsd-core/workflows/milestone-summary.md": "20e56b9e3902e1d2", "gsd-core/workflows/mvp-phase.md": "3e3936e1f247ac7c", - "gsd-core/workflows/new-milestone.md": "a85761d8c9f760be", + "gsd-core/workflows/new-milestone.md": "38370e363174b1da", "gsd-core/workflows/new-project.md": "66bf6135f54ece0e", "gsd-core/workflows/new-workspace.md": "f5eed5dd9a21878f", "gsd-core/workflows/next.md": "3f8b61a35587a021", diff --git a/tests/fixtures/golden-install-parity/codebuddy.json b/tests/fixtures/golden-install-parity/codebuddy.json index e7d2c8f6e..620888b75 100644 --- a/tests/fixtures/golden-install-parity/codebuddy.json +++ b/tests/fixtures/golden-install-parity/codebuddy.json @@ -67,7 +67,7 @@ "commands/gsd-mempalace-recall.md": "6efef238d8c62569", "commands/gsd-milestone-summary.md": "e3a36fbd695a1833", "commands/gsd-mvp-phase.md": "671791ba61c4654a", - "commands/gsd-new-milestone.md": "00b5d757991941c7", + "commands/gsd-new-milestone.md": "288fa7dfcec18094", "commands/gsd-new-project.md": "490195d5003d3d17", "commands/gsd-next.md": "dbec71acc9d5d2a2", "commands/gsd-ns-context.md": "cc8954f405ae7916", @@ -321,7 +321,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "924860e1f07defb0", "gsd-core/workflows/help/modes/default.md": "08a02976c0c5cc50", - "gsd-core/workflows/help/modes/full.md": "67e3dffe34ddc9bc", + "gsd-core/workflows/help/modes/full.md": "5eefc87999cde1a6", "gsd-core/workflows/help/modes/topic.md": "5c160093f3cbf35d", "gsd-core/workflows/import.md": "3d3fa603ceb8bc9f", "gsd-core/workflows/inbox.md": "437f981ef9ae7b26", @@ -334,7 +334,7 @@ "gsd-core/workflows/map-codebase.md": "83d750aade709983", "gsd-core/workflows/milestone-summary.md": "e0cdfddbd39a9043", "gsd-core/workflows/mvp-phase.md": "344dd300e0cc1e74", - "gsd-core/workflows/new-milestone.md": "bc020226377af22a", + "gsd-core/workflows/new-milestone.md": "83bf5593e94c3377", "gsd-core/workflows/new-project.md": "278e0b88dd1d2ea0", "gsd-core/workflows/new-workspace.md": "017688423110ed66", "gsd-core/workflows/next.md": "13fb800f2472d970", diff --git a/tests/fixtures/golden-install-parity/codex.json b/tests/fixtures/golden-install-parity/codex.json index c0894658f..bc37fd513 100644 --- a/tests/fixtures/golden-install-parity/codex.json +++ b/tests/fixtures/golden-install-parity/codex.json @@ -356,7 +356,7 @@ "gsd-core/workflows/help.md": "08e1349950c5602a", "gsd-core/workflows/help/modes/brief.md": "da44b130d1afe556", "gsd-core/workflows/help/modes/default.md": "4bb3082d28026eea", - "gsd-core/workflows/help/modes/full.md": "773717d0699320b8", + "gsd-core/workflows/help/modes/full.md": "9a512db0dce1a315", "gsd-core/workflows/help/modes/topic.md": "b7c7e4a8800bc3ea", "gsd-core/workflows/import.md": "cb3c8f9d09edb434", "gsd-core/workflows/inbox.md": "61b8b10e7a74b2e9", @@ -369,7 +369,7 @@ "gsd-core/workflows/map-codebase.md": "2edab7d924b296ff", "gsd-core/workflows/milestone-summary.md": "a53b30a84f904b50", "gsd-core/workflows/mvp-phase.md": "100dded5c7b4cb86", - "gsd-core/workflows/new-milestone.md": "f30f968a4f0912ed", + "gsd-core/workflows/new-milestone.md": "f8fac345bb136e04", "gsd-core/workflows/new-project.md": "a9dbf207833897cc", "gsd-core/workflows/new-workspace.md": "3630b78e83b1e491", "gsd-core/workflows/next.md": "7097a9607a981086", diff --git a/tests/fixtures/golden-install-parity/copilot.json b/tests/fixtures/golden-install-parity/copilot.json index 45ca627e7..84c5c3c0f 100644 --- a/tests/fixtures/golden-install-parity/copilot.json +++ b/tests/fixtures/golden-install-parity/copilot.json @@ -251,7 +251,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "be05e56b2c5ee2c0", - "gsd-core/workflows/help/modes/full.md": "bd1bca7960fadd61", + "gsd-core/workflows/help/modes/full.md": "fb46abb57e525863", "gsd-core/workflows/help/modes/topic.md": "0bf9ab39d7044d69", "gsd-core/workflows/import.md": "f4fa65e332b00f7e", "gsd-core/workflows/inbox.md": "a448220c548f27bc", @@ -264,7 +264,7 @@ "gsd-core/workflows/map-codebase.md": "b426a450e5d90cc3", "gsd-core/workflows/milestone-summary.md": "8a53906253984a62", "gsd-core/workflows/mvp-phase.md": "93304b26c92e4418", - "gsd-core/workflows/new-milestone.md": "9909214a1782b31f", + "gsd-core/workflows/new-milestone.md": "a7d8835c192e9b7d", "gsd-core/workflows/new-project.md": "5c34f0cb064568bd", "gsd-core/workflows/new-workspace.md": "403796bdf6318515", "gsd-core/workflows/next.md": "81c08abb34b2da88", @@ -359,7 +359,7 @@ "skills/gsd-mempalace-recall/SKILL.md": "89523db66f834b2f", "skills/gsd-milestone-summary/SKILL.md": "5ca1dabfe0cd85f0", "skills/gsd-mvp-phase/SKILL.md": "60ac3c3fa6a6a19b", - "skills/gsd-new-milestone/SKILL.md": "e5eafe44e271bb92", + "skills/gsd-new-milestone/SKILL.md": "65670b65fb89d928", "skills/gsd-new-project/SKILL.md": "4ce68352221126c6", "skills/gsd-next/SKILL.md": "16507fc39a3a0684", "skills/gsd-ns-context/SKILL.md": "d61edde87ae53105", diff --git a/tests/fixtures/golden-install-parity/cursor.json b/tests/fixtures/golden-install-parity/cursor.json index d0e015385..864137256 100644 --- a/tests/fixtures/golden-install-parity/cursor.json +++ b/tests/fixtures/golden-install-parity/cursor.json @@ -321,7 +321,7 @@ "gsd-core/workflows/help.md": "08e1349950c5602a", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "b88ff431fffe40ae", - "gsd-core/workflows/help/modes/full.md": "d30803f316abd6fa", + "gsd-core/workflows/help/modes/full.md": "9ae8b1e8908c7ead", "gsd-core/workflows/help/modes/topic.md": "cee80e0adfa3b06c", "gsd-core/workflows/import.md": "5d5fb8e51f6a243a", "gsd-core/workflows/inbox.md": "797c287852eb8957", @@ -334,7 +334,7 @@ "gsd-core/workflows/map-codebase.md": "3118100462eee628", "gsd-core/workflows/milestone-summary.md": "6c76386b500cc5d3", "gsd-core/workflows/mvp-phase.md": "50b189c5f4503369", - "gsd-core/workflows/new-milestone.md": "efba76f3dd38d87c", + "gsd-core/workflows/new-milestone.md": "68143de8032d8a41", "gsd-core/workflows/new-project.md": "a2b559fba00904eb", "gsd-core/workflows/new-workspace.md": "9e76e96253d54a57", "gsd-core/workflows/next.md": "174356dcfd7c6c32", diff --git a/tests/fixtures/golden-install-parity/hermes.json b/tests/fixtures/golden-install-parity/hermes.json index 6aa557c30..f84d86460 100644 --- a/tests/fixtures/golden-install-parity/hermes.json +++ b/tests/fixtures/golden-install-parity/hermes.json @@ -250,7 +250,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "8874dac94eb68ae6", - "gsd-core/workflows/help/modes/full.md": "cad4eed3907e8a58", + "gsd-core/workflows/help/modes/full.md": "a9351cf72f580a87", "gsd-core/workflows/help/modes/topic.md": "6e42db16f1568be9", "gsd-core/workflows/import.md": "c6a19584809ea635", "gsd-core/workflows/inbox.md": "91aac6360e1a8672", @@ -263,7 +263,7 @@ "gsd-core/workflows/map-codebase.md": "420caa00abbd73f5", "gsd-core/workflows/milestone-summary.md": "353fffb60749d892", "gsd-core/workflows/mvp-phase.md": "4d102523b5d81f95", - "gsd-core/workflows/new-milestone.md": "75944be19e6eda0a", + "gsd-core/workflows/new-milestone.md": "dec95592513880e1", "gsd-core/workflows/new-project.md": "291d623166b97754", "gsd-core/workflows/new-workspace.md": "5c78c58ad84e886b", "gsd-core/workflows/next.md": "8e1fa29751d96564", @@ -392,7 +392,7 @@ "skills/gsd/gsd-ns-project/skills/import/SKILL.md": "bf781c7a0d83c2cd", "skills/gsd/gsd-ns-project/skills/ingest-docs/SKILL.md": "a38273c6b6604830", "skills/gsd/gsd-ns-project/skills/milestone-summary/SKILL.md": "c4bda5737f1137f8", - "skills/gsd/gsd-ns-project/skills/new-milestone/SKILL.md": "47586c1fc8aa0645", + "skills/gsd/gsd-ns-project/skills/new-milestone/SKILL.md": "384b6754a950e6eb", "skills/gsd/gsd-ns-project/skills/new-project/SKILL.md": "8f22ba75717b3c88", "skills/gsd/gsd-ns-project/skills/onboard/SKILL.md": "1d8d06ac1f27dacd", "skills/gsd/gsd-ns-project/skills/profile-user/SKILL.md": "5a8e01bc7b873a20", diff --git a/tests/fixtures/golden-install-parity/kilo.json b/tests/fixtures/golden-install-parity/kilo.json index 27979fc0f..aa387af23 100644 --- a/tests/fixtures/golden-install-parity/kilo.json +++ b/tests/fixtures/golden-install-parity/kilo.json @@ -67,7 +67,7 @@ "command/gsd-mempalace-recall.md": "d358cdd89c935877", "command/gsd-milestone-summary.md": "4ab29b777f62f0bb", "command/gsd-mvp-phase.md": "273a1c26ca13274b", - "command/gsd-new-milestone.md": "a315b3205c5d3470", + "command/gsd-new-milestone.md": "dbf06cebb4c10039", "command/gsd-new-project.md": "f2763e2ccbb70ebe", "command/gsd-next.md": "dfdcfb2fd79957fa", "command/gsd-ns-context.md": "9a2b55b64f2e1e4b", @@ -321,7 +321,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "be05e56b2c5ee2c0", - "gsd-core/workflows/help/modes/full.md": "c41d1012b4577fe4", + "gsd-core/workflows/help/modes/full.md": "08101a965254f10e", "gsd-core/workflows/help/modes/topic.md": "0bf9ab39d7044d69", "gsd-core/workflows/import.md": "f8bbe6f2c0e08a78", "gsd-core/workflows/inbox.md": "a073396097c89c01", @@ -334,7 +334,7 @@ "gsd-core/workflows/map-codebase.md": "c3d6418d64ba2994", "gsd-core/workflows/milestone-summary.md": "25775eeb4e2747be", "gsd-core/workflows/mvp-phase.md": "a97708464f103300", - "gsd-core/workflows/new-milestone.md": "9da5203fabe324c4", + "gsd-core/workflows/new-milestone.md": "f3f76acd32311959", "gsd-core/workflows/new-project.md": "6d8cd0b4fe813374", "gsd-core/workflows/new-workspace.md": "f58d6a8c0c7796a1", "gsd-core/workflows/next.md": "888297a0b21888ea", diff --git a/tests/fixtures/golden-install-parity/kimi.json b/tests/fixtures/golden-install-parity/kimi.json index 540055861..2379f3f09 100644 --- a/tests/fixtures/golden-install-parity/kimi.json +++ b/tests/fixtures/golden-install-parity/kimi.json @@ -314,7 +314,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "924860e1f07defb0", "gsd-core/workflows/help/modes/default.md": "08a02976c0c5cc50", - "gsd-core/workflows/help/modes/full.md": "9706b4bee6829f82", + "gsd-core/workflows/help/modes/full.md": "91e115f058b6d3cf", "gsd-core/workflows/help/modes/topic.md": "5c160093f3cbf35d", "gsd-core/workflows/import.md": "3d3fa603ceb8bc9f", "gsd-core/workflows/inbox.md": "437f981ef9ae7b26", @@ -327,7 +327,7 @@ "gsd-core/workflows/map-codebase.md": "83d750aade709983", "gsd-core/workflows/milestone-summary.md": "e0cdfddbd39a9043", "gsd-core/workflows/mvp-phase.md": "344dd300e0cc1e74", - "gsd-core/workflows/new-milestone.md": "bc020226377af22a", + "gsd-core/workflows/new-milestone.md": "83bf5593e94c3377", "gsd-core/workflows/new-project.md": "1ec88027a8e5a2fe", "gsd-core/workflows/new-workspace.md": "017688423110ed66", "gsd-core/workflows/next.md": "13fb800f2472d970", diff --git a/tests/fixtures/golden-install-parity/opencode.json b/tests/fixtures/golden-install-parity/opencode.json index cd288e16c..9a9a6be7a 100644 --- a/tests/fixtures/golden-install-parity/opencode.json +++ b/tests/fixtures/golden-install-parity/opencode.json @@ -67,7 +67,7 @@ "command/gsd-mempalace-recall.md": "d358cdd89c935877", "command/gsd-milestone-summary.md": "86a827271bc647c5", "command/gsd-mvp-phase.md": "d45fd76cb9cff3ef", - "command/gsd-new-milestone.md": "64c9dc73425a66a2", + "command/gsd-new-milestone.md": "613902b46d2ba6ef", "command/gsd-new-project.md": "0086f0de26002219", "command/gsd-next.md": "b0ef81652d72d894", "command/gsd-ns-context.md": "9a2b55b64f2e1e4b", @@ -321,7 +321,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "be05e56b2c5ee2c0", - "gsd-core/workflows/help/modes/full.md": "de70e34ebd257ce9", + "gsd-core/workflows/help/modes/full.md": "7e68200aeb086535", "gsd-core/workflows/help/modes/topic.md": "0bf9ab39d7044d69", "gsd-core/workflows/import.md": "67fbf389ab8e57b4", "gsd-core/workflows/inbox.md": "a073396097c89c01", @@ -334,7 +334,7 @@ "gsd-core/workflows/map-codebase.md": "6aa5a52741af0b3e", "gsd-core/workflows/milestone-summary.md": "321ea8a6a206f38f", "gsd-core/workflows/mvp-phase.md": "b4d688baa4c60dfc", - "gsd-core/workflows/new-milestone.md": "66d00a85528a2170", + "gsd-core/workflows/new-milestone.md": "72010b09ae067366", "gsd-core/workflows/new-project.md": "a3850644ef65bea6", "gsd-core/workflows/new-workspace.md": "3d82392388ee24c7", "gsd-core/workflows/next.md": "6719a37b8ce2ac4f", diff --git a/tests/fixtures/golden-install-parity/pi.json b/tests/fixtures/golden-install-parity/pi.json index f2e8be531..eb0a6ae2a 100644 --- a/tests/fixtures/golden-install-parity/pi.json +++ b/tests/fixtures/golden-install-parity/pi.json @@ -217,7 +217,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "924860e1f07defb0", "gsd-core/workflows/help/modes/default.md": "08a02976c0c5cc50", - "gsd-core/workflows/help/modes/full.md": "584e50ea86a86b6f", + "gsd-core/workflows/help/modes/full.md": "99d5387021de656f", "gsd-core/workflows/help/modes/topic.md": "5c160093f3cbf35d", "gsd-core/workflows/import.md": "3d3fa603ceb8bc9f", "gsd-core/workflows/inbox.md": "437f981ef9ae7b26", @@ -230,7 +230,7 @@ "gsd-core/workflows/map-codebase.md": "83d750aade709983", "gsd-core/workflows/milestone-summary.md": "e0cdfddbd39a9043", "gsd-core/workflows/mvp-phase.md": "344dd300e0cc1e74", - "gsd-core/workflows/new-milestone.md": "bc020226377af22a", + "gsd-core/workflows/new-milestone.md": "83bf5593e94c3377", "gsd-core/workflows/new-project.md": "4c32c69910d55ec0", "gsd-core/workflows/new-workspace.md": "017688423110ed66", "gsd-core/workflows/next.md": "13fb800f2472d970", diff --git a/tests/fixtures/golden-install-parity/qwen.json b/tests/fixtures/golden-install-parity/qwen.json index 72685b34a..f67996dca 100644 --- a/tests/fixtures/golden-install-parity/qwen.json +++ b/tests/fixtures/golden-install-parity/qwen.json @@ -250,7 +250,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "7ca77077085452f5", - "gsd-core/workflows/help/modes/full.md": "9d620deed3bf9011", + "gsd-core/workflows/help/modes/full.md": "6022991e0f56fd1d", "gsd-core/workflows/help/modes/topic.md": "6e42db16f1568be9", "gsd-core/workflows/import.md": "e56c18a984396066", "gsd-core/workflows/inbox.md": "91aac6360e1a8672", @@ -263,7 +263,7 @@ "gsd-core/workflows/map-codebase.md": "2328da6a2f180f07", "gsd-core/workflows/milestone-summary.md": "97ddfc40c6bce906", "gsd-core/workflows/mvp-phase.md": "5ca64695120d250c", - "gsd-core/workflows/new-milestone.md": "a65c567f1f38cc73", + "gsd-core/workflows/new-milestone.md": "6bee4870688f0170", "gsd-core/workflows/new-project.md": "493ae04534a80f97", "gsd-core/workflows/new-workspace.md": "0d59bb987430003e", "gsd-core/workflows/next.md": "c13515a80c20d37d", @@ -391,7 +391,7 @@ "skills/gsd-ns-project/skills/import/SKILL.md": "88f83921a85e536b", "skills/gsd-ns-project/skills/ingest-docs/SKILL.md": "eb85e654917a503a", "skills/gsd-ns-project/skills/milestone-summary/SKILL.md": "33d51a325d314f3a", - "skills/gsd-ns-project/skills/new-milestone/SKILL.md": "ac99ffb8a966fe9d", + "skills/gsd-ns-project/skills/new-milestone/SKILL.md": "24c97f2814d30501", "skills/gsd-ns-project/skills/new-project/SKILL.md": "9d5c27ebfa2c4746", "skills/gsd-ns-project/skills/onboard/SKILL.md": "46460b479b7524bf", "skills/gsd-ns-project/skills/profile-user/SKILL.md": "78ca46045e85223f", diff --git a/tests/fixtures/golden-install-parity/trae.json b/tests/fixtures/golden-install-parity/trae.json index 00d58b1fe..fc111dc3f 100644 --- a/tests/fixtures/golden-install-parity/trae.json +++ b/tests/fixtures/golden-install-parity/trae.json @@ -250,7 +250,7 @@ "gsd-core/workflows/help.md": "08e1349950c5602a", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "ed7368e0d1b8644a", - "gsd-core/workflows/help/modes/full.md": "249beacac5798123", + "gsd-core/workflows/help/modes/full.md": "01a34fdde96bf138", "gsd-core/workflows/help/modes/topic.md": "8a5344e56fa64ab9", "gsd-core/workflows/import.md": "31af58468db15daf", "gsd-core/workflows/inbox.md": "e9ea37b2d46dc5b4", @@ -263,7 +263,7 @@ "gsd-core/workflows/map-codebase.md": "713561638fb1b88e", "gsd-core/workflows/milestone-summary.md": "a0063cfbe08f2250", "gsd-core/workflows/mvp-phase.md": "8ed9f4552350ede8", - "gsd-core/workflows/new-milestone.md": "fd0f56ebd91dc2db", + "gsd-core/workflows/new-milestone.md": "9a3a99448397cee4", "gsd-core/workflows/new-project.md": "32ce66cbeaa26cc5", "gsd-core/workflows/new-workspace.md": "6b6fbad18d30f783", "gsd-core/workflows/next.md": "2163195861877de5", diff --git a/tests/fixtures/golden-install-parity/windsurf.json b/tests/fixtures/golden-install-parity/windsurf.json index 29ae4744a..f4230a938 100644 --- a/tests/fixtures/golden-install-parity/windsurf.json +++ b/tests/fixtures/golden-install-parity/windsurf.json @@ -250,7 +250,7 @@ "gsd-core/workflows/help.md": "08e1349950c5602a", "gsd-core/workflows/help/modes/brief.md": "fa2675516b40e2e3", "gsd-core/workflows/help/modes/default.md": "6a253f1756f74e95", - "gsd-core/workflows/help/modes/full.md": "8f1bf0e563a02255", + "gsd-core/workflows/help/modes/full.md": "670392a80cbcbd7d", "gsd-core/workflows/help/modes/topic.md": "cee80e0adfa3b06c", "gsd-core/workflows/import.md": "029cacf530aca86b", "gsd-core/workflows/inbox.md": "797c287852eb8957", @@ -263,7 +263,7 @@ "gsd-core/workflows/map-codebase.md": "0a2a653ed1603784", "gsd-core/workflows/milestone-summary.md": "1fdbae19ad06a0a3", "gsd-core/workflows/mvp-phase.md": "e3c4cade4fe69408", - "gsd-core/workflows/new-milestone.md": "7043ae90a2bc0401", + "gsd-core/workflows/new-milestone.md": "f2b8fdcd1a19d378", "gsd-core/workflows/new-project.md": "0ff192c54373ab8a", "gsd-core/workflows/new-workspace.md": "8c2b60fbbf241e03", "gsd-core/workflows/next.md": "d48cea140a5153cb", diff --git a/tests/fixtures/golden-install-parity/zcode.json b/tests/fixtures/golden-install-parity/zcode.json index 977f5c445..1e021ac56 100644 --- a/tests/fixtures/golden-install-parity/zcode.json +++ b/tests/fixtures/golden-install-parity/zcode.json @@ -67,7 +67,7 @@ "commands/gsd-mempalace-recall.md": "5ac67fa4b7f2b03b", "commands/gsd-milestone-summary.md": "f8fa9c357c08b74b", "commands/gsd-mvp-phase.md": "cba67bcb55250e5f", - "commands/gsd-new-milestone.md": "53cb82c66bbc808c", + "commands/gsd-new-milestone.md": "8d52385b21cadd20", "commands/gsd-new-project.md": "952d7601b800ad8d", "commands/gsd-next.md": "7d3a2cb0bd12043a", "commands/gsd-ns-context.md": "011c44e7aa46e64a", @@ -321,7 +321,7 @@ "gsd-core/workflows/help.md": "5d040504b9ab35e3", "gsd-core/workflows/help/modes/brief.md": "924860e1f07defb0", "gsd-core/workflows/help/modes/default.md": "08a02976c0c5cc50", - "gsd-core/workflows/help/modes/full.md": "1810842c75f73fb2", + "gsd-core/workflows/help/modes/full.md": "dda8f6d101b0870d", "gsd-core/workflows/help/modes/topic.md": "5c160093f3cbf35d", "gsd-core/workflows/import.md": "3d3fa603ceb8bc9f", "gsd-core/workflows/inbox.md": "437f981ef9ae7b26", @@ -334,7 +334,7 @@ "gsd-core/workflows/map-codebase.md": "83d750aade709983", "gsd-core/workflows/milestone-summary.md": "e0cdfddbd39a9043", "gsd-core/workflows/mvp-phase.md": "344dd300e0cc1e74", - "gsd-core/workflows/new-milestone.md": "bc020226377af22a", + "gsd-core/workflows/new-milestone.md": "83bf5593e94c3377", "gsd-core/workflows/new-project.md": "fdb53bee6b4178ad", "gsd-core/workflows/new-workspace.md": "017688423110ed66", "gsd-core/workflows/next.md": "13fb800f2472d970", @@ -434,7 +434,7 @@ "skills/gsd-ns-project/skills/import/SKILL.md": "0a734021e0b86481", "skills/gsd-ns-project/skills/ingest-docs/SKILL.md": "0fadb4f9c69aa61d", "skills/gsd-ns-project/skills/milestone-summary/SKILL.md": "c77f6c8ce7c57db9", - "skills/gsd-ns-project/skills/new-milestone/SKILL.md": "3aad804c48d77474", + "skills/gsd-ns-project/skills/new-milestone/SKILL.md": "c7c20da62ac3f6e1", "skills/gsd-ns-project/skills/new-project/SKILL.md": "e3cfd1fdbc2c3406", "skills/gsd-ns-project/skills/onboard/SKILL.md": "84519552f7396347", "skills/gsd-ns-project/skills/profile-user/SKILL.md": "f57ad5e5c5899100", diff --git a/tests/new-milestone-clear-phases.test.cjs b/tests/new-milestone-clear-phases.test.cjs index 7419f40ea..156474801 100644 --- a/tests/new-milestone-clear-phases.test.cjs +++ b/tests/new-milestone-clear-phases.test.cjs @@ -10,7 +10,7 @@ const { test, describe, beforeEach, afterEach } = require('node:test'); const assert = require('node:assert/strict'); -const { execSync } = require('child_process'); +const { execSync, execFileSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const { runGsdTools, createTempProject, createTempGitProject, cleanup } = require('./helpers.cjs'); @@ -563,3 +563,162 @@ test('execute-phase.md: awk extracts resolves_phase from YAML frontmatter', () = }); }); } + +// ──────────────────────────────────────────────────────────────────────── +// #2308 / #2334 follow-up: new-milestone.md must not clobber the shared +// PROJECT.md when a workstream is active, and must propagate ${GSD_WS} to +// downstream routing. Step 1 and Step 6's bash fences are extracted and +// EXECUTED (not grepped) so these tests fail on an inert guard — e.g. the +// step-6 conditional merely being PRESENT (`if [ -n "$GSD_WS" ]`) is not +// enough if GSD_WS was never re-derived and is always empty at runtime. +// ──────────────────────────────────────────────────────────────────────── +describe('new-milestone.md: workstream-aware PROJECT.md guard (#2308)', () => { + const workflowPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'new-milestone.md'); + const content = fs.readFileSync(workflowPath, 'utf8'); + + // Locate the first ```bash fence strictly between two headings. + function extractFenceBetween(markdown, startHeading, endHeading) { + const startIdx = markdown.indexOf(startHeading); + const endIdx = markdown.indexOf(endHeading); + assert.ok(startIdx !== -1, `heading not found: ${startHeading}`); + assert.ok(endIdx !== -1, `heading not found: ${endHeading}`); + assert.ok(startIdx < endIdx, `${startHeading} must precede ${endHeading}`); + const section = markdown.slice(startIdx, endIdx); + const match = section.match(/```bash\r?\n([\s\S]*?)```/); + assert.ok(match, `no bash fence found between "${startHeading}" and "${endHeading}"`); + return match[1]; + } + + // Step 6 has multiple ```bash fences; locate the one containing `marker`. + function extractFenceContaining(markdown, startHeading, endHeading, marker) { + const startIdx = markdown.indexOf(startHeading); + const endIdx = markdown.indexOf(endHeading); + assert.ok(startIdx !== -1 && endIdx !== -1 && startIdx < endIdx, 'headings not found in order'); + const section = markdown.slice(startIdx, endIdx); + const fenceRe = /```bash\r?\n([\s\S]*?)```/g; + let m; + while ((m = fenceRe.exec(section)) !== null) { + if (m[1].includes(marker)) return m[1]; + } + assert.fail(`no bash fence containing "${marker}" found between "${startHeading}" and "${endHeading}"`); + return null; + } + + describe('step 1: --ws parsing is real, executable shell (not prose)', () => { + const step1Fence = extractFenceBetween(content, '## 1. Load Context', '## 2. Gather Milestone Goals'); + + function runStep1(argumentsValue) { + const script = `ARGUMENTS=${JSON.stringify(argumentsValue)}\n${step1Fence}\n` + + 'printf \'GSD_WS=[%s]\\nMILESTONE_ARG=[%s]\\n\' "$GSD_WS" "$MILESTONE_ARG"'; + const out = execFileSync('bash', ['-c', script], { encoding: 'utf8' }); + return { + gsdWs: /GSD_WS=\[(.*)\]/.exec(out)[1], + milestoneArg: /MILESTONE_ARG=\[(.*)\]/.exec(out)[1], + }; + } + + test('parses --ws into GSD_WS and strips it from the milestone name (finding 6)', () => { + const { gsdWs, milestoneArg } = runStep1('--ws search v2.0 Search'); + assert.strictEqual(gsdWs, '--ws search'); + assert.strictEqual(milestoneArg, 'v2.0 Search'); + }); + + test('leaves GSD_WS empty when --ws is absent, milestone name unaffected', () => { + const { gsdWs, milestoneArg } = runStep1('v2.0 Search'); + assert.strictEqual(gsdWs, ''); + assert.strictEqual(milestoneArg, 'v2.0 Search'); + }); + }); + + describe('step 6: commit stages PROJECT.md in both modes, with no cross-step guard', () => { + const step6CommitFence = extractFenceContaining( + content, + '## 6. Cleanup and Commit', + '## 7. Load Context and Resolve Models', + 'docs: start milestone v[X.Y] [Name]' + ); + + function runStep6Commit(argumentsValue) { + const gsdRunStub = 'gsd_run() { printf "%s\\n" "gsd_run_call:$*"; }\n'; + const script = `ARGUMENTS=${JSON.stringify(argumentsValue)}\n${gsdRunStub}${step6CommitFence}`; + return execFileSync('bash', ['-c', script], { encoding: 'utf8' }); + } + + // Step 4 Part A's guard — not this commit — is what protects the shared + // heading. Part B's Evolution backfill DOES write PROJECT.md in workstream + // mode, so a ws-mode branch that dropped PROJECT.md from --files would + // strand that edit uncommitted. + for (const [mode, args] of [['ws', '--ws search v2.0 Search'], ['flat', 'v2.0 Search']]) { + test(`${mode} mode: --files stages PROJECT.md so Part B's Evolution backfill is committed`, () => { + const out = runStep6Commit(args); + assert.ok( + out.includes('--files .planning/PROJECT.md .planning/STATE.md'), + `expected PROJECT.md + STATE.md --files in ${mode} mode, got: ${out}` + ); + }); + } + + test('does not guard the commit on GSD_WS — a cross-step variable is always empty here', () => { + // Regression guard for the inert-guard trap: GSD_WS is assigned in Step + // 1's shell, and each step's bash block runs in its own shell (the same + // reason Step 5 round-trips OUTGOING_MILESTONE through a file). A + // `[ -n "$GSD_WS" ]` branch here reads an unset variable, always takes + // the flat branch, and only appears to work. + assert.ok( + !/\[\s*-n\s*"\$GSD_WS"\s*\]/.test(step6CommitFence), + `step 6 must not branch on a cross-step GSD_WS; got fence:\n${step6CommitFence}` + ); + }); + }); + + test('routing interpolations still propagate ${GSD_WS} at the documented lines', () => { + assert.ok( + content.includes('/gsd:new-milestone --reset-phase-numbers ${GSD_WS}'), + 'reset-phase-numbers rerun hint should propagate ${GSD_WS}' + ); + assert.ok( + content.includes('/gsd:discuss-phase [N] ${GSD_WS}'), + 'discuss-phase routing hint should propagate ${GSD_WS}' + ); + assert.ok( + content.includes('/gsd:plan-phase [N] ${GSD_WS}'), + 'plan-phase routing hint should propagate ${GSD_WS}' + ); + }); + + test('success criteria reflects PROJECT.md update is skipped in workstream mode', () => { + assert.match( + content, + /PROJECT\.md updated with Current Milestone section.*skipped.*workstream/i, + 'success criteria should note the PROJECT.md step is skipped in workstream mode' + ); + }); + + test('step 4 scopes the workstream skip to the milestone-state write only; Evolution repair always runs (finding 2)', () => { + const step4Idx = content.indexOf('## 4. Update PROJECT.md'); + const step5Idx = content.indexOf('## 5. Update STATE.md'); + assert.ok(step4Idx !== -1 && step5Idx !== -1 && step4Idx < step5Idx, 'steps 4 and 5 should be locatable'); + const step4Body = content.slice(step4Idx, step5Idx); + + const partAIdx = step4Body.indexOf('Part A'); + const partBIdx = step4Body.indexOf('Part B'); + assert.ok(partAIdx !== -1 && partBIdx !== -1 && partAIdx < partBIdx, 'step 4 should have distinct Part A / Part B sections'); + + const partABody = step4Body.slice(partAIdx, partBIdx); + const partBBody = step4Body.slice(partBIdx); + + assert.match(partABody, /skip/i, 'Part A should describe the workstream skip'); + assert.ok(partABody.includes('GSD_WS'), 'Part A guard should be keyed on GSD_WS'); + assert.match( + step4Body, + /shared/i, + 'step 4 should justify the guard by pointing at PROJECT.md being the shared file' + ); + + // The Evolution structural repair must be reachable OUTSIDE Part A's skip, + // and Part B's own text must state it is unconditional. + assert.ok(!partABody.includes('## Evolution'), 'Evolution repair must NOT be nested inside the guarded Part A'); + assert.ok(partBBody.includes('## Evolution'), 'Part B must contain the Evolution section template'); + assert.match(partBBody, /always runs/i, 'Part B must state it always runs regardless of GSD_WS'); + }); +}); diff --git a/tests/workflow-size-baseline.json b/tests/workflow-size-baseline.json index 94b1850d7..059b7f92f 100644 --- a/tests/workflow-size-baseline.json +++ b/tests/workflow-size-baseline.json @@ -44,7 +44,7 @@ "map-codebase.md": 20833, "milestone-summary.md": 11842, "mvp-phase.md": 13626, - "new-milestone.md": 35045, + "new-milestone.md": 37581, "new-project.md": 66182, "new-workspace.md": 11298, "next.md": 20138,