fix(#2308): make new-milestone workstream-aware; stop clobbering shared PROJECT.md (#2338)

* 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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLufH5sDuqA1AiEGu45cuA

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-17 06:48:13 -04:00
committed by GitHub
parent b2961c3f69
commit ada79bee97
26 changed files with 240 additions and 56 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2338
---
**`/gsd:new-milestone --ws <name>` 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.

View File

@@ -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 <name>]"
allowed-tools:
- Read
- Write

View File

@@ -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 <name>` | 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
```
---

View File

@@ -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 <name>` 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 <version>`**
Archive completed milestone and prepare for next version.

View File

@@ -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 <name>` flag → active workstream scope, parsed into `GSD_WS`
- remaining text, with `--ws <name>` 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/<name>/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
</process>
<success_criteria>
- [ ] 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

View File

@@ -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 <name>]"
allowed-tools:
- Read
- Write

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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 <name> 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');
});
});

View File

@@ -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,