From 182f60b4c170785d96f1deb87fea8b108e9b985f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:51:02 +0000 Subject: [PATCH] chore: promote CHANGELOG for v1.11.0 --- .../1884-planning-lock-mkdir-failure.md | 6 - .../2115-plan-phase-ai-keyword-precision.md | 6 - .changeset/2229-explore-claim-disposition.md | 5 - .../2570-smartentry-stale-activity-suffix.md | 5 - .changeset/2573-state-head-freshness.md | 5 - .changeset/2783-ship-note-wedged-pr.md | 7 - .../3102-render-edge-coverage-report.md | 5 - .../3174-quick-verification-status-query.md | 5 - .changeset/3206-verifier-explicit-evidence.md | 5 - ...econdition-blocking-human-retry-ceiling.md | 6 - .changeset/3481-state-phase-placeholder.md | 5 - .../3497-frontmatter-escape-amplification.md | 5 - .changeset/3503-diff-base-scope-anchor.md | 6 - .changeset/3518-uat-path-phase-pinned.md | 6 - .../3527-mempalace-default-true-gates.md | 6 - .changeset/agile-cranes-frolic.md | 5 - .changeset/agile-elks-sing.md | 5 - .changeset/agile-rams-run.md | 5 - .changeset/agile-tigers-cheer.md | 5 - .changeset/amber-cobalt-spark.md | 5 - .changeset/bold-badgers-climb.md | 5 - .changeset/bold-jaguars-run.md | 5 - .changeset/bold-lynx-dart.md | 5 - .changeset/bold-orcas-sing.md | 5 - .changeset/bold-otters-scope.md | 6 - .changeset/bold-wasps-hop.md | 5 - .changeset/brave-geese-bark.md | 5 - .changeset/calm-bears-howl.md | 9 - .changeset/calm-bears-sing.md | 5 - .changeset/calm-fern-meadow.md | 5 - .changeset/calm-ibex-travel.md | 5 - .changeset/calm-lemurs-sing.md | 5 - .changeset/calm-voles-howl.md | 5 - .changeset/calm-wasps-dart.md | 5 - .changeset/clever-bears-wander.md | 5 - .changeset/clever-cranes-zip.md | 5 - .changeset/clever-eagles-wake.md | 5 - .changeset/clever-tigers-dart.md | 5 - .changeset/clever-voles-swim.md | 5 - .changeset/curious-goats-zip.md | 5 - .changeset/curious-koalas-travel.md | 5 - .changeset/curious-mice-munch.md | 5 - .changeset/curious-quails-tumble.md | 5 - .changeset/curious-tunas-fly.md | 5 - .changeset/curious-tunas-greet.md | 5 - .changeset/curious-zebras-squeak.md | 5 - .changeset/daring-koalas-zip.md | 5 - .changeset/daring-seals-wander.md | 5 - .changeset/eager-cranes-gather.md | 5 - .changeset/eager-lynx-romp.md | 5 - .changeset/eager-pandas-glide.md | 5 - .changeset/eager-rams-click.md | 5 - .changeset/eager-sloths-purr.md | 5 - .changeset/eager-wasps-travel.md | 5 - .changeset/fierce-eagles-roam.md | 5 - .changeset/fierce-hawks-climb.md | 5 - .changeset/fierce-lemurs-forage.md | 5 - .changeset/fierce-mice-munch.md | 5 - .changeset/fierce-quails-cheer.md | 5 - .changeset/gallant-foxes-squeak.md | 5 - .changeset/gallant-jaguars-forage.md | 5 - .changeset/gallant-otters-fly.md | 5 - .changeset/gallant-voles-wake.md | 5 - .changeset/gentle-badgers-forage.md | 5 - .changeset/gentle-foxes-glide.md | 5 - .changeset/gentle-moles-sprint.md | 5 - .changeset/gentle-wasps-wave.md | 5 - .changeset/gentle-wolves-hum.md | 5 - .changeset/graceful-dogs-tumble.md | 5 - .changeset/graceful-jaguars-frolic.md | 5 - .changeset/graceful-lynx-hop.md | 5 - .changeset/graceful-moles-sing.md | 5 - .changeset/graceful-seals-dance.md | 5 - .changeset/graceful-seals-march.md | 5 - .changeset/graceful-wolves-wake.md | 5 - .changeset/happy-bears-hop.md | 5 - .changeset/happy-birds-roar.md | 5 - .changeset/happy-ibex-romp.md | 5 - .changeset/happy-jaguars-roar.md | 5 - .changeset/happy-quails-parade.md | 5 - .changeset/happy-ravens-run.md | 5 - .changeset/happy-voles-chatter.md | 5 - .changeset/humble-cats-sprint.md | 5 - .changeset/humble-koalas-snooze.md | 5 - .changeset/humble-lemurs-roar.md | 5 - .changeset/jolly-geese-wake.md | 6 - .changeset/jolly-jaguars-caper.md | 5 - .changeset/jolly-seals-purr.md | 5 - .changeset/jolly-voles-rally.md | 5 - .changeset/kind-deer-caper.md | 5 - .changeset/kind-jaguars-romp.md | 5 - .changeset/lazy-otter-purchase.md | 5 - .changeset/lively-bears-click.md | 5 - .changeset/lively-birds-frolic.md | 5 - .changeset/lively-orcas-climb.md | 5 - .changeset/loud-jade-canyon.md | 5 - .changeset/lucky-bears-forage.md | 5 - .changeset/lucky-ravens-leap.md | 5 - .changeset/merry-badgers-tumble.md | 5 - .changeset/merry-cats-jump.md | 5 - .changeset/merry-voles-swim.md | 5 - .changeset/merry-wolves-climb.md | 5 - .changeset/nimble-agents-route.md | 5 - .changeset/nimble-jaguars-tumble.md | 5 - .changeset/nimble-orcas-roam.md | 5 - .changeset/nimble-orcas-tumble.md | 5 - .changeset/nimble-ravens-hop.md | 5 - .changeset/nimble-wasps-jump.md | 5 - .changeset/noble-wasps-munch.md | 5 - .changeset/patient-dogs-sprint.md | 5 - .changeset/patient-foxes-click.md | 5 - .changeset/patient-lynx-march.md | 5 - .changeset/patient-yaks-click.md | 5 - .changeset/plucky-tigers-roar.md | 5 - .changeset/plucky-wolves-leap.md | 5 - .changeset/proud-birds-travel.md | 5 - .changeset/proud-wasps-forage.md | 5 - .changeset/quick-finches-snooze.md | 5 - .changeset/quick-lynx-wander.md | 5 - .changeset/quick-moles-caper.md | 5 - .changeset/quick-moles-purr.md | 5 - .changeset/quick-pumas-climb.md | 5 - .changeset/quick-quails-march.md | 5 - .changeset/quick-rams-greet.md | 5 - .changeset/quiet-marble-field.md | 5 - .changeset/quiet-moons-derive.md | 7 - .changeset/rapid-foxes-forage.md | 5 - .changeset/rapid-foxes-glide.md | 5 - .changeset/rapid-orcas-tumble.md | 5 - .changeset/rapid-tunas-dance.md | 5 - .changeset/reviewer-lane-env-disclosure.md | 5 - .changeset/serene-birds-sing.md | 5 - .changeset/serene-eagles-roar.md | 5 - .changeset/serene-elks-sing.md | 5 - .changeset/serene-finches-bark.md | 5 - .changeset/serene-ibex-hum.md | 5 - .changeset/serene-tigers-gather.md | 5 - .changeset/sharp-birds-wave.md | 5 - .changeset/sharp-ibex-tumble.md | 5 - .changeset/sharp-moles-howl.md | 5 - .changeset/sharp-wolves-wander.md | 5 - .changeset/silly-jaguars-caper.md | 5 - .changeset/silly-ravens-hum.md | 5 - .changeset/soft-jade-quartz.md | 5 - .changeset/steady-jaguars-dance.md | 5 - .changeset/steady-pumas-click.md | 5 - .changeset/steady-wasps-jump.md | 5 - .changeset/sturdy-birds-chatter.md | 5 - .changeset/sturdy-dogs-caper.md | 5 - .changeset/sturdy-finches-caper.md | 5 - .changeset/sturdy-foxes-gather.md | 5 - .changeset/sturdy-hawks-munch.md | 5 - .changeset/sturdy-koalas-zip.md | 5 - .changeset/sturdy-mice-sprint.md | 5 - .changeset/sturdy-tigers-click.md | 5 - .changeset/sturdy-voles-run.md | 5 - .changeset/sunny-badgers-howl.md | 5 - .changeset/sunny-deer-hum.md | 5 - .changeset/sunny-geese-hop.md | 5 - .changeset/sunny-ravens-parade.md | 5 - .changeset/sunny-wolves-gather.md | 5 - .changeset/tame-river-song.md | 5 - .changeset/tidy-bears-chatter.md | 5 - .changeset/tidy-bears-wave.md | 5 - .changeset/tidy-goats-jump.md | 5 - .changeset/tidy-lynx-sing.md | 5 - .changeset/tidy-ravens-sing.md | 5 - .changeset/vivid-jaguars-purr.md | 5 - .changeset/vivid-moles-chatter.md | 6 - .changeset/vivid-pumas-jump.md | 5 - .changeset/wise-jaguars-cheer.md | 5 - .changeset/wise-seals-zip.md | 7 - .changeset/witty-herons-march.md | 5 - .changeset/witty-lynx-greet.md | 5 - .changeset/witty-wasps-bark.md | 5 - .changeset/zesty-ibex-hop.md | 5 - .changeset/zesty-moles-tumble.md | 5 - .changeset/zesty-pumas-hum.md | 5 - .changeset/zesty-rams-march.md | 5 - .changeset/zesty-tigers-forage.md | 5 - CHANGELOG.md | 207 ++++++++++++++++++ 181 files changed, 207 insertions(+), 919 deletions(-) delete mode 100644 .changeset/1884-planning-lock-mkdir-failure.md delete mode 100644 .changeset/2115-plan-phase-ai-keyword-precision.md delete mode 100644 .changeset/2229-explore-claim-disposition.md delete mode 100644 .changeset/2570-smartentry-stale-activity-suffix.md delete mode 100644 .changeset/2573-state-head-freshness.md delete mode 100644 .changeset/2783-ship-note-wedged-pr.md delete mode 100644 .changeset/3102-render-edge-coverage-report.md delete mode 100644 .changeset/3174-quick-verification-status-query.md delete mode 100644 .changeset/3206-verifier-explicit-evidence.md delete mode 100644 .changeset/3210-precondition-blocking-human-retry-ceiling.md delete mode 100644 .changeset/3481-state-phase-placeholder.md delete mode 100644 .changeset/3497-frontmatter-escape-amplification.md delete mode 100644 .changeset/3503-diff-base-scope-anchor.md delete mode 100644 .changeset/3518-uat-path-phase-pinned.md delete mode 100644 .changeset/3527-mempalace-default-true-gates.md delete mode 100644 .changeset/agile-cranes-frolic.md delete mode 100644 .changeset/agile-elks-sing.md delete mode 100644 .changeset/agile-rams-run.md delete mode 100644 .changeset/agile-tigers-cheer.md delete mode 100644 .changeset/amber-cobalt-spark.md delete mode 100644 .changeset/bold-badgers-climb.md delete mode 100644 .changeset/bold-jaguars-run.md delete mode 100644 .changeset/bold-lynx-dart.md delete mode 100644 .changeset/bold-orcas-sing.md delete mode 100644 .changeset/bold-otters-scope.md delete mode 100644 .changeset/bold-wasps-hop.md delete mode 100644 .changeset/brave-geese-bark.md delete mode 100644 .changeset/calm-bears-howl.md delete mode 100644 .changeset/calm-bears-sing.md delete mode 100644 .changeset/calm-fern-meadow.md delete mode 100644 .changeset/calm-ibex-travel.md delete mode 100644 .changeset/calm-lemurs-sing.md delete mode 100644 .changeset/calm-voles-howl.md delete mode 100644 .changeset/calm-wasps-dart.md delete mode 100644 .changeset/clever-bears-wander.md delete mode 100644 .changeset/clever-cranes-zip.md delete mode 100644 .changeset/clever-eagles-wake.md delete mode 100644 .changeset/clever-tigers-dart.md delete mode 100644 .changeset/clever-voles-swim.md delete mode 100644 .changeset/curious-goats-zip.md delete mode 100644 .changeset/curious-koalas-travel.md delete mode 100644 .changeset/curious-mice-munch.md delete mode 100644 .changeset/curious-quails-tumble.md delete mode 100644 .changeset/curious-tunas-fly.md delete mode 100644 .changeset/curious-tunas-greet.md delete mode 100644 .changeset/curious-zebras-squeak.md delete mode 100644 .changeset/daring-koalas-zip.md delete mode 100644 .changeset/daring-seals-wander.md delete mode 100644 .changeset/eager-cranes-gather.md delete mode 100644 .changeset/eager-lynx-romp.md delete mode 100644 .changeset/eager-pandas-glide.md delete mode 100644 .changeset/eager-rams-click.md delete mode 100644 .changeset/eager-sloths-purr.md delete mode 100644 .changeset/eager-wasps-travel.md delete mode 100644 .changeset/fierce-eagles-roam.md delete mode 100644 .changeset/fierce-hawks-climb.md delete mode 100644 .changeset/fierce-lemurs-forage.md delete mode 100644 .changeset/fierce-mice-munch.md delete mode 100644 .changeset/fierce-quails-cheer.md delete mode 100644 .changeset/gallant-foxes-squeak.md delete mode 100644 .changeset/gallant-jaguars-forage.md delete mode 100644 .changeset/gallant-otters-fly.md delete mode 100644 .changeset/gallant-voles-wake.md delete mode 100644 .changeset/gentle-badgers-forage.md delete mode 100644 .changeset/gentle-foxes-glide.md delete mode 100644 .changeset/gentle-moles-sprint.md delete mode 100644 .changeset/gentle-wasps-wave.md delete mode 100644 .changeset/gentle-wolves-hum.md delete mode 100644 .changeset/graceful-dogs-tumble.md delete mode 100644 .changeset/graceful-jaguars-frolic.md delete mode 100644 .changeset/graceful-lynx-hop.md delete mode 100644 .changeset/graceful-moles-sing.md delete mode 100644 .changeset/graceful-seals-dance.md delete mode 100644 .changeset/graceful-seals-march.md delete mode 100644 .changeset/graceful-wolves-wake.md delete mode 100644 .changeset/happy-bears-hop.md delete mode 100644 .changeset/happy-birds-roar.md delete mode 100644 .changeset/happy-ibex-romp.md delete mode 100644 .changeset/happy-jaguars-roar.md delete mode 100644 .changeset/happy-quails-parade.md delete mode 100644 .changeset/happy-ravens-run.md delete mode 100644 .changeset/happy-voles-chatter.md delete mode 100644 .changeset/humble-cats-sprint.md delete mode 100644 .changeset/humble-koalas-snooze.md delete mode 100644 .changeset/humble-lemurs-roar.md delete mode 100644 .changeset/jolly-geese-wake.md delete mode 100644 .changeset/jolly-jaguars-caper.md delete mode 100644 .changeset/jolly-seals-purr.md delete mode 100644 .changeset/jolly-voles-rally.md delete mode 100644 .changeset/kind-deer-caper.md delete mode 100644 .changeset/kind-jaguars-romp.md delete mode 100644 .changeset/lazy-otter-purchase.md delete mode 100644 .changeset/lively-bears-click.md delete mode 100644 .changeset/lively-birds-frolic.md delete mode 100644 .changeset/lively-orcas-climb.md delete mode 100644 .changeset/loud-jade-canyon.md delete mode 100644 .changeset/lucky-bears-forage.md delete mode 100644 .changeset/lucky-ravens-leap.md delete mode 100644 .changeset/merry-badgers-tumble.md delete mode 100644 .changeset/merry-cats-jump.md delete mode 100644 .changeset/merry-voles-swim.md delete mode 100644 .changeset/merry-wolves-climb.md delete mode 100644 .changeset/nimble-agents-route.md delete mode 100644 .changeset/nimble-jaguars-tumble.md delete mode 100644 .changeset/nimble-orcas-roam.md delete mode 100644 .changeset/nimble-orcas-tumble.md delete mode 100644 .changeset/nimble-ravens-hop.md delete mode 100644 .changeset/nimble-wasps-jump.md delete mode 100644 .changeset/noble-wasps-munch.md delete mode 100644 .changeset/patient-dogs-sprint.md delete mode 100644 .changeset/patient-foxes-click.md delete mode 100644 .changeset/patient-lynx-march.md delete mode 100644 .changeset/patient-yaks-click.md delete mode 100644 .changeset/plucky-tigers-roar.md delete mode 100644 .changeset/plucky-wolves-leap.md delete mode 100644 .changeset/proud-birds-travel.md delete mode 100644 .changeset/proud-wasps-forage.md delete mode 100644 .changeset/quick-finches-snooze.md delete mode 100644 .changeset/quick-lynx-wander.md delete mode 100644 .changeset/quick-moles-caper.md delete mode 100644 .changeset/quick-moles-purr.md delete mode 100644 .changeset/quick-pumas-climb.md delete mode 100644 .changeset/quick-quails-march.md delete mode 100644 .changeset/quick-rams-greet.md delete mode 100644 .changeset/quiet-marble-field.md delete mode 100644 .changeset/quiet-moons-derive.md delete mode 100644 .changeset/rapid-foxes-forage.md delete mode 100644 .changeset/rapid-foxes-glide.md delete mode 100644 .changeset/rapid-orcas-tumble.md delete mode 100644 .changeset/rapid-tunas-dance.md delete mode 100644 .changeset/reviewer-lane-env-disclosure.md delete mode 100644 .changeset/serene-birds-sing.md delete mode 100644 .changeset/serene-eagles-roar.md delete mode 100644 .changeset/serene-elks-sing.md delete mode 100644 .changeset/serene-finches-bark.md delete mode 100644 .changeset/serene-ibex-hum.md delete mode 100644 .changeset/serene-tigers-gather.md delete mode 100644 .changeset/sharp-birds-wave.md delete mode 100644 .changeset/sharp-ibex-tumble.md delete mode 100644 .changeset/sharp-moles-howl.md delete mode 100644 .changeset/sharp-wolves-wander.md delete mode 100644 .changeset/silly-jaguars-caper.md delete mode 100644 .changeset/silly-ravens-hum.md delete mode 100644 .changeset/soft-jade-quartz.md delete mode 100644 .changeset/steady-jaguars-dance.md delete mode 100644 .changeset/steady-pumas-click.md delete mode 100644 .changeset/steady-wasps-jump.md delete mode 100644 .changeset/sturdy-birds-chatter.md delete mode 100644 .changeset/sturdy-dogs-caper.md delete mode 100644 .changeset/sturdy-finches-caper.md delete mode 100644 .changeset/sturdy-foxes-gather.md delete mode 100644 .changeset/sturdy-hawks-munch.md delete mode 100644 .changeset/sturdy-koalas-zip.md delete mode 100644 .changeset/sturdy-mice-sprint.md delete mode 100644 .changeset/sturdy-tigers-click.md delete mode 100644 .changeset/sturdy-voles-run.md delete mode 100644 .changeset/sunny-badgers-howl.md delete mode 100644 .changeset/sunny-deer-hum.md delete mode 100644 .changeset/sunny-geese-hop.md delete mode 100644 .changeset/sunny-ravens-parade.md delete mode 100644 .changeset/sunny-wolves-gather.md delete mode 100644 .changeset/tame-river-song.md delete mode 100644 .changeset/tidy-bears-chatter.md delete mode 100644 .changeset/tidy-bears-wave.md delete mode 100644 .changeset/tidy-goats-jump.md delete mode 100644 .changeset/tidy-lynx-sing.md delete mode 100644 .changeset/tidy-ravens-sing.md delete mode 100644 .changeset/vivid-jaguars-purr.md delete mode 100644 .changeset/vivid-moles-chatter.md delete mode 100644 .changeset/vivid-pumas-jump.md delete mode 100644 .changeset/wise-jaguars-cheer.md delete mode 100644 .changeset/wise-seals-zip.md delete mode 100644 .changeset/witty-herons-march.md delete mode 100644 .changeset/witty-lynx-greet.md delete mode 100644 .changeset/witty-wasps-bark.md delete mode 100644 .changeset/zesty-ibex-hop.md delete mode 100644 .changeset/zesty-moles-tumble.md delete mode 100644 .changeset/zesty-pumas-hum.md delete mode 100644 .changeset/zesty-rams-march.md delete mode 100644 .changeset/zesty-tigers-forage.md diff --git a/.changeset/1884-planning-lock-mkdir-failure.md b/.changeset/1884-planning-lock-mkdir-failure.md deleted file mode 100644 index a290595a9..000000000 --- a/.changeset/1884-planning-lock-mkdir-failure.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Fixed -pr: 3472 ---- - -**`withPlanningLock` no longer reports a phantom "held by a live process" timeout when `.planning/` cannot be created** — a best-effort `try { platformEnsureDir(...) } catch { /* ok */ }` swallowed the real mkdir failure (EACCES/ENOSPC/EROFS), so the subsequent lock write failed with ENOENT (parent missing), and because ENOENT is in the lock's retry set (added for a Docker overlay-fs race) the loop spun the full 10 s budget before throwing a misattributed contention error that pointed operators at a nonexistent lock-holder. The mkdir failure now propagates immediately with its real filesystem errno and message, so an unwritable or full disk is reported as itself, not as concurrent-writer contention. The Docker overlay-fs ENOENT *lock-write* race (directory present) is still retried as before, and every code path where `.planning/` already exists or can be created is unchanged. Part of epic #1879 (distinguish "absent" from "corrupt/permission-denied" across engine read paths). (#1884) diff --git a/.changeset/2115-plan-phase-ai-keyword-precision.md b/.changeset/2115-plan-phase-ai-keyword-precision.md deleted file mode 100644 index 6fa6827dd..000000000 --- a/.changeset/2115-plan-phase-ai-keyword-precision.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Changed -pr: 3431 ---- - -**The `plan-phase` AI-integration capability gate no longer lists substring-collidable keywords** — bare `eval` (a substring of ordinary phase-goal words like `evaluation` and `retrieval`) is replaced by `llm eval`, and the under-specified `ai system` is dropped, per maintainer triage on the linked issue. The gate is a capability prompt, not a hard block, so this is a precision improvement: phase goals like "add evaluation metrics" or "build the retrieval layer" no longer invite a spurious AI-SPEC branch, and genuinely AI-flavored goals still match on the precise framework and technique names. (#2115) diff --git a/.changeset/2229-explore-claim-disposition.md b/.changeset/2229-explore-claim-disposition.md deleted file mode 100644 index 704c447de..000000000 --- a/.changeset/2229-explore-claim-disposition.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 2543 ---- -**`/gsd-explore` research passes now disposition each surfaced claim three ways** — **admit** (survives a prompted-to-refute pass and is grounded in a source, shown with the source), **refute** (a source contradicts it, dropped or corrected), or **abstain** (unverifiable, or a source-vs-prior conflict). Abstained claims go to a separate **Unresolved** ledger instead of being smoothed into confident prose, so you can see what the research could not stand behind. Refute and abstain are separated by whether the disagreeing source is *authoritative for that claim* — a blog post contradicting your `engines` field is an abstain, the `engines` field itself is a refute — and your own prior belief is never authoritative alone. A finding that comes back with no disposition at all is ledgered as an abstain rather than silently dropped or asserted as prose. Two guards ship with it: conflict-abstention (a source-vs-prior conflict routes to the ledger, not a silent pick-a-side) and a tier floor (a would-be admit is presented as an abstain when the researcher's resolved tier is budget-level or could not be determined, because an under-tiered or unverified researcher over-defers to whatever source it was handed; corrections are unaffected). Keying the floor on the resolved tier rather than the model id keeps it working on non-Claude installs, where the model id is often blank or substituted by the runtime. The floor narrows this gap rather than closing it — a config that deliberately repoints one tier at another tier's model can still report a higher tier than what actually runs. Claims-side analogue of the honest verifier. (#2229) diff --git a/.changeset/2570-smartentry-stale-activity-suffix.md b/.changeset/2570-smartentry-stale-activity-suffix.md deleted file mode 100644 index 2bc88c66e..000000000 --- a/.changeset/2570-smartentry-stale-activity-suffix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 2571 ---- -**The idle/staleness detector now fires when `last_activity` carries a description** — a `last_activity` written in the shape `templates/state.md` prescribes (`[YYYY-MM-DD] — [What happened]`) parsed to `NaN`, and because the detector treats an unparseable value as "not stale" it failed open to `false`. Any project whose `last_activity` kept its description was never reported idle, no matter how long it had sat. The leading date is now parsed out of the value, so the description no longer blinds the only staleness signal in the front door. An impossible calendar date such as `2026-02-30` is now rejected outright rather than silently rolling forward to a real — and wrong — date. (#2570) diff --git a/.changeset/2573-state-head-freshness.md b/.changeset/2573-state-head-freshness.md deleted file mode 100644 index c399f1db1..000000000 --- a/.changeset/2573-state-head-freshness.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 2622 ---- -**STATE.md now records the commit it was written against** — a new `state_head` frontmatter stamp lets `/gsd-health` and smart-entry report how far the codebase has moved since STATE.md was last written, so a long-stale STATE.md can be discounted rather than read at face value. Health adds advisory `W024` once the gap reaches 20 commits. This is a freshness proxy, not a drift measurement: the count includes commits that never touched anything STATE.md describes, and the stamp refreshes on any state write — so it is always worded as approximate and never gates anything. The stamp is omitted entirely when the commit cannot be resolved to the project's *own* repository — a project nested inside an unrelated checkout reports unknown rather than borrowing that repo's freshness. (#2573) diff --git a/.changeset/2783-ship-note-wedged-pr.md b/.changeset/2783-ship-note-wedged-pr.md deleted file mode 100644 index bb096cc4c..000000000 --- a/.changeset/2783-ship-note-wedged-pr.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: Fixed -pr: 2818 ---- -**`/gsd-ship` now detects and recovers a PR wedged by the ship-note commit** — when the `[ci skip]` ship note leaves required checks unstarted, ship re-triggers CI instead of leaving the PR unmergeable. (#2783) - -*Note: This introduces a latency tradeoff. All `/gsd-ship` invocations now poll GitHub PR state for up to 15 seconds to ensure the commit was processed and check if recovery is needed, even for repositories without required checks.* diff --git a/.changeset/3102-render-edge-coverage-report.md b/.changeset/3102-render-edge-coverage-report.md deleted file mode 100644 index 0075447fc..000000000 --- a/.changeset/3102-render-edge-coverage-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3391 ---- -**`spec-phase` Step 5.5 now surfaces the edge-probe's proposed edges to the resolution loop instead of discarding them** — the deterministic coverage report was computed, validated, then reduced to a single applicable-count, so the resolution loop re-derived edge categories from requirement prose and the engine's proposals never reached it. The report is now rendered into context and its rows are consumed as a *floor* the model unions with its own classification (still adding any category the classifier missed), so the written `## Edge Coverage` reflects the engine's deterministic taxonomy rather than model-invented categories; `--auto` gets the same floor. (#3102) diff --git a/.changeset/3174-quick-verification-status-query.md b/.changeset/3174-quick-verification-status-query.md deleted file mode 100644 index ed9257464..000000000 --- a/.changeset/3174-quick-verification-status-query.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3205 ---- -**`/gsd-quick --validate` no longer trusts a verification result it cannot actually read** — quick parsed the verifier's status by grepping the whole report rather than its frontmatter, so a `status:` line in the report's prose could be picked up alongside or instead of the real one, staleness was never detected at all, and a range of valid and malformed reports alike resolved to a value no routing arm matched — leaving the orchestrator to improvise at the moment the pipeline had failed. Quick now reads the same frontmatter-anchored, staleness-aware `verification.status` query that `execute-phase`, `verify-work` and `progress` already use, and routes `missing` / `unknown` / `stale` through an explicit arm instead of falling through. (#3174) diff --git a/.changeset/3206-verifier-explicit-evidence.md b/.changeset/3206-verifier-explicit-evidence.md deleted file mode 100644 index fa686f67f..000000000 --- a/.changeset/3206-verifier-explicit-evidence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3435 ---- -**The verifier's non-inferable (`backstop`) abstention rule now defines "explicit evidence" where the verifier is guaranteed to read it.** Step 3 item 5b used the term undefined — its definition was stranded in `gsd-core/references/honest-verifier.md` behind a stale `references/` cite that does not resolve, so the term fell back to the verifier's default notion of evidence (symbol presence + wiring), the exact false-pass the #1154 abstention protocol exists to refuse. 5b now carries the definition inline (a passing wired held-out/property-based test or directly observed behavior; presence + wiring never qualifies), the AFK never-silent/never-halt completion line and the `insufficient_spec`-vs-manual-UAT distinction ship in the eagerly-loaded `verifier-phase-gates.md` reference, and the agent file's three stale bare `references/` cites are gone: the two at 5c and the MVP-mode section now resolve under the `gsd-core/` prefix, and 5b's is superseded by the inline definition itself. (#3206) diff --git a/.changeset/3210-precondition-blocking-human-retry-ceiling.md b/.changeset/3210-precondition-blocking-human-retry-ceiling.md deleted file mode 100644 index 172a30612..000000000 --- a/.changeset/3210-precondition-blocking-human-retry-ceiling.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Fixed -pr: 3528 ---- - -**Autonomous/auto-mode no longer auto-approves unmet `` checkpoints, and the blocker loop now halts `needs_human` instead of retrying forever** — the checkpoint an executor returns when a task's `` is unmet (an unmet `user_setup` step, a missing env var, an absent prior-phase artifact) now carries `gate="blocking-human"`, which both auto-mode bypass layers (executor checkpoint protocol and execute-phase checkpoint handling) honor, so it always stops for a human instead of being silently approved with a synthetic "approved" and then failing `` on the still-missing prerequisite. Independently, `/gsd:autonomous`'s blocker handler now counts "Fix and retry" attempts per phase step and, after 3 failed attempts, escalates to a terminal `needs_human` halt that surfaces the unmet items and records a `## Needs Human` STATE.md row, ending the observed multi-hour retry loops on operator-gated plans. (#3210) diff --git a/.changeset/3481-state-phase-placeholder.md b/.changeset/3481-state-phase-placeholder.md deleted file mode 100644 index f54fb753f..000000000 --- a/.changeset/3481-state-phase-placeholder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3522 ---- -**`state add-roadmap-evolution` and `state add-decision` no longer persist a literal `Phase ?` when `--phase` is omitted** — both commands built their entry from the raw CLI flag's `?` fallback instead of the phase already recorded in STATE.md, even with `current_phase: 3` present in frontmatter. Both now resolve the phase through a strict write-path ladder (frontmatter `current_phase` → body `Current Phase` → `Phase: X of Y` scoped strictly to `## Current Position`), leaving `?` only when genuinely unresolvable; an explicit `--phase` still wins. The resolver deliberately does not reuse the read-path `resolveStatePhase`, whose document-wide fallback could adopt a stale historical `| Phase | N |` table row. A guard test now sweeps `src/*.cts` for any new raw `phase || '?'` call site. (#3481) diff --git a/.changeset/3497-frontmatter-escape-amplification.md b/.changeset/3497-frontmatter-escape-amplification.md deleted file mode 100644 index 0722ae200..000000000 --- a/.changeset/3497-frontmatter-escape-amplification.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3521 ---- -**Frontmatter round-trips no longer double backslashes on every state write** — `escapeDoubleQuoted` escaped `\`, `"`, and control characters on each serialize while the parser only stripped the outer quote delimiters, so every read-modify-write cycle doubled existing escapes (2ⁿ−1 backslashes after n cycles). `syncStateFrontmatter` carries `last_activity_desc` through that seam on every state command, growing STATE.md unboundedly — the reported 134 MB file OOMed `state.record-session` after 26 writes. Double-quoted scalars are now un-escaped on parse via the exact inverse of the escaper, making serialize→parse a fixed point; unrecognized escapes are kept literally so hand-authored files parse unchanged. (#3497) diff --git a/.changeset/3503-diff-base-scope-anchor.md b/.changeset/3503-diff-base-scope-anchor.md deleted file mode 100644 index 2c72430a0..000000000 --- a/.changeset/3503-diff-base-scope-anchor.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Fixed -pr: 3526 ---- - -**`/gsd:code-review` now derives the phase diff base from GSD's own commit scopes instead of a prose phrase, ending silently wrong review scopes** — the diff base fed to the reviewer file-list fallback, the SUMMARY↔diff cross-check union, the reviewer agent's `diff_base`, and the fallow `--changed-since` structural pass was greped from commit messages for the literal "Phase N" and kept the oldest match, so any prose mention anywhere in history (a planning commit deferring work "to Phase N per D-09", a doc commit using "### Phase N" as a format example) silently set the base months before the phase existed — on a real repo ~4 phases too early, inflating the reviewer's reading list ~78% with no warning — while GSD's own commits (`docs(phase-N):`, `feat(N-MM):`, `docs(N):`), which never contain the literal phrase, were never matched at all. All three derivations now anchor on the subject-line conventional-commit phase scope (both padded `06` and unpadded `6` spellings, since workflows emit the unpadded roadmap number), commit bodies can no longer capture the base, and a history with no scope-style commits fails loudly with the existing no-base warning and `--files` escape hatch instead of silently picking an arbitrary commit. (#3503) diff --git a/.changeset/3518-uat-path-phase-pinned.md b/.changeset/3518-uat-path-phase-pinned.md deleted file mode 100644 index 91b56d2b0..000000000 --- a/.changeset/3518-uat-path-phase-pinned.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Fixed -pr: 3525 ---- - -**`uat_path` is now pinned to the phase's own UAT artifact instead of being picked by unsorted directory-listing order** — both `uat_path` projections (`init plan-phase` and `init phase-op`) selected the phase's `*-UAT.md` with a bare first-match `.find()` that had no phase-membership check and no ordering, so a stray or cross-phase `04-UAT.md` sitting in phase 03's directory could become phase 03's `uat_path`, and which file won was filesystem-dependent (creation order on APFS, hash order on ext4/XFS) — meaning two machines on the same commit could emit different `uat_path` values for the same phase, sending downstream workflows to read another phase's UAT state. Both sites now route through a shared phase-pinned resolver (`resolveUatFile`, sibling of the `resolveVerificationFile` rule from #3357/#3492): the phase's own `-UAT.md` always wins, otherwise the alphabetically-first dashed candidate, deterministically on every machine. (#3518) diff --git a/.changeset/3527-mempalace-default-true-gates.md b/.changeset/3527-mempalace-default-true-gates.md deleted file mode 100644 index 336122693..000000000 --- a/.changeset/3527-mempalace-default-true-gates.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Fixed -pr: 3527 ---- - -**MemPalace sub-features whose defaults are enabled now run when their config keys are absent** — the earlier `capture_artifacts` absent-key fix (#2982) had been applied to only one of six hand-written config gates; the remaining gates for `mempalace.mirror_kg` (knowledge-graph mirroring in the capture and recall skills, their command mirrors, and the curator agent) and `mempalace.diary_journal` (per-agent diary entries at ship) still required the key to be explicitly present and `true`, so a project that enabled MemPalace without writing every sub-toggle silently never mirrored KG facts or wrote diary entries, with no warning. All six gates now treat an absent key as enabled (matching the capability registry's declared `default: true`) and disable the behavior only on an explicit `false`; default-off switches (`mempalace.enabled`, `cross_project_tunnels`) still require explicit opt-in, and a registry-parity regression test keeps future default-true keys from reintroducing the inversion. (#3479) diff --git a/.changeset/agile-cranes-frolic.md b/.changeset/agile-cranes-frolic.md deleted file mode 100644 index 8cc9d6c7f..000000000 --- a/.changeset/agile-cranes-frolic.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3635 ---- -**Roadmap `Plans:` lines keep their hand-written text instead of being overwritten with a plan count** — `roadmap update-plan-progress` replaced everything after the `Plans:` label whenever the line did not already begin with a canonical `N/N plans` token, silently destroying freeform prose, a `TBD` note, or a hand-written annotation. A sentence that wrapped onto a second line lost only its first line, leaving the continuation stranded so the roadmap asserted something nobody wrote — at exit 0, in a diff that read as a routine count bump. The count is now written only over a real count token or the fresh-template placeholder, and a single-plan phase (`1 plan`) is recognized rather than frozen. (#3584) diff --git a/.changeset/agile-elks-sing.md b/.changeset/agile-elks-sing.md deleted file mode 100644 index d24a37418..000000000 --- a/.changeset/agile-elks-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3650 ---- -**Running a capability's own test suite no longer silently deactivates it** — `bundleContentHash` digested every entry under a capability bundle with no exclusions, so ordinary Python bytecode caching (`__pycache__/*.pyc`, written by any plain `python3` run) changed the consent-binding hash. The capability then reported `inactive` with no error and no warning, and `loop render-hooks` quietly dropped its step and gate — indistinguishable from never having installed it. An *empty* `__pycache__` directory was enough to trigger it, since the digest binds directory existence. Only a `*.pyc`/`*.pyo` file sitting directly inside a `__pycache__` directory is now excluded from the digest; a `.pyc`/`.pyo` file anywhere else stays bound, since a sourceless legacy `.pyc` there is still importable and executable. A `__pycache__`/`.pytest_cache` directory has only its own marker suppressed — its contents still bind the digest normally. `node_modules` and other executable content stay bound, excluded entries still count toward the walk's caps, and the filter runs after the symlink rejection so it cannot smuggle one past. (#3631) diff --git a/.changeset/agile-rams-run.md b/.changeset/agile-rams-run.md deleted file mode 100644 index a4e327713..000000000 --- a/.changeset/agile-rams-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3199 ---- -**Live-plan counting now has one owner, so `superseded` plans stop being scheduled and nested-layout phases stop reporting zero** — `scanPhasePlans` is the sole source of which plans exist and which are outstanding. Twenty-one call sites that re-derived it from filenames now route through it, so a plan marked `status: superseded` is no longer scheduled into an execute-phase wave, phases using the nested `plans/` layout no longer report zero plans, and stray summaries no longer inflate completion. (#3183) diff --git a/.changeset/agile-tigers-cheer.md b/.changeset/agile-tigers-cheer.md deleted file mode 100644 index daa486818..000000000 --- a/.changeset/agile-tigers-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3536 ---- -**Corrected `model-profiles.md`: `model` and `effort` do not resolve through one shared precedence ladder** — the reference previously claimed a `models[phase_type]` or `dynamic_routing` override flips both, and that an effort config change takes effect like a model change. In reality effort (claude runtime) is baked into agent frontmatter at install time and requires `node gsd-tools.cjs effort sync --apply` to change; Codex agents pin `model_reasoning_effort` in generated `.toml` files. (#3530) diff --git a/.changeset/amber-cobalt-spark.md b/.changeset/amber-cobalt-spark.md deleted file mode 100644 index f422f0b35..000000000 --- a/.changeset/amber-cobalt-spark.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3377 ---- -**`requirements mark-complete` now flips the traceability row when `## Traceability` holds more than one table** — `updateTableCell` no longer binds to the first table in the section; it scans for the table that actually carries the requested column. A section with a phase-summary table above the requirement rows previously made the Status write silently bail (`table_unmatched`) while the checkbox still flipped, leaving the row at `Pending` indefinitely. Single-table sections are unchanged. (#3255) diff --git a/.changeset/bold-badgers-climb.md b/.changeset/bold-badgers-climb.md deleted file mode 100644 index b81c96f9e..000000000 --- a/.changeset/bold-badgers-climb.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3542 ---- -**`resolve-execution` now tells the truth about what the agent will run at** — the query reported only the config-cascade effort, which is not what an installed agent uses when its `effort:` frontmatter was hand-stripped or drifted. `--json` adds `effort_effective` (read from the installed agent frontmatter for the claude runtime; `"inherit"` when the key is absent) and `effort_effective_source` (`frontmatter` | `frontmatter-absent` | `resolved`). All existing fields, including `--pick effort`, are unchanged. (#3534) diff --git a/.changeset/bold-jaguars-run.md b/.changeset/bold-jaguars-run.md deleted file mode 100644 index 649ddefa3..000000000 --- a/.changeset/bold-jaguars-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3429 ---- -**`init execute-phase` no longer hands a directory slug to the phase-start flow as the phase display name.** When a phase's working directory already exists on disk, the disk-lookup path derived `phase_name` from the directory-name remainder — itself an already-slugified value (`phase.add` writes `${num}-${slug}` dirs) — so `phase_name` and `phase_slug` came out byte-identical. The execute-phase workflow forwards `phase_name` into `state begin-phase --name`, which wrote that raw slug into STATE.md's `current_phase_name` on every phase start (`loop-termination-and-baseline-correctness` instead of `Loop-Termination and Baseline Correctness`). `init execute-phase` now prefers the ROADMAP's curated display name (`### Phase N: `) for `phase_name`, matching the no-disk fallback path that already did this correctly; `phase_slug` is unchanged so branch-name construction is unaffected. The `state begin-phase` override mechanism (#2821/#2736) is untouched. (#3171) diff --git a/.changeset/bold-lynx-dart.md b/.changeset/bold-lynx-dart.md deleted file mode 100644 index 79c2ac593..000000000 --- a/.changeset/bold-lynx-dart.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3281 ---- -**Two GSD workflows told agents that a Claude Code `Agent()` spawn blocks until the subagent finishes** — Claude Code backgrounds subagents by default, so `/gsd-execute-phase` could treat a wave as returned when it had not, and `/gsd-debug` lost its session-manager handoff in exactly the way #2196 was filed to fix. The dispatch notes now match this package's own shipped capability matrix, and both debug spawns carry the `run_in_background: false` opt-out they always needed. (#3177) diff --git a/.changeset/bold-orcas-sing.md b/.changeset/bold-orcas-sing.md deleted file mode 100644 index 202c34868..000000000 --- a/.changeset/bold-orcas-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3323 ---- -**The install manifest now records which runtime and scope wrote it** — a global and a project-local install used to write two `gsd-file-manifest.json` files that neither named their own runtime nor their own scope, so nothing could answer "which GSD surfaces are installed, where". The manifest gains `manifestVersion`, `runtime` and `scope`, and a new read-only Installed Surface Resolver reads both scopes at once. Manifests written by earlier versions are read without error and need no reinstall. (#2872) diff --git a/.changeset/bold-otters-scope.md b/.changeset/bold-otters-scope.md deleted file mode 100644 index be26fe8bd..000000000 --- a/.changeset/bold-otters-scope.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Changed -pr: 3318 ---- - -**A percentage is now withheld everywhere its scope is not `COMPLETE`, not just at the sites Phase 3 reached** — closing ADR-3180 §7.6 rule 4 at the two remaining gaps an isolated review caught: `state json`'s `buildStateFrontmatter` no longer hardcodes `SCOPE.COMPLETE` when deriving `progress.percent` (it now threads the real `listMilestonePhaseDirs` scope through `_diskScanCache`, including its prose-fallback path, so a genuinely unreadable `.planning/phases` directory can no longer surface a stale or falsely-earned number there while every other surface withholds), and `roadmap analyze --json` now exposes the scope that actually gates `progress_percent` as its own `progress_scope` field — distinct from the top-level `scope` (heading-windowing identity) — so a consumer can tell *why* `progress_percent` is `null` from the JSON alone instead of seeing `scope: "complete"` next to an unexplained `null`. `state update-progress` also now writes a `[gsd-tools] WARNING:` line to stderr when it silently no-ops on a non-`COMPLETE` scope, so the skip is not visible only to a JSON `reason` field most callers never read. **`state sync` now also withholds**: it no longer hardcodes `SCOPE.COMPLETE` when deriving the percentage it writes into `STATE.md`'s body — a non-`COMPLETE` scope (confirmed reproducible on `TRUNCATED` and `UNSCOPED` fixtures, not just the previously-checked `UNREADABLE` case) skips the `Progress:` write entirely and records a `Progress: skipped — …(#3217)` entry in `changes`, instead of persisting a fabricated percentage that could disagree with the same write's own (already-scoped) frontmatter `progress:` block. `0` under a genuinely `COMPLETE` scope is unaffected and still renders. Tier-2: `progress_percent`, `percent`, and `plan_percent` are `number | null`; `computeProgressPercent` requires a `scope` argument; `roadmap analyze --json` gains a new `progress_scope` field; `state sync --raw`'s `changes` array can now contain a scope-skip entry and correspondingly withhold a `Progress:` body write it would previously have made. (#3217) diff --git a/.changeset/bold-wasps-hop.md b/.changeset/bold-wasps-hop.md deleted file mode 100644 index 6cbb58396..000000000 --- a/.changeset/bold-wasps-hop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3478 ---- -Executor dispatch prompts now state checkpoint gate semantics: gate="blocking" (the default) is auto-approvable in auto-mode, only gate="blocking-human" always surfaces to a human. The phase-level and single-plan-level orchestrators no longer leave room to compose dispatch text that refuses auto-approval, which stalled autonomous runs at ordinary blocking checkpoints. diff --git a/.changeset/brave-geese-bark.md b/.changeset/brave-geese-bark.md deleted file mode 100644 index a6c881245..000000000 --- a/.changeset/brave-geese-bark.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3237 ---- -**`gsd-plan-checker` now flags same-wave plans that are coupled but don't say so** — two plans in the same wave that share mutable state (a config key, table, migration, env var, singleton) or depend on each other's execution order, with no `depends_on` edge between them, are reported as an advisory Dimension 3 finding. The coupling gets settled at plan time instead of surfacing as an intermittent failure during parallel execution. `docs/AGENTS.md`'s plan-checker entry, which claimed eight verification dimensions and listed eight names matching none of the agent's actual fifteen, is corrected to the real list in the same change. (#1954) diff --git a/.changeset/calm-bears-howl.md b/.changeset/calm-bears-howl.md deleted file mode 100644 index b519e505d..000000000 --- a/.changeset/calm-bears-howl.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -type: Changed -pr: 3283 ---- -state validate now runs its drift scan for STATE.md files whose phase lives only in frontmatter, instead of silently skipping the scan and reporting a false-clean result (#3162); it also no longer lets a frontmatter status: key shadow the body Status field. Its output gains a scope field (complete/truncated/unscoped/unreadable) reporting whether the check could actually run — valid still means no drift was found, and is not derived from scope. (#3187) - -state complete-phase's idempotency guard now consults frontmatter current_phase (via the same fallback chain as state validate), so a STATE.md whose phase lives only in frontmatter is no longer silently rolled back on a re-run of `state complete-phase --phase N`. It also gains a new refusal path: when the frontmatter cannot be parsed, the command now errors out ("Unable to read STATE.md frontmatter; refusing to run complete-phase to avoid a destructive rollback") instead of guessing. (#3187) - -workstream list/status/progress's per-workstream state projection (status, current_phase, last_activity) now resolves those fields from frontmatter when the body has no corresponding field, instead of reporting them absent — a frontmatter-only STATE.md's workstream inventory output changes accordingly. (#3187) diff --git a/.changeset/calm-bears-sing.md b/.changeset/calm-bears-sing.md deleted file mode 100644 index bb25e3574..000000000 --- a/.changeset/calm-bears-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3539 ---- -**`effort.routing_tier_defaults` now merges over the built-in tier defaults instead of replacing them** — previously, creating an `effort` block without `routing_tier_defaults` silently disabled the built-in tier ladder (light:low / standard:high / heavy:xhigh), collapsing every non-overridden agent to `high`; one `agent_overrides` entry could reshape 20+ agents you never named. A partial block now fills gaps from the built-ins, and an invalid value falls back to that tier's built-in. (#3531) diff --git a/.changeset/calm-fern-meadow.md b/.changeset/calm-fern-meadow.md deleted file mode 100644 index 8ecf68612..000000000 --- a/.changeset/calm-fern-meadow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3369 ---- -**`gsd-tools validate health` no longer flags `.planning/WINDOWS.md` as an unrecognized file** — the broken-windows ledger that gsd-core's own `windows` command writes is now registered as a canonical `.planning/` artifact. Previously the W019 warning advised archiving or deleting a file that, with `workflow.windows_enforce` on, gates `/gsd-ship`. (#3224) diff --git a/.changeset/calm-ibex-travel.md b/.changeset/calm-ibex-travel.md deleted file mode 100644 index 3f7c8abc2..000000000 --- a/.changeset/calm-ibex-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3501 ---- -**milestone complete no longer lets a stale STATE.md body line overwrite fresher frontmatter** — it wrote through a path that re-derived frontmatter from the body with no preservation pass, so a stale Stopped-at line silently replaced a newer curated value, exactly as phase complete did before it was fixed. It now runs the same preservation the rest of the write path uses, and reports each field it protected in a new preservation_warnings array instead of staying silent about the divergence. (#3469) diff --git a/.changeset/calm-lemurs-sing.md b/.changeset/calm-lemurs-sing.md deleted file mode 100644 index a38c2d1c7..000000000 --- a/.changeset/calm-lemurs-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3416 ---- -**GSD now requires Node 24 or newer** — the `engines.node` floor moves from 22 to 24, and the Node 22 test lane is retired. Node 22 entered Maintenance LTS and this project tracks the Active LTS line; the change is what lets regex escaping delegate to the built-in `RegExp.escape` instead of a hand-rolled implementation. If you are on Node 22, upgrade before updating GSD. diff --git a/.changeset/calm-voles-howl.md b/.changeset/calm-voles-howl.md deleted file mode 100644 index aa4b868a9..000000000 --- a/.changeset/calm-voles-howl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3438 ---- -gap-analysis check gap-analysis.plan-post no longer reports prose trailing the requirement ID list as missing requirements. ROADMAP Requirements lines routinely carry locked-decision annotations, ambiguity scores, and prohibition notes after the ID list; passing that value verbatim into --phase-req-ids previously caused every prose word to be reported as an individually-missing requirement, drowning the real coverage signal. Tokens that cannot be requirement IDs (prose, punctuation, dates) are now dropped after range expansion. diff --git a/.changeset/calm-wasps-dart.md b/.changeset/calm-wasps-dart.md deleted file mode 100644 index 64386d99c..000000000 --- a/.changeset/calm-wasps-dart.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3487 ---- -state.patch now reports a field as updated only when its post-write on-disk value matches the requested value; fields the write pipeline re-derives away (e.g. current_phase, current_phase_name) are reported as failed instead of phantom updated diff --git a/.changeset/clever-bears-wander.md b/.changeset/clever-bears-wander.md deleted file mode 100644 index c86499a73..000000000 --- a/.changeset/clever-bears-wander.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3609 ---- -**Opt-in `.git/hooks/pre-commit` guard for `commit_docs`** — `gsd-tools commit-docs-guard enable`/`disable` writes (or removes) a pre-commit hook that shells out to the existing `check-commit` verb, refusing a commit that stages `.planning/` files while `commit_docs` resolves to `false`. Closes the one bypass earlier phases of epic #2292 could not reach: a plain `git add -A && git commit` run by hand or by a script outside GSD's own tooling. Fully opt-in by maintainer narrowing — no install path wires it in by default (regression-locked by `tests/commands.test.cjs`'s E2 row); `enable` refuses rather than overwrites an existing foreign `pre-commit` hook, refuses when `core.hooksPath` would make the written hook inert, and resolves the real hooks directory via `git rev-parse --git-path hooks` so a linked worktree or submodule (where `.git` is a file) is handled correctly rather than assuming a literal `.git/hooks` path. The hook is identified by a stable `# gsd-core:commit-docs-guard` marker line, checked by presence rather than byte-equality. (#3588) diff --git a/.changeset/clever-cranes-zip.md b/.changeset/clever-cranes-zip.md deleted file mode 100644 index cae65f2ac..000000000 --- a/.changeset/clever-cranes-zip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3568 ---- -**installRuntimeArtifacts() now returns the plan it executed** — per kind, per scope, including on the combined OpenCode/Kilo family path that previously returned nothing — so an install's correctness is a value a caller can assert, not something only re-readable from disk afterward. Install IO routes through a new injectable fs seam (`install-fs-adapter.cts`), letting a full install run end-to-end against a fake adapter with no real destination filesystem contact; failures still throw rather than becoming a value, and a best-effort cleanup that fails is now visible in the return instead of silently swallowed. Writes on disk are unchanged. Completes ADR-58's never-landed `cleanup` rollout step. (#2874) diff --git a/.changeset/clever-eagles-wake.md b/.changeset/clever-eagles-wake.md deleted file mode 100644 index 0c5ac28b1..000000000 --- a/.changeset/clever-eagles-wake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3253 ---- -**Capability skills are now named at the install consent prompt** — installing a third-party capability whose only contribution was skills printed "ships no executable surfaces (declarative only)" and listed nothing, even though each `SKILL.md` body lands verbatim in your agent's instruction context. The pre-install disclosure now names every contributed skill in its own section and states plainly that the bodies are not content-scanned. Values interpolated into the prompt are escaped across every disclosed surface, so a crafted name can no longer forge additional lines of disclosure text. No stored consent is disturbed and no re-consent prompt fires. (#3248) diff --git a/.changeset/clever-tigers-dart.md b/.changeset/clever-tigers-dart.md deleted file mode 100644 index 4a99171dd..000000000 --- a/.changeset/clever-tigers-dart.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3600 ---- -**User profile and dev-preferences files are no longer lost when an install or uninstall is interrupted.** These files were held only in memory while GSD deleted and rebuilt the directory containing them, so pressing Ctrl-C — or any crash during the copy — destroyed them permanently. On the main install path that window spanned the entire gsd-core tree rebuild. They are now staged to disk before anything is deleted, and any copy orphaned by an interrupted run is restored automatically on the next install or uninstall. (#1874) diff --git a/.changeset/clever-voles-swim.md b/.changeset/clever-voles-swim.md deleted file mode 100644 index 56bf634fc..000000000 --- a/.changeset/clever-voles-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3434 ---- -**`/gsd:code-review-fix --auto` now commits the converged REVIEW.md alongside REVIEW-FIX.md and reliably commits REVIEW-FIX.md at all** — the --auto re-review loop overwrote REVIEW.md every iteration but the workflow's single docs commit staged only REVIEW-FIX.md, so the committed REVIEW.md stayed at iteration 1 and contradicted the committed REVIEW-FIX.md (and the converged REVIEW.md plus .iterN.md backups survived only as uncommitted working-tree state). Separately, the two inline frontmatter validators exported REVIEW_PATH into a node -e body that reads process.env.FIX_REPORT_PATH, so the status check was always empty and REVIEW-FIX.md was never committed (the user was wrongly told the agent produced malformed output). The validators now export FIX_REPORT_PATH, the --auto commit stages REVIEW.md too, and spent .iterN.md backups are removed on successful convergence (retained on degradation). Non-auto single-pass runs are unchanged. (#3190) diff --git a/.changeset/curious-goats-zip.md b/.changeset/curious-goats-zip.md deleted file mode 100644 index 48a6d154a..000000000 --- a/.changeset/curious-goats-zip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3558 ---- -**Three shell guards that could never fire now do** — the planner's Walking Skeleton mode never activated on any project, phase planning recorded an empty requirement list instead of `TBD`, and completing a milestone with no phase summaries could hang instead of finishing. Each read a value that came back empty on success, so the fallback written to handle it was unreachable. (#3409) diff --git a/.changeset/curious-koalas-travel.md b/.changeset/curious-koalas-travel.md deleted file mode 100644 index ba2bd3099..000000000 --- a/.changeset/curious-koalas-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3596 ---- -**Shipped workflow/agent citations resolve again** — 43 backticked `references/.md` cites across 19 shipped files were dead pointers from every install location; all repaired to the canonical `gsd-core/references/.md` form, and a new sweep gate fails the build on any future bare cite across the runtime-loaded trees. (#3576) diff --git a/.changeset/curious-mice-munch.md b/.changeset/curious-mice-munch.md deleted file mode 100644 index 3fdb11f55..000000000 --- a/.changeset/curious-mice-munch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3480 ---- -A genuinely milestone-sectioned ROADMAP whose STATE.md asserts a milestone token matching no heading no longer has progress.total_phases clobbered to the on-disk phase-directory count (e.g. 25 -> 4) on every state-mutating command. The stored total is preserved (or the key omitted when nothing is stored), a stderr warning names the unbounded milestone token, and progress.percent stays withheld as before. diff --git a/.changeset/curious-quails-tumble.md b/.changeset/curious-quails-tumble.md deleted file mode 100644 index 2b7dd041e..000000000 --- a/.changeset/curious-quails-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3486 ---- -Phase-directory collisions in .planning/phases/ (two in-scope dirs normalizing to the same phase number) no longer resolve by filesystem mtime — a checkout-order signal that made progress.total_plans and completed_plans differ across clones of the same commit. The survivor is now chosen deterministically by lexicographic directory name, and the collision is surfaced as a stderr warning naming both directories. diff --git a/.changeset/curious-tunas-fly.md b/.changeset/curious-tunas-fly.md deleted file mode 100644 index d7a3c01c1..000000000 --- a/.changeset/curious-tunas-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3437 ---- -code-review: every phase diff-base derivation now uses the same anchored, POSIX-portable phase-mention grep. Fixes wrong review scope from /gsd:code-review when a phase has no SUMMARY artifacts: the reviewer diff_base and the fallow --changed-since base no longer resolve to old unrelated commits whose messages merely contain the phase digits, and the anchored search now actually matches on macOS (the previous \b word boundary is not POSIX ERE and silently matched nothing there). diff --git a/.changeset/curious-tunas-greet.md b/.changeset/curious-tunas-greet.md deleted file mode 100644 index 39433470a..000000000 --- a/.changeset/curious-tunas-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3264 ---- -**Worktree-wave merges now warn when a plan branch committed outside its declared scope** — the `execute-phase` cleanup gauntlet compares each branch's actual committed diff against the `files_modified` the plan declared and reports every path outside it. Advisory only: the merge still proceeds and the exit status is unchanged. (#2596) diff --git a/.changeset/curious-zebras-squeak.md b/.changeset/curious-zebras-squeak.md deleted file mode 100644 index 75c79f811..000000000 --- a/.changeset/curious-zebras-squeak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Removed -pr: 3272 ---- -**The undocumented `runtime.hostBehaviors.reviewerCli` capability field has been removed** — it was superseded by the declared `reviewer` body in 1.9.0 and kept working for one release as a derived alias. A manifest that still sets it contributes no reviewer lane and now reports a non-fatal warning naming the capability, at build time on stderr and at install time through the overlay loader; nothing crashes and no other behavior changes. Every shipped reviewer lane already declares a `reviewer` body, so the roster is unchanged — if you maintain an out-of-tree runtime descriptor that relied on the flag, declare a `reviewer` body to restore the lane. (#2801) diff --git a/.changeset/daring-koalas-zip.md b/.changeset/daring-koalas-zip.md deleted file mode 100644 index 358e53291..000000000 --- a/.changeset/daring-koalas-zip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3400 ---- -**`phase add` no longer files new phases inside archived roadmap history** — the insertion point used the file's last horizontal rule, which on a long roadmap sits deep in shipped/archive content, so new phases landed under an unrelated archived phase's heading instead of at the end of the active phase list. Insertion is now scoped to the current milestone. (#3163) diff --git a/.changeset/daring-seals-wander.md b/.changeset/daring-seals-wander.md deleted file mode 100644 index 5dfca882a..000000000 --- a/.changeset/daring-seals-wander.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3589 ---- -**Agent isolation guard enforces on multi-runtime machines** — the isolation guard (and Cursor's subagent-start fallback) resolved the project runtime from the host-wide ~/.gsd/defaults.json, which names whichever runtime installed last; on machines with two runtimes this confidently picked the wrong runtime and silently disabled executor worktree policing. Both now read the per-install .gsd-runtime marker above that file. (#3566) diff --git a/.changeset/eager-cranes-gather.md b/.changeset/eager-cranes-gather.md deleted file mode 100644 index db7d52ac0..000000000 --- a/.changeset/eager-cranes-gather.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3634 ---- -**`state update-progress` no longer writes two different completion percentages in one call** — the verb printed plan throughput (summaries/plans) to stdout and into the body `Progress:` bar, while the same write independently derived the frontmatter `progress.percent` as the deliberate `min(plan, phase)` cap. Mid-phase, when plan throughput runs ahead of phase completion, STATE.md contradicted itself and `state json` disagreed with the command that had just written it — silently, at exit 0. All surfaces now derive from the single canonical computation, and its reported plan counts come from the same milestone window as the percent, so the verb's own output can no longer disagree with itself. When that computation withholds a percent, the verb withholds too rather than substituting a different metric. The min-cap definition is unchanged. (#3583) diff --git a/.changeset/eager-lynx-romp.md b/.changeset/eager-lynx-romp.md deleted file mode 100644 index 6c375a185..000000000 --- a/.changeset/eager-lynx-romp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3223 ---- -**Progress percentages now come from one owner** — every `.planning/` completion percentage the CLI reports is computed by a single shared function instead of six hand-inlined copies, so a rounding or ceiling fix can no longer land on one command and silently miss the others. Reported values are unchanged. (#3180) diff --git a/.changeset/eager-pandas-glide.md b/.changeset/eager-pandas-glide.md deleted file mode 100644 index 8ccd7a027..000000000 --- a/.changeset/eager-pandas-glide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3633 ---- -**Fallow binary resolution now shares the platform seam** — resolving the fallow binary uses the same PATH/PATHEXT logic as every other spawn, so on Windows a `fallow.cmd` shim resolves correctly and an extensionless npm shim is no longer picked up in its place. `node_modules/.bin` is still searched before `PATH`, and the POSIX executable-bit check is unchanged. (#3618) diff --git a/.changeset/eager-rams-click.md b/.changeset/eager-rams-click.md deleted file mode 100644 index 832eca188..000000000 --- a/.changeset/eager-rams-click.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3425 ---- -**GSD skills no longer override the caller's effort level** (#3151) — invoking `/gsd-plan-phase`, `/gsd-execute-phase`, `/gsd-autonomous`, `/gsd-next`, `/gsd-progress`, or `/gsd-stats` previously set `output_config.effort` to a static value baked into the skill frontmatter; when that differed from the session's effort (which it did ~76% of the time), it invalidated the entire prompt cache at both scope boundaries (skill entry and exit). These skills now run at the session's existing effort level (no `effort:` emitted into SKILL.md). The elevated-effort intent is preserved on the source command files; only the skill-frontmatter emission is dropped. The separate agent-effort surface is unaffected. diff --git a/.changeset/eager-sloths-purr.md b/.changeset/eager-sloths-purr.md deleted file mode 100644 index d71478f15..000000000 --- a/.changeset/eager-sloths-purr.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3563 ---- -**Global OpenCode/Kilo installs no longer pin a tier-default model over your session selection** — a project's `model_profile: "inherit"` was invisible to the install-time resolver on global installs (it probes from the install dir and never reaches the project), so the `balanced` default silently baked e.g. `anthropic/claude-opus-4-8` into the agent frontmatter, which those runtimes use over the live `/model` selection — producing "Model not found" on providers without that exact id. A profile that cannot be verified now bakes no `model:` line, so subagents follow the session model as documented; declare `model_profile` in `~/.gsd/defaults.json` to pin tiers machine-wide. (#3543) diff --git a/.changeset/eager-wasps-travel.md b/.changeset/eager-wasps-travel.md deleted file mode 100644 index f339902ea..000000000 --- a/.changeset/eager-wasps-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3594 ---- -**`phase remove` no longer corrupts STATE.md after removing an inserted (decimal) phase** — the removed-phase write prepended a second, partially-wrong frontmatter block (and left the phase's ROADMAP heading behind, so total_phases kept counting it); removal now updates STATE.md in place as a single block, drops the heading, and clamps phase counts at zero. (#3572) diff --git a/.changeset/fierce-eagles-roam.md b/.changeset/fierce-eagles-roam.md deleted file mode 100644 index 2f1a08fe8..000000000 --- a/.changeset/fierce-eagles-roam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3405 ---- -**`validate health --backfill` now works without also passing `--repair`** — previously it silently did nothing unless `--repair` was also set, due to an unreachable internal gate. diff --git a/.changeset/fierce-hawks-climb.md b/.changeset/fierce-hawks-climb.md deleted file mode 100644 index d8989275b..000000000 --- a/.changeset/fierce-hawks-climb.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3452 ---- -gsd-health's STATE/ROADMAP staleness warning (W011) now reads the current phase from the YAML frontmatter format gsd-tools itself writes (current_phase), in addition to the legacy prose, canonical body, and pipe-table forms, and suppresses the warning when the recorded status reports completion in the state writer's own vocabulary (status: completed). The stale-worktree warning (W027) no longer advises unconditional forced removal: its remediation now directs checking for uncommitted work first (git -C status --porcelain), removing non-destructively when clean, with --force presented as an explicit opt-in to discard changes. diff --git a/.changeset/fierce-lemurs-forage.md b/.changeset/fierce-lemurs-forage.md deleted file mode 100644 index ad508f108..000000000 --- a/.changeset/fierce-lemurs-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3614 ---- -**Completing one phase no longer marks the whole milestone done** — `state complete-phase` wrote the body prose `Phase N complete`, and the status normalizer matches `complete` as a substring, so finishing phase 2 of 4 collapsed the milestone-level STATE.md frontmatter to `status: completed` while the very same call correctly recorded `completed_phases: 2` of `total_phases: 4`. Downstream automation that gates on milestone status — auto-advance, archival, ship gating — was told a half-open milestone was finished. Milestone status is now derived from those counters instead of from phase-level prose. (#3578) diff --git a/.changeset/fierce-mice-munch.md b/.changeset/fierce-mice-munch.md deleted file mode 100644 index 1df5f57b4..000000000 --- a/.changeset/fierce-mice-munch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Removed -pr: 3564 ---- -**Two workflow files that shipped to every runtime but were never loaded are gone** — `discovery-phase.md` and `plan-milestone-gaps.md` had no command, agent, or skill referencing them, and `docs/INVENTORY.md` claimed callers for one that did not exist. A new lint rule now fails the build if any shipped workflow becomes unreachable again. (#3560) diff --git a/.changeset/fierce-quails-cheer.md b/.changeset/fierce-quails-cheer.md deleted file mode 100644 index b82ea4098..000000000 --- a/.changeset/fierce-quails-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3607 ---- -**`progress`, `stats`, and `query progress` now report a real percentage inside a workstream** — under `--ws`, these commands counted the workstream's own phases and plans but read the milestone window from the project root, which `workstream create` has already migrated away. The scope resolved as unreadable and the percentage was withheld, so a fully-complete workstream reported no progress at all. **`milestone complete` no longer archives every phase directory when its milestone window is unreadable** — it previously fell back to moving everything on disk in that case; it now declines to archive and reports why, leaving the phase directories in place. (#3597) diff --git a/.changeset/gallant-foxes-squeak.md b/.changeset/gallant-foxes-squeak.md deleted file mode 100644 index 7f6be0401..000000000 --- a/.changeset/gallant-foxes-squeak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 3517 ---- -**MCP server configs are now explicitly flagged as unconfined in the capability consent prompt** — a capability's MCP servers can legitimately point at commands, args, env, and working directories anywhere on the machine (unlike its hooks, which are confined to the installed bundle), and the consent disclosure now says so plainly for every spawned server instead of leaving the asymmetry unstated. (#3515) diff --git a/.changeset/gallant-jaguars-forage.md b/.changeset/gallant-jaguars-forage.md deleted file mode 100644 index 8db047888..000000000 --- a/.changeset/gallant-jaguars-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3591 ---- -**`gsd-tools stats` no longer counts phantom phases from inline code** — prose mentioning `### Phase N:` inside an inline code span (e.g. a roadmap explaining its own numbering) inflated phases_total with a never-completing Not-Started row and deflated completion percent; stats now requires the same digit-bearing phase id shape roadmap analyze uses, so the two agree. (#3569) diff --git a/.changeset/gallant-otters-fly.md b/.changeset/gallant-otters-fly.md deleted file mode 100644 index ecc03572d..000000000 --- a/.changeset/gallant-otters-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3405 ---- -**`validate health` splits two previously-conflated warning codes into their own codes** — W021 now covers only the phase-id-convention mismatch it originally meant; the STATE-vs-ROADMAP milestone-complete mismatch it used to also report moves to the new W026. Likewise W017 now covers only orphan worktrees; the stale-worktree case moves to the new W027. diff --git a/.changeset/gallant-voles-wake.md b/.changeset/gallant-voles-wake.md deleted file mode 100644 index 2fd18ae22..000000000 --- a/.changeset/gallant-voles-wake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3488 ---- -parseDeferredItems now counts heading-delimited deferred items as ONE entry (a heading plus its descriptive sub-bullets) instead of one per bullet, across flat, container-heading, and mixed-depth files; headless one-bullet-per-item files are unchanged. A bolded `- **Status:** resolved` marker now resolves its item instead of surfacing as a bogus unresolved entry. diff --git a/.changeset/gentle-badgers-forage.md b/.changeset/gentle-badgers-forage.md deleted file mode 100644 index dad82d15e..000000000 --- a/.changeset/gentle-badgers-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3230 ---- -**`state.record-session` no longer shrinks your phase count** — a project whose ROADMAP declares more phases than it has directories on disk (phases 5 and 6 planned but not started yet) had `progress.total_phases` silently overwritten with the directory count, converging on the right number only once the last phase directory happened to exist. A flat roadmap carrying an ordinary heading like `## Progress` was being misread as milestone-sectioned. Known limit: two milestone sections carrying no version token, no status marker and not the word "Milestone" are still not detected as sectioning. (#3204) diff --git a/.changeset/gentle-foxes-glide.md b/.changeset/gentle-foxes-glide.md deleted file mode 100644 index 29ff86b4b..000000000 --- a/.changeset/gentle-foxes-glide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3290 ---- -**`validate agents` now reports Codex `.toml` model posture, not just presence** — on a `codex` install it flags any agent whose `.toml` pins a GSD tier alias or a `claude-*` id (which Codex rejects with a 400, so the agent never spawns) or carries a `model_reasoning_effort` with no `model`. Previously the check confirmed only that agent files existed, so a stale install from before the passive-model posture reported healthy right up until a typed agent failed to start. Read-only — it names the offending agent and value and never edits your files. Reports `not_codex` and reads nothing on other runtimes. (#3242) diff --git a/.changeset/gentle-moles-sprint.md b/.changeset/gentle-moles-sprint.md deleted file mode 100644 index c0d5bc917..000000000 --- a/.changeset/gentle-moles-sprint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3455 ---- -Parallel phases running in the same working tree no longer corrupt STATE.md silently: state.begin-phase, state.advance-plan and phase.complete now consult a milestone claim (.planning/milestone.lock) keyed by phase + session id, and surface a visible milestone_conflict warning (stderr plus a typed JSON field, and phase.complete's warnings[]) when another live session holds a different phase — instead of silently overwriting the single Current Position slot. diff --git a/.changeset/gentle-wasps-wave.md b/.changeset/gentle-wasps-wave.md deleted file mode 100644 index 5db2764c3..000000000 --- a/.changeset/gentle-wasps-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3296 ---- -**`effort sync` now repairs stale Codex `.toml` files without a reinstall** — on a `codex` install it strips a `model` pin that Codex rejects (a tier alias or a `claude-*` id) and an orphaned `model_reasoning_effort`, so agents fall back to the always-available session model. An explicit real-Codex pin is left alone. It is a **dry run by default** — pass `--apply` to write — and only the offending lines are removed: line endings, BOM, comments, key order, and any keys you added by hand are preserved byte-for-byte, so a repair is a two-line diff rather than a reformatted file. A file that cannot be parsed is refused and reported, never partially rewritten, and writes are atomic. Pairs with `validate agents`, which detects the same drift. The `claude` path is unchanged. (#3243) diff --git a/.changeset/gentle-wolves-hum.md b/.changeset/gentle-wolves-hum.md deleted file mode 100644 index d52fd5ca2..000000000 --- a/.changeset/gentle-wolves-hum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3562 ---- -**`/gsd-map-codebase --fast` now actually runs the fast scan** — the flag routed to "the scan workflow" in prose but named no path any runtime could resolve, and the command loaded only the full four-agent map workflow, so `scan.md` was never read and the single-agent scan was improvised rather than executed. (#3561) diff --git a/.changeset/graceful-dogs-tumble.md b/.changeset/graceful-dogs-tumble.md deleted file mode 100644 index 4a9e2468e..000000000 --- a/.changeset/graceful-dogs-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3407 ---- -**`validate consistency`'s `warnings` are now coded diagnostics** — each entry is a `{code, message, fix, repairable}` object instead of a bare string. Findings that overlap with `validate health` (a phase in ROADMAP.md with no directory on disk, or vice versa) now carry the exact same `W006`/`W007` codes `validate health` already uses for them, so there's one vocabulary for that finding, not two. The four subjects unique to this command (phase/plan numbering gaps, orphan summaries, plans missing `wave` frontmatter) get a new `C001`-`C004` code range. diff --git a/.changeset/graceful-jaguars-frolic.md b/.changeset/graceful-jaguars-frolic.md deleted file mode 100644 index fea3e9915..000000000 --- a/.changeset/graceful-jaguars-frolic.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 2559 ---- -**Digit-leading phase names now resolve consistently by bare number** — phases such as "24/7 Autonomy", "80/20 Cleanup", and "12-Factor Refactor" now resolve across every phase verb instead of appearing missing; ambiguous directory collisions now fail loudly with their candidate paths instead of silently selecting the first match. `/gsd` and `/gsd:progress` also stop under-reporting: their verify-failed check shares the same directory selection, so a failed verification in one of these phases is surfaced rather than read as a healthy phase, and phase directories carrying a project-code prefix (`MEM-05-…`) are no longer skipped by that check entirely. The same selection now backs every remaining consumer that had resolved directories on its own, so `phases list`, `phase remove`, `phase next-decimal`, the schema-drift gate, the init-manager overview, `roadmap analyze`, and the milestone-completion and health consistency checks stop reporting these phases as having no directory. `/gsd-health` no longer reports one of these phases as both missing from disk and absent from the roadmap at the same time (W006 + W007), and `phase remove` now refuses — without deleting or renumbering anything — when two directories claim the same bare phase number. `phase remove` also stops writing a phase count one too high into STATE.md when the phase it just deleted was one of these digit-leading directories (#2528). diff --git a/.changeset/graceful-lynx-hop.md b/.changeset/graceful-lynx-hop.md deleted file mode 100644 index 4b3920f45..000000000 --- a/.changeset/graceful-lynx-hop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3208 ---- -**State validation properly detects drift** — Resolved an issue where state validation would silently fail to detect drift because it skipped scanning entirely when the shipped template lacked a specific field. diff --git a/.changeset/graceful-moles-sing.md b/.changeset/graceful-moles-sing.md deleted file mode 100644 index d552adacc..000000000 --- a/.changeset/graceful-moles-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3446 ---- -Phase writes now guard the current milestone's scope. phase add/add-batch/insert reject a description containing a level 1-3 heading with a milestone marker (version token, status marker, or the word Milestone) before anything is written, and the edit-phase workflow captures roadmap milestone-scope (new read-only probe) around its in-place section write and rolls the edit back with an explicit error if the milestone window's scope or phase set changed. diff --git a/.changeset/graceful-seals-dance.md b/.changeset/graceful-seals-dance.md deleted file mode 100644 index 328bf50ce..000000000 --- a/.changeset/graceful-seals-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 2728 ---- -**`/gsd-quick` and the UAT-diagnosis step no longer abort with a FATAL on a non-Claude runtime that can actually isolate** — both dispatch sites resolved worktree isolation from a hardcoded `RUNTIME != "claude"` test, so every non-Claude host was refused regardless of what it could actually do. They now read the negotiated `dispatch.isolation` capability (#2584), and installs for runtimes that declare worktree support no longer stamp `workflow.use_worktrees` to `false`, which had pre-empted that negotiation. A runtime is judged by what it declares rather than by its name. A host that declares no isolation primitive at all still fails closed when worktrees are explicitly enabled — that FATAL is the fail-closed contract, not the bug — and a host whose isolation model the single-agent sites cannot express degrades to sequential, one agent at a time, on the main working tree. diff --git a/.changeset/graceful-seals-march.md b/.changeset/graceful-seals-march.md deleted file mode 100644 index 3cff75ea8..000000000 --- a/.changeset/graceful-seals-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3540 ---- -**`~/.gsd/defaults.json` shadowing is now diagnosed instead of silent** — in any project with a `.planning/config.json`, global model-side keys (`model_profile`, `model_overrides`, `models`, `dynamic_routing`, `runtime`, …) were silently ignored for model resolution; a file named `defaults.json` applied to no real project with no signal. GSD now prints a one-time stderr warning naming the shadowed keys. Resolution precedence is unchanged; global `effort` keeps working via effort sync and never warns. (#3532) diff --git a/.changeset/graceful-wolves-wake.md b/.changeset/graceful-wolves-wake.md deleted file mode 100644 index bea0ceb03..000000000 --- a/.changeset/graceful-wolves-wake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Removed -pr: 3422 ---- -**Removed the orphaned `verify-phase` workflow (~40 KB shipped to every runtime, never loaded)** — its still-live verification gates (decision-coverage validation, test-quality audit, infrastructure-phase human-verification scoping) moved to a reference the verifier agent actually loads, so they run again instead of shipping as dead prose; installs are ~40 KB lighter and PRs to the verifier no longer mirror a dead twin to keep lockstep tests green. (#1891) diff --git a/.changeset/happy-bears-hop.md b/.changeset/happy-bears-hop.md deleted file mode 100644 index d6395c672..000000000 --- a/.changeset/happy-bears-hop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3407 ---- -**`state validate`'s `warnings` are now coded diagnostics, and the `drift` field is gone** — each entry is a `{code, severity, message, remedy}` object (seven codes, `S001`-`S007`) naming exactly what STATE.md disagrees with the filesystem about and how to fix it, instead of a bare string. The separate `drift` object every response used to carry is removed entirely; every condition it used to report (a conflicting phase reference, a missing phases directory, a plan-count mismatch, a stale executing status) is now one of the seven coded warnings, so no information is lost, it's just structured. `valid` and `scope` are unchanged. diff --git a/.changeset/happy-birds-roar.md b/.changeset/happy-birds-roar.md deleted file mode 100644 index 609180c2d..000000000 --- a/.changeset/happy-birds-roar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3436 ---- -Reviewer lanes that declare source-grounded evidence are now verified at run time: a review citing zero file:line source evidence is stamped [reviewed-without-source-citations] and down-weighted in the Consensus Summary, instead of silently riding its declared evidence class at full weight (gemini plan-only reviews were measured doing exactly this). diff --git a/.changeset/happy-ibex-romp.md b/.changeset/happy-ibex-romp.md deleted file mode 100644 index fd3aacaa8..000000000 --- a/.changeset/happy-ibex-romp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3327 ---- -**/gsd-progress and /gsd-execute-plan stop counting superseded plans as outstanding work** — seven prompt-layer sites across execute-plan.md, plan-phase.md, plan-review-convergence.md and progress.md counted plans with a raw `ls *-PLAN.md | wc -l`, so a plan marked `status: superseded` was still counted as outstanding, a phase on the nested plans/ layout (#3139) reported zero plans it actually had, and loosely-named plan files were missed entirely. Every site now calls `phase find`, which gains three additive fields — `plan_count`/`summary_count` (live, superseded excluded — 'how much is left') and `plan_count_all` (physical, every plan on disk — 'what did the planner write') — so what a workflow shows and what `phase find` reports for the same phase are now the same number. This also fixes a dead route: progress.md's Route 0 resume-incomplete-phase check read `.plans`/`.summaries` arrays that its producer, roadmap.analyze, never emitted (it emits plan_count/summary_count scalars), so both counts were always 0 and the check had never fired at all — it now fires correctly. **This is a behavior change you'll notice:** plan/summary counts shown by these workflows will move — toward being correct. (#3218) diff --git a/.changeset/happy-jaguars-roar.md b/.changeset/happy-jaguars-roar.md deleted file mode 100644 index 0efd897d8..000000000 --- a/.changeset/happy-jaguars-roar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3405 ---- -**`validate health --repair` no longer resets config.json or regenerates STATE.md automatically** — these two repairs are destructive (they lose custom settings or session history), so they're now reported with their fix described but never auto-applied; run the suggested command yourself to apply them. diff --git a/.changeset/happy-quails-parade.md b/.changeset/happy-quails-parade.md deleted file mode 100644 index 18ae84ae2..000000000 --- a/.changeset/happy-quails-parade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3476 ---- -Managed /gsd:debug auto-resume no longer stalls after an answered checkpoint: the respawned session manager now receives the recorded next action and checkpoint status, plus the disposition that prior checkpoints were already answered, so the debug loop proceeds on the persisted next step instead of stopping behind the no-progress guard. diff --git a/.changeset/happy-ravens-run.md b/.changeset/happy-ravens-run.md deleted file mode 100644 index f89c4d3c1..000000000 --- a/.changeset/happy-ravens-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3570 ---- -**Concurrent Claude Code sessions no longer share one active-workstream pointer** — Claude Code exports its session id as `CLAUDE_CODE_SESSION_ID`, but the session-identity probe only listened for `CLAUDE_SESSION_ID`, so session-scoped workstream isolation never engaged on Claude Code: every session in a working tree resolved through the single shared `.planning/active-workstream` pointer, and a `STATE.md` update belonging to one workstream could be written silently into another's directory. The probe now accepts `CLAUDE_CODE_SESSION_ID` (no other key's precedence changed); concurrent sessions each keep their own session-scoped pointer again. (#3557) diff --git a/.changeset/happy-voles-chatter.md b/.changeset/happy-voles-chatter.md deleted file mode 100644 index d91bbbf19..000000000 --- a/.changeset/happy-voles-chatter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3453 ---- -plan-phase: a completed --gaps planning run's Next Up handoff now recommends /gsd:execute-phase --gaps-only (matching the gap-closure scope just planned) instead of the whole-phase /gsd:execute-phase . Standard and --reviews runs are unchanged. diff --git a/.changeset/humble-cats-sprint.md b/.changeset/humble-cats-sprint.md deleted file mode 100644 index c8ebb2171..000000000 --- a/.changeset/humble-cats-sprint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3590 ---- -**GSD no longer commits `.planning/` files you told it to ignore** — several workflow steps staged planning artifacts with raw `git add`, bypassing the `commit_docs` setting and the `.gitignore` auto-detect entirely, so planning docs reached shared history anyway. (#3585) diff --git a/.changeset/humble-koalas-snooze.md b/.changeset/humble-koalas-snooze.md deleted file mode 100644 index 7d31d5b47..000000000 --- a/.changeset/humble-koalas-snooze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3649 ---- -**`/gsd-review` now records which model each reviewer actually used** — REVIEWS.md frontmatter gains `models:` and `model_sources:`, so an unpinned lane's verdict is no longer attributable to an unknown model. (#2295) diff --git a/.changeset/humble-lemurs-roar.md b/.changeset/humble-lemurs-roar.md deleted file mode 100644 index bc3677ebd..000000000 --- a/.changeset/humble-lemurs-roar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3393 ---- -**Global Claude installs load skill content correctly again** — the installer rewrote `@~/.claude/` file references to `@$HOME/.claude/`, which Claude Code does not expand, silently leaving every GSD skill with an empty execution_context (the model got scaffolding but never the workflow body). @-references now stay on the tilde form Claude resolves. (#3133) diff --git a/.changeset/jolly-geese-wake.md b/.changeset/jolly-geese-wake.md deleted file mode 100644 index dcc58a6cc..000000000 --- a/.changeset/jolly-geese-wake.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Changed -pr: 3443 ---- -**Gap-closure planning no longer documents a completion marker nothing reads** — the planner emitted `## GAP CLOSURE PLANS CREATED` but no workflow had a dispatch branch for it, so completion was always detected via the `gap_closure: true` fix-plan artifacts anyway; the dead marker is retired and the artifact route (verify-work `--gaps` spawn → plans → `execute-phase --gaps-only`) is now the documented contract. (#3440) - diff --git a/.changeset/jolly-jaguars-caper.md b/.changeset/jolly-jaguars-caper.md deleted file mode 100644 index d6353e835..000000000 --- a/.changeset/jolly-jaguars-caper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3222 ---- -**Progress, stats, and phase listings now stay within the current milestone.** `progress`, `stats`, and `phases list` no longer count backlog (`999.*`) or pre-milestone (`0-*`) directories as current-milestone phases, and `phases clear` / `milestone complete` no longer delete or archive those directories. `phases list --phase` and `--include-archived` are unaffected, since they intentionally look up or list beyond the current milestone. (#3185) diff --git a/.changeset/jolly-seals-purr.md b/.changeset/jolly-seals-purr.md deleted file mode 100644 index cc374f59a..000000000 --- a/.changeset/jolly-seals-purr.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3285 ---- -**Twenty-five folded test suites no longer run twice on every CI lane** — three consolidated install suites each carried a verbatim second copy of a contiguous run of folded regression blocks (~5,800 lines), left behind by a stale-base re-application during the test-consolidation epic. Every duplicated block registered and passed twice, so nothing reported it, and a contributor fixing one of those regressions could edit one copy and leave the other asserting the old behavior with the suite still green. The duplicates are deleted, and a new `local/no-duplicate-fold-marker` ESLint rule fails the build if a folded suite ever appears twice in one host file again. (#3271) diff --git a/.changeset/jolly-voles-rally.md b/.changeset/jolly-voles-rally.md deleted file mode 100644 index 30f62f50b..000000000 --- a/.changeset/jolly-voles-rally.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3555 ---- -**Items left unresolved when a milestone closes are no longer invisible to every later audit** — `query audit-open`'s four phase-scoped scanners read only `.planning/phases/`, so once a milestone closed and its phase directories moved to `.planning/milestones/vX.Y-phases/`, any UAT gap, verification gap, context question or deferred item still open at that moment vanished from the pre-close audit permanently. In a fully-archived project the scanners returned nothing at all, which is indistinguishable from a clean tree — and because the audit sums every category into one `has_open_items` boolean, that could report a clean close it had not verified. All four now scan the archived milestone directories as well, and each item says which milestone it came from. (#3458) diff --git a/.changeset/kind-deer-caper.md b/.changeset/kind-deer-caper.md deleted file mode 100644 index 9dd568925..000000000 --- a/.changeset/kind-deer-caper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3599 ---- -**`roadmap` tools recognize table-style phase listings** — a ROADMAP whose current-milestone phases are declared as markdown table rows (`| 20 | … |`) reported phase_count: 0 and found: false across roadmap.analyze, roadmap.get-phase, init.phase-op, and the milestone filter; all four surfaces now resolve table-declared phases (progress tables and fenced examples excluded). (#3577) diff --git a/.changeset/kind-jaguars-romp.md b/.changeset/kind-jaguars-romp.md deleted file mode 100644 index ba88a2043..000000000 --- a/.changeset/kind-jaguars-romp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3616 ---- -**A terminal session now follows the workstream your repo says is active** — with `.planning/active-workstream` naming a workstream, any invocation that had never run `workstream use` silently resolved the flat `.planning/` tree instead: it misreported milestone, phase and progress on reads, and wrote to the superseded flat `STATE.md`. Because the stale tree is well-formed, nothing warned, and the documented workaround was to prepend `GSD_WORKSTREAM=` or `--ws` to every command. A session that has never set its own pointer now inherits the repo marker. Session isolation is unchanged — a session that owns a pointer is never repointed — and the two workstream-mode fail-safe guards now say whether a marker exists but failed to resolve, instead of claiming none is set. Note that clearing a session's pointer returns it to inheriting the marker rather than forcing flat mode. (#3579) diff --git a/.changeset/lazy-otter-purchase.md b/.changeset/lazy-otter-purchase.md deleted file mode 100644 index fc0603c3c..000000000 --- a/.changeset/lazy-otter-purchase.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3363 ---- -**`branching_strategy: "phase"`/`"milestone"` once again lands the first strategy-scoped commit on the strategy branch** — `gsd-tools query commit` now creates *and* switches to a brand-new phase/milestone branch (restoring the #1278 intent), instead of creating it without switching and leaving the commit on the base branch. The #3079 protection is preserved: an *already-existing* strategy branch is still never silently switched to (it warns and commits on the current branch). The first fresh create is now logged to stderr instead of being silent, and the misleading "already exists" warning no longer recurs on every subsequent commit once HEAD is on the strategy branch. (#3207) diff --git a/.changeset/lively-bears-click.md b/.changeset/lively-bears-click.md deleted file mode 100644 index 8764c5f32..000000000 --- a/.changeset/lively-bears-click.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3535 ---- -**A file belonging to another phase no longer blocks the phase you are in** — sixteen scans (plus the single-pick fallback inside `resolveVerificationFile`) collected verification and UAT artifacts from a phase directory without checking they belonged to that phase, so a stray or copied file such as `04-VERIFICATION.md` sitting in phase 03's directory contributed its status to phase 03. The worst case was not cosmetic: a stray file carrying `gaps_found` or `human_needed` pushed a blocker that flipped the UAT-passed predicate to false, and `transition` gates on that — so a leftover file could refuse to let a phase advance. Some scans could also claim the opposite, reporting verification passed on the strength of a file the phase does not own. All of them now check phase membership. Where a directory's own phase cannot be determined from its name, every file is still included, so no scan silently loses a phase's real blockers; where it can, a phase holding only another phase's report now correctly reports having none of its own rather than adopting it. (#3511) diff --git a/.changeset/lively-birds-frolic.md b/.changeset/lively-birds-frolic.md deleted file mode 100644 index f1e60d3a1..000000000 --- a/.changeset/lively-birds-frolic.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3276 ---- -**Codex agents now inherit the session model instead of getting a pinned per-tier model** — if you install for `codex` with a `runtime` set and any `model_profile` other than `inherit`, GSD no longer writes a `model` (or `model_reasoning_effort`) line into `~/.codex/agents/.toml`. This fixes typed agents failing to spawn with `400 invalid_request_error: "The 'sonnet' model is not supported when using Codex with a ChatGPT account"`, which degraded the whole plan/execute flow to a generic-agent fallback. **To keep pinning a model, set an explicit real-Codex id in `model_overrides`** (e.g. `{"model_overrides": {"gsd-planner": "gpt-5.6-sol"}}`) — that path is unchanged. The installer prints a one-time notice when it drops a pin. Codex-only; all other runtimes are untouched. (#3241) diff --git a/.changeset/lively-orcas-climb.md b/.changeset/lively-orcas-climb.md deleted file mode 100644 index a8180a580..000000000 --- a/.changeset/lively-orcas-climb.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3499 ---- -**The build no longer requires Node 24: `escapeRegex` falls back to an in-file metachar escape when `RegExp.escape` is absent** (#3498) — `RegExp.escape` is ES2026 (Node 24+), and `src/pattern.cts` called it unconditionally, so `npm run build` itself failed on Node 22 (`gen-loop-host-contract` consumes the module), breaking the gsd-test `linux-node22` verification lane. The seam now prefers the built-in when present and falls back otherwise — still the single owner of escaping (#3212 invariant preserved). Behavior on Node 24+ is unchanged; match behavior below Node 24 is verified equivalent by regression tests that neuter `RegExp.escape` in a child process. diff --git a/.changeset/loud-jade-canyon.md b/.changeset/loud-jade-canyon.md deleted file mode 100644 index 0919d9c39..000000000 --- a/.changeset/loud-jade-canyon.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3387 ---- -**Full-line `#` comments in `.planning/STATE.md` (and every frontmatter surface) now survive a mutating write** — `parseYamlRegion` carries column-0 comments through to `reconstructFrontmatter` via a Symbol-keyed channel, and `syncStateFrontmatter` propagates that channel across its fresh-rebuild of the frontmatter object, so a comment like `# NOTE: current_phase is hand-maintained` is no longer silently destroyed on the next `state` verb. Comment-less frontmatter is unchanged; data identity (keys/values/arrays/nested) is preserved alongside the comments. (#3257) diff --git a/.changeset/lucky-bears-forage.md b/.changeset/lucky-bears-forage.md deleted file mode 100644 index ec9b2d12f..000000000 --- a/.changeset/lucky-bears-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3459 ---- -A plan SUMMARY whose frontmatter declares status: blocked is no longer counted as a completed plan. Previously both the progress counters written to STATE.md (state planned-phase / begin-phase / record-session) and the phase-plan-index read path paired PLAN and SUMMARY files by filename existence alone, so a blocked plan counted as done and was omitted from the incomplete list. Filename existence remains the fallback when a SUMMARY carries no status field, and status: halted summaries still count as completion records (a designed stop), so untouched projects are unaffected. diff --git a/.changeset/lucky-ravens-leap.md b/.changeset/lucky-ravens-leap.md deleted file mode 100644 index b3075e8d5..000000000 --- a/.changeset/lucky-ravens-leap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3419 ---- -**Auto-chain phase completion now runs the same post-processing as a normal transition** (#1526) — completing a phase via `/gsd:execute-phase` (auto-chain) previously skipped the transition workflow's graduation scan, session-continuity, project-reference, accumulated-context, and current-position updates, leaving project state different from a normal transition. execute-phase now delegates post-completion processing to the transition workflow (post-completion mode: skips re-verify + re-running `phase.complete` to avoid a double-write). Identity/standalone transition behavior is unchanged. diff --git a/.changeset/merry-badgers-tumble.md b/.changeset/merry-badgers-tumble.md deleted file mode 100644 index ec87944c6..000000000 --- a/.changeset/merry-badgers-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3250 ---- -**`gsd-verifier` now says *why* a verified truth holds, not just that it does** — a truth that reaches `✓ VERIFIED` is additionally classified against three incidental-reliance patterns (an undeclared precondition, an ordering or side effect nothing enforces, a truth that is only true under the test fixture) and, when one matches, is reported as `✓ VERIFIED (coincidental-reliance)` with an entry in the new `coincidental_reliance_items` frontmatter list naming what to harden. Purely advisory: the base `✓ VERIFIED` token is unchanged, the truth still counts toward the score, the overall `status` is unaffected, and no human-verification item is emitted — a passing phase still passes. Only a consumer matching the truth-row verdict cell for exact equality (rather than as a substring) needs to tolerate the suffix. Two limits stated up front: the check is endogenous, and so measurably weaker than the exogenous `backstop` tag `gsd-core/references/honest-verifier.md` routes on — advisory status is the consequence, and its precision is unmeasured; and `gsd-core/workflows/verify-phase.md` is not edited, receiving the rule through its eager import of the verification-report template rather than a second inline copy, because it sits 29 bytes under its size hard cap. (#1955) diff --git a/.changeset/merry-cats-jump.md b/.changeset/merry-cats-jump.md deleted file mode 100644 index 7d22ff955..000000000 --- a/.changeset/merry-cats-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3278 ---- -**Install scope is now resolved once, as a value** — the installer and the modules downstream of it no longer each re-derive whether an install is global or local from a bare string. One module owns the scope axis and reports its config home, its per-scope settings file, and whether it requires a consent record. No behavior changes for any install. (#2870) diff --git a/.changeset/merry-voles-swim.md b/.changeset/merry-voles-swim.md deleted file mode 100644 index 893376650..000000000 --- a/.changeset/merry-voles-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3548 ---- -**The read-injection scanner now reports which rules fired as structured data** — its PostToolUse output carries a `findings` array of `{ruleId, match}` records alongside the human-readable advisory, so consumers no longer have to parse the advisory sentence to learn what was detected (the advisory text itself is unchanged). (#3523) diff --git a/.changeset/merry-wolves-climb.md b/.changeset/merry-wolves-climb.md deleted file mode 100644 index 4455a3e19..000000000 --- a/.changeset/merry-wolves-climb.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3249 ---- -**Capability skill bodies are now documented as an instruction surface** — the capability trust model previously grouped skills with inert assets as "non-executable" surfaces whose consent is lighter *because they do not execute code*. A skill body does not execute code; it instructs the agent that does. The docs now state that a capability's SKILL.md bodies reach your agent's instruction context verbatim and are not content-scanned, and capability authors are told the same on the authoring side. No behavior changed and no existing consent was invalidated. (#3247) diff --git a/.changeset/nimble-agents-route.md b/.changeset/nimble-agents-route.md deleted file mode 100644 index b41a31eb9..000000000 --- a/.changeset/nimble-agents-route.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3417 ---- -**Plans can now opt into a specialist executor via a per-plan `agent_hint:` frontmatter field** — `execute-phase` dispatches the named subagent instead of `gsd-executor` when it resolves on the active runtime, and falls back to `gsd-executor` when the field is absent, blank, or the named agent does not resolve (byte-identical to today). Resolution consults the active runtime's agent directory (project-local and user-global, across filename variants) via a new `gsd-tools resolve-agent` query, and the hint flows through `phase-plan-index` as `plan_json.agent_hint`. Default-on via `workflow.agent_hint_routing` (set `false` to disable); covers the `Agent()`-based dispatch (harness-worktree and sequential). (#1689) diff --git a/.changeset/nimble-jaguars-tumble.md b/.changeset/nimble-jaguars-tumble.md deleted file mode 100644 index e89c6e9a1..000000000 --- a/.changeset/nimble-jaguars-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 3510 ---- -**Hook security hardening — shared injection patterns + fail-closed force-add guard** — the prompt-injection pattern list is now one shared module used by both the write-guard and the read-scanner hooks, so the two surfaces can no longer silently drift apart; and the opt-in workflow guard's force-add block on agent branches now fails closed on internal error instead of silently allowing. (#3504) diff --git a/.changeset/nimble-orcas-roam.md b/.changeset/nimble-orcas-roam.md deleted file mode 100644 index bf2211edc..000000000 --- a/.changeset/nimble-orcas-roam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3272 ---- -**`runtime.hostBehaviors` is now a closed vocabulary** — the capability-manifest field that carries per-host install and adaptation switches was validated by nothing, so a typo'd or invented key was silently ignored forever. Its 59 keys are now enumerated, and a key outside the vocabulary is ignored with a non-fatal warning naming the capability and the key. It is never a validation error: a manifest authored against a newer GSD degrades visibly rather than failing the build, and an out-of-tree runtime descriptor carrying a bespoke key keeps installing. No shipped capability is affected. (#2801) diff --git a/.changeset/nimble-orcas-tumble.md b/.changeset/nimble-orcas-tumble.md deleted file mode 100644 index 11f9996c6..000000000 --- a/.changeset/nimble-orcas-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 3516 ---- -**Capability installs no longer fetch from internal hosts, and unpinned installs say so in the consent prompt** — the URL importer refuses loopback/link-local/metadata hosts (including the cloud metadata addresses and localhost) before any bytes leave, and an http:// tarball URL fails with a clear https-only reason instead of a raw protocol error. Installs without an integrity pin now show a distinct 'NO PINNED HASH — staged unverified' line in the consent disclosure. (#3514) diff --git a/.changeset/nimble-ravens-hop.md b/.changeset/nimble-ravens-hop.md deleted file mode 100644 index bf8394d76..000000000 --- a/.changeset/nimble-ravens-hop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3608 ---- -**A capability's `ship:pre` gate now actually blocks the ship** — the ship preflight resolved every declared `ship:pre` gate but enforced only the built-in `security` and `broken-windows` capabilities, so any other capability's `blocking: true` gate was resolved, evaluable, and then silently dropped: a phase shipped past its own declared failing condition with nothing evaluated, nothing warned, and nothing logged. Preflight now dispatches every active gate generically — honoring each gate's own `blocking` and `onError` — matching the contract `execute:wave:post`, `execute:post` and `plan:post` already implement. (#3559) diff --git a/.changeset/nimble-wasps-jump.md b/.changeset/nimble-wasps-jump.md deleted file mode 100644 index c425b04fe..000000000 --- a/.changeset/nimble-wasps-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3489 ---- -package-legitimacy-gate tests now locate the executor RULE 3 section by its heading inside the deviation rules block, so unrelated prompt edits can no longer redirect or silently defeat the package-install guardrail assertions diff --git a/.changeset/noble-wasps-munch.md b/.changeset/noble-wasps-munch.md deleted file mode 100644 index 23573ca68..000000000 --- a/.changeset/noble-wasps-munch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3291 ---- -resolveTriggerSurface (Runtime Artifact Layout Module) resolves the /gsd- trigger surface — winner, shadowedBy, and nested-router registration — per runtime/scope, and a new runtime.triggerPrecedence descriptor axis (required-with-default) decides same-trigger collisions; agents and kimi-agents are never trigger-bearing. diff --git a/.changeset/patient-dogs-sprint.md b/.changeset/patient-dogs-sprint.md deleted file mode 100644 index 00690c723..000000000 --- a/.changeset/patient-dogs-sprint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3325 ---- -**Known-defect warnings that lived only in docs are now enforced checks** — six failure modes that `CONTEXT.md` merely described are now caught automatically, including unbounded subprocesses that could hang a run indefinitely and an unscoped frontmatter read that could pick up a body line. Writing the checks surfaced nine live instances, all fixed. (#2896) diff --git a/.changeset/patient-foxes-click.md b/.changeset/patient-foxes-click.md deleted file mode 100644 index bb37d775f..000000000 --- a/.changeset/patient-foxes-click.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3266 ---- -**The ADR gate now resolves documentation links and checks H1 status brackets** — a link in `docs/adr/` that pointed nowhere, and an H1 whose trailing `[Status]` bracket contradicted its own `Status:` field, both passed CI green; readers and agents following those citations hit dead ends the build had already blessed. `gen-adr-index.cjs --check` now fails on either, naming the file, the line, and the unresolved target. Links inside fenced or inline code are left alone, and resolution is case-exact on every platform. A new `--json` flag reports the same findings as a structured document with stable `reason` codes, so tooling never has to pattern-match an error message. (#2704) diff --git a/.changeset/patient-lynx-march.md b/.changeset/patient-lynx-march.md deleted file mode 100644 index 71a390204..000000000 --- a/.changeset/patient-lynx-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 3506 ---- -**Path validation no longer accepts a symbolic link whose target is missing** — `validatePath` canonicalizes a path with `realpath`, and for a path that does not exist yet it fell back to canonicalizing the parent directory instead. A symlink inside the project pointing at a **non-existent** location outside it took that fallback and was accepted, while a symlink pointing at an **existing** outside location was correctly refused — a difference an attacker could use to test whether arbitrary absolute paths exist. Such a link is now refused outright. The same fallback also compared an uncanonicalized path against a canonicalized base when several leading directories were missing, wrongly refusing legitimate not-yet-created paths on any non-canonical working directory (every macOS temp directory, for one); it now canonicalizes from the nearest existing ancestor. (#3493) diff --git a/.changeset/patient-yaks-click.md b/.changeset/patient-yaks-click.md deleted file mode 100644 index 5f111c6bc..000000000 --- a/.changeset/patient-yaks-click.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3430 ---- -The `init phase-op`, `init plan-phase` and `init execute-phase` queries no longer hand consumers a fully-formed absolute path for a `REQUIREMENTS.md`/`STATE.md`/`ROADMAP.md` that does not exist. Those three fields were built with a bare path.join and no existence check, so a non-null value was indistinguishable from the file actually being there — even as the conditional sibling fields in the same payload (`patterns_path`, `context_path`, ...) already returned null for absent files, and `ultraplan-phase.md` explicitly gates its REQUIREMENTS.md read on `requirements_path is not null`. Each of the three reading sites now returns null when the file is absent and its absolute path when present. The project/milestone-bootstrap and doc-ingest emitters that use these paths as write-targets for not-yet-created files are intentionally unchanged. (#3188) diff --git a/.changeset/plucky-tigers-roar.md b/.changeset/plucky-tigers-roar.md deleted file mode 100644 index 4dc394885..000000000 --- a/.changeset/plucky-tigers-roar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 3496 ---- -**`verify key-links` can no longer be hung by a plan's `key_links` pattern** — the pattern was compiled straight from plan frontmatter with a backtracking engine and tested against whole file contents, so a nested-quantifier pattern such as `(a+)+$` pinned a CPU core indefinitely and stalled any `verify-phase` run that reached it. Untrusted patterns now execute on the RE2 engine, whose match time is linear in the input length, and a pattern that cannot be compiled is refused outright rather than guessed at — a refused pattern can never report a match. (#3477) diff --git a/.changeset/plucky-wolves-leap.md b/.changeset/plucky-wolves-leap.md deleted file mode 100644 index 3f41ee4bb..000000000 --- a/.changeset/plucky-wolves-leap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3491 ---- -phase.complete no longer rewrites STATE.md frontmatter stopped_at with a stale body 'Stopped at:' line: the completion now refreshes the session continuity line it implies ('Phase N complete, ready to plan Phase N+1') and applies the standard field-preservation policy on its atomic commit path. state record-session no longer reports 'Stopped At' as updated when the value is already current. diff --git a/.changeset/proud-birds-travel.md b/.changeset/proud-birds-travel.md deleted file mode 100644 index 61e009949..000000000 --- a/.changeset/proud-birds-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3261 ---- -**Complexity-triggered refactor proposals** — after a phase runs, GSD can now measure the complexity of the code that phase touched and surface a scoped refactor proposal when a function crosses a threshold or drifts past its recorded anchor, so entropy gets caught while it is still one function instead of a rewrite. Advisory and off by default; enable with `gsd config-set refactor.trigger_enabled true`. (#1953) diff --git a/.changeset/proud-wasps-forage.md b/.changeset/proud-wasps-forage.md deleted file mode 100644 index 3f1c0d039..000000000 --- a/.changeset/proud-wasps-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3571 ---- -**`check:contract-drift` — a machine-enforced agent-contract registry** — sentinel markers, read-tag gates, and deleted-file test references can no longer drift silently: the Agent Registry table in `gsd-core/references/agent-contracts.md` is now linted against what agents emit and what workflows consume, and `lint-removed-but-needed` catches tests that pin files your PR deleted. (#3565) diff --git a/.changeset/quick-finches-snooze.md b/.changeset/quick-finches-snooze.md deleted file mode 100644 index f573a0ccd..000000000 --- a/.changeset/quick-finches-snooze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3445 ---- -Cross-AI reviewer lanes now run on Windows: the declared bare CLI name is resolved through one shared PATH+PATHEXT lookup before spawning, so npm-installed .cmd/.bat shims start via cmd.exe mediation instead of failing with spawn ENOENT (#3275). diff --git a/.changeset/quick-lynx-wander.md b/.changeset/quick-lynx-wander.md deleted file mode 100644 index d2e7e2191..000000000 --- a/.changeset/quick-lynx-wander.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3447 ---- -Six STATE.md frontmatter fields whose preservation policy is declared in the field-classification table were not honored by the table-driven preservation pass — `last_activity_desc`, `paused_at`, `current_phase`, `current_plan` (preserve-when-unchanged) and `milestone`, `milestone_name` (preserve-if-placeholder). The pass now implements every declared row, so editing a preservation row is a one-row table edit as the table's own contract documents. Curated frontmatter values for `paused_at` / `current_phase` / `current_plan` now survive a body-only write (e.g. `state update`) even when the body carries a stale-but-present derived value — previously only an absent derived value triggered the fallback, so a stale body value silently overwrote the curated frontmatter value. `last_activity_desc` is now governed by a single rule (the table row) rather than a separate date-comparison guard that could disagree with it. (#3258) diff --git a/.changeset/quick-moles-caper.md b/.changeset/quick-moles-caper.md deleted file mode 100644 index a300a220a..000000000 --- a/.changeset/quick-moles-caper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3595 ---- -**`state` writes no longer shrink progress.total_phases to the started-phase count when ROADMAP.md is absent** — with no readable roadmap, every state command persisted the on-disk phase-directory count as the declared total (only phases that had started counted, so a 5-phase project read 50-100% complete with 3-4 phases unstarted); the stored frontmatter total now wins, with a warning, and `state json` reports the same preserved value. (#3573) diff --git a/.changeset/quick-moles-purr.md b/.changeset/quick-moles-purr.md deleted file mode 100644 index fe7528b6b..000000000 --- a/.changeset/quick-moles-purr.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 2493 ---- -**The `claude` reviewer in `/gsd:review` no longer inherits your CLAUDE.md or auto-memory** — the lane now declares `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` (CLAUDE.md loading and auto-memory are independently-toggled mechanisms, so each gets its own variable), merged into that one spawn's environment, so it reviews the same self-contained prompt the gemini and codex reviewers already receive. It was previously the only reviewer additionally seeing your global CLAUDE.md, the project CLAUDE.md, and Claude Code auto-memory — a context asymmetry against the workflow's own independent-review premise, and a measured ~4k extra input tokens per spawn. Carried as declared lane data (`invoke.env`, ADR-2782), not a bespoke handler; nothing reaches the orchestrating session or any other lane in the run. Affects `/gsd:review` (and the convergence flow that reuses it) invoked from a non-Claude-Code runtime; inside Claude Code the claude reviewer already self-skips for independence. (#2483) diff --git a/.changeset/quick-pumas-climb.md b/.changeset/quick-pumas-climb.md deleted file mode 100644 index ffbabe79d..000000000 --- a/.changeset/quick-pumas-climb.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3451 ---- -ui-plan-gate no longer blocks planning on a UI-token match alone: the gate now requires static frontend evidence (a package.json UI-framework dependency or a component-framework file in the tree) before blocking, so a phase section naming a hyphenated repo like dashboard-financeiro no longer trips the gate in a repo with no frontend. The gate result also surfaces matchedToken/matchedLine so operators can see what triggered the flag. diff --git a/.changeset/quick-quails-march.md b/.changeset/quick-quails-march.md deleted file mode 100644 index 87768979b..000000000 --- a/.changeset/quick-quails-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3432 ---- -**Agent required-reading enforcement now actually fires** — spawner workflows and commands emitted `` while agents gate on ``, so the "you MUST Read every listed file" clause never triggered; the canonical tag is now `` everywhere (46 spawn blocks across 24 workflows), with a repo guard banning the legacy tag so the two vocabularies can never drift apart again. (#3423) diff --git a/.changeset/quick-rams-greet.md b/.changeset/quick-rams-greet.md deleted file mode 100644 index 5bad0882c..000000000 --- a/.changeset/quick-rams-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3460 ---- -Windows/Claude Code: /gsd-update and re-running the installer now migrate stale `.sh` managed hook commands (gsd-validate-commit, gsd-graphify-update, gsd-session-state, gsd-phase-boundary) in settings.json/settings.local.json to the current bash-runner-omission format — removing the redundant nested bash that the pre-#580/#3393 shape spawns on every hook fire. Custom user hooks are never touched. diff --git a/.changeset/quiet-marble-field.md b/.changeset/quiet-marble-field.md deleted file mode 100644 index 76d98b5af..000000000 --- a/.changeset/quiet-marble-field.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3375 ---- -**`gsd-tools query state update-progress` no longer rewrites Progress to 0% after a milestone close** — when the current-milestone phase scan finds zero plans (the post-archive state, where `.planning/phases/` is empty), the command is now a no-op that leaves STATE.md unchanged, instead of mapping 0/0 to 0% and destroying the shipped `[██████████] 100%` record. The legitimate 0% case (plans exist, none summarized) still writes 0%. (#3233) diff --git a/.changeset/quiet-moons-derive.md b/.changeset/quiet-moons-derive.md deleted file mode 100644 index b0cae781c..000000000 --- a/.changeset/quiet-moons-derive.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: Added -pr: 2677 ---- -**`runtime-homes` now exports its non-registry config-home descriptors** — `KIMI_HOOKS_TOML_DESCRIPTOR`, `NON_REGISTRY_CONFIG_HOME_DESCRIPTORS`, `GSD_LOCATION_ENV_KEYS`, and the `ConfigHomeDescriptor` type are public, so consumers that need the *set* of config-location env vars (rather than a single resolved path) can derive it instead of hand-maintaining a copy. `resolveKimiHooksTomlDir()` behaviour is unchanged; its descriptor is simply named rather than inline (#3156). - -**The test-instrumentation scripts no longer ship in the npm package** — `scripts/run-tests.cjs`, `scripts/live-config-guard.cjs`, `scripts/affected-tests-lib.cjs`, and `scripts/run-affected-tests.cjs` are now excluded from the tarball (they are one closed require chain of repo-only test tooling). `npm test` in an installed package was already inoperable (`tests/` has never shipped); a deep import of `scripts/run-tests.cjs` from the published package — an unsupported surface — will now be `MODULE_NOT_FOUND` (#3156). diff --git a/.changeset/rapid-foxes-forage.md b/.changeset/rapid-foxes-forage.md deleted file mode 100644 index bf31b23ce..000000000 --- a/.changeset/rapid-foxes-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3495 ---- -**STATE.md preservation now enforces every policy its own table declares** — a field could be declared `preserve-when-unchanged` and quietly go unenforced, because the executor branched on field names rather than on the declared policy, so four of eight rows were honored by a weaker mechanism elsewhere and two policies had no implementation at all. Preservation is now dispatched from the classification table, a declared row nothing enforces fails loudly instead of silently, and a whitespace-only curated value is no longer treated as a real one. (#3468) diff --git a/.changeset/rapid-foxes-glide.md b/.changeset/rapid-foxes-glide.md deleted file mode 100644 index f28e0a5cd..000000000 --- a/.changeset/rapid-foxes-glide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3615 ---- -**The installer module no longer re-exports internals it does not own.** `bin/install.js` exported 197 names, 70 of which were either dead or plain pass-throughs to the modules that actually implement them — kept for "existing consumers" that turned out not to exist, since no production code has ever required the file. Those 70 are gone and their tests now import the owning modules directly. No installed output changes. (#2876) diff --git a/.changeset/rapid-orcas-tumble.md b/.changeset/rapid-orcas-tumble.md deleted file mode 100644 index 7e6ade412..000000000 --- a/.changeset/rapid-orcas-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3601 ---- -**Per-phase `commit_docs` override** — set `phase_commit_docs.` to commit one phase's `.planning/` artifacts (e.g. an architecture phase) while keeping other phases local, without flipping the project-wide `commit_docs` switch. (#3587) diff --git a/.changeset/rapid-tunas-dance.md b/.changeset/rapid-tunas-dance.md deleted file mode 100644 index fa3fe422b..000000000 --- a/.changeset/rapid-tunas-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3555 ---- -**`audit-open acknowledge` now suppresses open audit items at future milestone closes** — deferring an item via /gsd-complete-milestone previously only wrote a human-readable note; the item resurfaced at every later close with no way to silence it short of resolving it for real. The new `audit-open acknowledge --category --milestone [--at ] ...` CLI verb writes a verdict-preserving `audit_acknowledged` marker that suppresses the item starting at the next audit scan, without ever touching the artifact's own `status:` field, and self-invalidates the moment the artifact's observed state changes again. `query audit-open --json` now also reports an `acknowledged` count per category alongside `counts`, so a clean close can be told apart from one that is clean only because prior items are still suppressed. (#3458) diff --git a/.changeset/reviewer-lane-env-disclosure.md b/.changeset/reviewer-lane-env-disclosure.md deleted file mode 100644 index aca2b9b54..000000000 --- a/.changeset/reviewer-lane-env-disclosure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 2493 ---- -**A reviewer lane's `invoke` fields are now disclosed at install and bound to the consent signature** — an installed third-party capability could declare `env` on its `reviewer` lane and have those variables applied to the spawned reviewer process without that ever appearing in the consent prompt, which makes `NODE_OPTIONS=--require ./evil.js` an undisclosed code-execution path. Overlay reviewer lanes only became executable in #3062, and the disclosure did not move with them. The consent prompt now shows each `env` key and value (highlighting names that are execution primitives) and the manifest's own `defaultHost`, which the runtime uses whenever the configured host key resolves to nothing — previously such a lane displayed "(unresolved)" while still sending plan and review text to the address the manifest chose. Every other declared `invoke` field is covered by a residual, so a future field cannot repeat this. No already-installed capability is re-prompted by this change — consent is bound to the bundle's content hash, not to the disclosure signature. What changes is that an upgrade which edits any declared `invoke` field now counts as an executable-surface change and asks for consent again, where before it could alter what the lane runs in silence. (#2483) diff --git a/.changeset/serene-birds-sing.md b/.changeset/serene-birds-sing.md deleted file mode 100644 index 7940084c5..000000000 --- a/.changeset/serene-birds-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3490 ---- -state planned-phase now refreshes the Current Position Phase: line (the body source current_phase is re-derived from) instead of leaving a stale previous-phase line behind, so STATE.md frontmatter, body prose, and state json stay coherent; the --name argument is persisted into the Phase line and current_phase_name instead of being silently dropped. diff --git a/.changeset/serene-eagles-roar.md b/.changeset/serene-eagles-roar.md deleted file mode 100644 index bbde4df7c..000000000 --- a/.changeset/serene-eagles-roar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Security -pr: 3506 ---- -**`verify key-links` no longer reads files outside the project** — `from:` and `to:` were taken verbatim from plan frontmatter and resolved with `path.join(cwd, …)`, which normalizes `../` rather than rejecting it, so a plan carried in an untrusted repository could name any file the process could read and learn from the reported result whether a supplied pattern matched its contents. Both paths now resolve through the project's realpath-based confinement seam; a path that escapes is refused without being read, reported as `path_rejected`, and never counts as verified. (#3493) diff --git a/.changeset/serene-elks-sing.md b/.changeset/serene-elks-sing.md deleted file mode 100644 index 99c8619e4..000000000 --- a/.changeset/serene-elks-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3209 ---- -**A truncated milestone window is no longer reported as an empty milestone** — `roadmap analyze` now emits a `scope` field (`complete`/`truncated`/`unscoped`/`unreadable`) so `phase_count: 0` from a genuinely fresh milestone is distinguishable from a window that closed before reaching the roadmap's phase sections, and `milestone complete` refuses to archive on a truncated window instead of moving every phase directory in the project. (#3184) diff --git a/.changeset/serene-finches-bark.md b/.changeset/serene-finches-bark.md deleted file mode 100644 index c5abc003b..000000000 --- a/.changeset/serene-finches-bark.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3307 ---- -**`init` now reports the host runtime it is actually running under** — inside a Codex session GSD reported `agent_runtime: claude`, and checked the wrong directory for installed agents, because runtime identity was only ever read from `GSD_RUNTIME` or an explicit `runtime` in `.planning/config.json`. A detection rung now sits beneath both explicit sources, resolving `codex` from Codex's own session environment. Explicit settings still win, no shared defaults are written, and model resolution is untouched. (#3245) diff --git a/.changeset/serene-ibex-hum.md b/.changeset/serene-ibex-hum.md deleted file mode 100644 index 1d76afd41..000000000 --- a/.changeset/serene-ibex-hum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3462 ---- -Executor dispatch prompts no longer list companion files as raw @-include lines that Claude Code never expands inside an Agent() prompt string. The orchestrator now build-time embeds execute-plan.md and its companion references (summary template, checkpoints, tdd, worktree-path-safety, executor-examples) into the dispatched gsd-executor prompt, so execute-plan-only steps (segment_execution, previous_phase_check, verification_failure_gate, update_codebase_map) actually reach executors instead of silently never running. diff --git a/.changeset/serene-tigers-gather.md b/.changeset/serene-tigers-gather.md deleted file mode 100644 index e1cba0905..000000000 --- a/.changeset/serene-tigers-gather.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3444 ---- -roadmap validate now emits a V005 warning and exits non-zero when the active milestone's window is truncated — phase entries exist in ROADMAP.md but are excluded from the milestone's resolved section (e.g. an intervening version-bearing heading closes the window before its own Phase sections). Previously this passed silently with {"warnings":[]}. diff --git a/.changeset/sharp-birds-wave.md b/.changeset/sharp-birds-wave.md deleted file mode 100644 index 33fb991c8..000000000 --- a/.changeset/sharp-birds-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3424 ---- -**`/gsd-plan-phase`'s §13a Decision Coverage Gate no longer reports false total-coverage failures when a decision's own body contains a bulleted cross-reference to a sibling decision** — a bullet nested (indented) under an already-open decision, elaborating on how it relates to another decision, was previously indistinguishable from a malformed top-level declaration attempt. A single such bullet forced the whole coverage analysis to `could-not-parse`, discarding every decision that DID parse correctly and reporting `covered: 0` even when every decision was, in fact, fully covered by the phase's plans. (#3169) diff --git a/.changeset/sharp-ibex-tumble.md b/.changeset/sharp-ibex-tumble.md deleted file mode 100644 index 97c969060..000000000 --- a/.changeset/sharp-ibex-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3541 ---- -**Effort now supports `inherit` — "follow the session" is a first-class, declarable choice** — `effort.agent_overrides`, `routing_tier_defaults`, and `effort.default` accept `inherit`; the install-time writer omits the `effort:` frontmatter key for agents resolving to it (Codex omits the `model_reasoning_effort` pin), and `effort sync --apply` no longer re-adds a hand-stripped key — an absent key under `inherit` is in-sync, and a present one is stripped. An explicit `inherit` never escalates on failed attempts. (#3533) diff --git a/.changeset/sharp-moles-howl.md b/.changeset/sharp-moles-howl.md deleted file mode 100644 index 8131889fe..000000000 --- a/.changeset/sharp-moles-howl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3483 ---- -ZCode installs now strip mcp__* tool grants from installed GSD subagents at install time. ZCode's dispatcher treats every mcp____* entry in an agent's tools: frontmatter as a required MCP server and hard-fails the subagent spawn (CONFIGURATION_ERROR) when it is not connected, so /gsd-quick --full and plan/execute-phase flows failed out of the box with zero MCP servers configured. Installed ZCode agents now declare only core tools; MCP tools remain available when servers are connected. Claude Code installs are unchanged. diff --git a/.changeset/sharp-wolves-wander.md b/.changeset/sharp-wolves-wander.md deleted file mode 100644 index 791663368..000000000 --- a/.changeset/sharp-wolves-wander.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3404 ---- -**`/gsd-sync-skills` now refuses cross-runtime skill sync** — skill content and directory layout are runtime-specific (the installer applies per-runtime converters/adapter headers/brand swaps/layout rules), and two runtimes alias another runtime's skills root, so a verbatim cross-runtime copy silently corrupted destination skills and could overwrite a runtime the user never named. sync now refuses any `--to` that differs from `--from` and points at the installer, keeping identity sync (`--from` == `--to`) as a no-op. (#3025) diff --git a/.changeset/silly-jaguars-caper.md b/.changeset/silly-jaguars-caper.md deleted file mode 100644 index 41e0bde92..000000000 --- a/.changeset/silly-jaguars-caper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3401 ---- -**`milestone.complete` no longer records the wrong line as a release's accomplishment** — the one-liner was extracted from the first bold text under the SUMMARY's first heading, so an incidental first heading (a rule list, deviation notes) could contribute `Rule 1 - Bug` or `NeutralPath` as the milestone's permanent accomplishment in MILESTONES.md. Extraction now anchors to a Summary/Overview/Accomplishments heading and falls back to empty when none is present. (#3170) diff --git a/.changeset/silly-ravens-hum.md b/.changeset/silly-ravens-hum.md deleted file mode 100644 index 4fcca4daa..000000000 --- a/.changeset/silly-ravens-hum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3277 ---- -**ESLint now actually runs on 56 previously-unlinted source files** — a file matching no `files:` glob was not linted-and-clean, it was skipped entirely while `eslint .` still exited 0. All of `hooks/` and `eslint-rules/` sat in that blind spot. A new drift guard fails the build if any tracked source file resolves to zero rules without a recorded reason, so the class cannot silently regrow. (#3059) diff --git a/.changeset/soft-jade-quartz.md b/.changeset/soft-jade-quartz.md deleted file mode 100644 index 4be7e3bce..000000000 --- a/.changeset/soft-jade-quartz.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3371 ---- -**`gsd-tools validate health` and `validate consistency` no longer flag sentinel phase directories (999.x backlog/interim, 0.x drafts)** — the disk-vs-roadmap comparison now applies the `isSentinelPhaseId` guard that the phase commands already had. Sentinel ids are defined as never-on-roadmap, so a `999-interim` directory previously produced a permanent spurious W007 ("Phase 999 exists on disk but not in ROADMAP.md", advice to add it to the roadmap or delete it — both wrong) and a spurious "Gap in phase numbering: N → 999". Real (non-sentinel) orphans and genuine numbering gaps still warn. (#3225) diff --git a/.changeset/steady-jaguars-dance.md b/.changeset/steady-jaguars-dance.md deleted file mode 100644 index 7954c1d1c..000000000 --- a/.changeset/steady-jaguars-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3428 ---- -**`roadmap.analyze` now reports the real phase count instead of a silent `phase_count: 0`** when a CLOSED milestone heading sits between the active milestone heading and its own phase-detail sections. A prior refactor (#3184) already added a `scope` discriminator so the empty result was distinguishable from a genuinely empty milestone; this closes the other half of the issue — the consuming resume gate (`workflows/next.md` Route 0) iterates `.phases[]`, so an empty array silently disarmed the safety invariant regardless of the scope field. When the scoped window comes back empty, is non-COMPLETE scope, and phase directories exist on disk, the query re-scans the shipped-milestone-stripped document and populates the phase list while keeping `scope` non-COMPLETE so the result remains flagged as best-effort. (#3165) diff --git a/.changeset/steady-pumas-click.md b/.changeset/steady-pumas-click.md deleted file mode 100644 index eb662d0e4..000000000 --- a/.changeset/steady-pumas-click.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3252 ---- -**The 1.4.0 changelog entry for Cursor slash commands now credits the PR that shipped it** — the entry describing `gsd install --cursor` writing `.cursor/commands/` cited #803 (the Cline PR, which the adjacent entry cites correctly) instead of #805, so anyone tracing the Cursor commands surface landed in an unrelated change. (#2359) diff --git a/.changeset/steady-wasps-jump.md b/.changeset/steady-wasps-jump.md deleted file mode 100644 index 3e8408e45..000000000 --- a/.changeset/steady-wasps-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3636 ---- -**A lint rule now keeps Windows binary resolution in one place** — re-implementing PATH/PATHEXT lookup outside the platform seam is rejected at lint time, so the four divergent resolvers epic #3411 removed cannot quietly come back. No change to how GSD behaves at runtime. (#3619) diff --git a/.changeset/sturdy-birds-chatter.md b/.changeset/sturdy-birds-chatter.md deleted file mode 100644 index 6f1ffe8f6..000000000 --- a/.changeset/sturdy-birds-chatter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 2531 ---- -**Settings no longer offer worktree isolation on runtimes that cannot honor it, and health warns before execution fails closed** — previously `/gsd-settings` recommended "Yes" and persisted `workflow.use_worktrees: true` on every runtime, handing installs whose declared `dispatch.isolation` capability is `none` the exact value `/gsd-execute-phase` and `/gsd-quick` fail closed on. On those runtimes the Worktrees question now offers "No (Recommended)" / "Leave unchanged" (never an enabling option), warns when the config carries an inherited explicit `true`, and `/gsd-health` surfaces such a config as new warning W025 with a DEGRADED status before execution-time failure. Runtimes that declare `harness-worktree` or `orchestrator-worktree` are unaffected — the gate is the declared capability, never the runtime name. Both surfaces resolve isolation through the new `inspect-dispatch-isolation` query, a sentinel-free sibling of `dispatch-isolation`: the dispatch verb records its decision to the executor-isolation sentinel by design, which a read-only diagnostic must never trigger. The inspection verb rejects `--force-isolation`, `--phase` and `--plan` as usage errors rather than accepting and ignoring them — the recording verb applies `--force-isolation` after resolution, so silently ignoring it would hand the same argv two different answers. Both surfaces also distinguish "this runtime declares no isolation primitive" from "the capability could not be resolved", and say which one happened instead of reporting a resolver failure as a capability verdict. (#2486) diff --git a/.changeset/sturdy-dogs-caper.md b/.changeset/sturdy-dogs-caper.md deleted file mode 100644 index fbf0b6cb1..000000000 --- a/.changeset/sturdy-dogs-caper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3485 ---- -The /gsd slash command in Pi now visibly renders its output (progress, errors) via Pi's ctx.ui.notify mechanism instead of a return value Pi silently discards. diff --git a/.changeset/sturdy-finches-caper.md b/.changeset/sturdy-finches-caper.md deleted file mode 100644 index 68e068865..000000000 --- a/.changeset/sturdy-finches-caper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3273 ---- -**The optional pre-commit hook now actually checks command-alias drift** — every guard in `.githooks/pre-commit` was inert: nine matched paths under the retired `sdk/` tree and invoked npm scripts that no longer exist, and the tenth watched gitignored build outputs that git can never stage. Staging `src/command-aliases.cts` now runs `check:alias-drift` instead of passing silently. (#2725) diff --git a/.changeset/sturdy-foxes-gather.md b/.changeset/sturdy-foxes-gather.md deleted file mode 100644 index fbfc01a1a..000000000 --- a/.changeset/sturdy-foxes-gather.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3603 ---- -**`init.progress` no longer infers the next phase from stray out-of-order artifacts** — a phase directory created out of order (e.g. a phase-9 UAT evidence file while roadmap phase 8 was still pending and unscaffolded) dragged the reported frontier forward, making `init.progress` skip Phase 8 and disagree with `roadmap.analyze`; the frontier is now derived from roadmap order, with artifacts as corroborating evidence only. (#3581) diff --git a/.changeset/sturdy-hawks-munch.md b/.changeset/sturdy-hawks-munch.md deleted file mode 100644 index e1e42fbd8..000000000 --- a/.changeset/sturdy-hawks-munch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3482 ---- -phase complete now selects the lowest genuinely-outstanding lower-numbered phase as next_phase instead of a merely-positionally-next higher phase heading, and keeps STATE.md frontmatter current_phase and current_phase_name paired (both describe the same phase) even for narrative-prose STATE.md files diff --git a/.changeset/sturdy-koalas-zip.md b/.changeset/sturdy-koalas-zip.md deleted file mode 100644 index 7fc0bc106..000000000 --- a/.changeset/sturdy-koalas-zip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3449 ---- -planning-config.md documented "light" as an allowed workflow.code_review_depth value, but config-set only accepts quick/standard/deep — the reference now matches the validator, pinned by a doc↔capability-registry parity test. The agent_skills row now also documents the array-of-strings form for assigning multiple skill sets to one agent type. diff --git a/.changeset/sturdy-mice-sprint.md b/.changeset/sturdy-mice-sprint.md deleted file mode 100644 index 9f69d64e9..000000000 --- a/.changeset/sturdy-mice-sprint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3658 ---- -**Documentation no longer points at files that were renamed or deleted** — `docs/INVENTORY.md` claimed its roster was anchored by six drift-control tests when five had been deleted, and the four translations named a seventh that the English file had already dropped. `CONTEXT.md`, `VERSIONING.md`, `docs/CONFIGURATION.md` and `docs/skills/discovery-contract.md` pointed at `issue-NNN-` test filenames and `sdk/` paths that no longer exist, and `VERSIONING.md` described an SDK bundling step the release workflow does not perform. Most consequentially, `docs/TESTING-SUITES.md` instructed contributors to add drift acknowledgments to a file `CONTRIBUTING.md` says to never use — following it put the entry where the contributing guide forbids. (#3620) diff --git a/.changeset/sturdy-tigers-click.md b/.changeset/sturdy-tigers-click.md deleted file mode 100644 index 59f3d149d..000000000 --- a/.changeset/sturdy-tigers-click.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3403 ---- -**The EoS Registry now lists GSD for Reasonix** — discover the independently maintained `onionviolet/gsd-reasonix` protocol-v1 host integration for Reasonix, including exact install and uninstall commands, supported interface points, and negotiated host axes. diff --git a/.changeset/sturdy-voles-run.md b/.changeset/sturdy-voles-run.md deleted file mode 100644 index 79be1ccc8..000000000 --- a/.changeset/sturdy-voles-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3621 ---- -**Windows binary resolution now has one owner** — GSD resolves a command name to the file Windows can actually start, in the single platform seam, instead of four divergent copies. Reviewer lanes, `execTool`, and the capability spawn path all share it, so a `.cmd`/`.bat` shim resolves and runs where it previously failed with `spawn ENOENT`. macOS and Linux behavior is unchanged. (#3411) diff --git a/.changeset/sunny-badgers-howl.md b/.changeset/sunny-badgers-howl.md deleted file mode 100644 index 26729e4c1..000000000 --- a/.changeset/sunny-badgers-howl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3450 ---- -Workflow-backend waves (claude-orchestration, BETA) no longer strand executor commits on worktree-wf_* branches: the emitted Workflow script now returns each agent's worktree metadata, and the orchestrator records it into the wave manifest so the existing merge-and-cleanup step lands every plan's commits. Missing metadata now halts the wave loudly instead of reporting success with an empty worklist. diff --git a/.changeset/sunny-deer-hum.md b/.changeset/sunny-deer-hum.md deleted file mode 100644 index f93892079..000000000 --- a/.changeset/sunny-deer-hum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3551 ---- -**Global Claude Code installs now load their referenced workflow context** — `gsd-core/workflows/*.md` and other spec-tree files previously emitted `@$HOME/.claude/...` `@`-file-references, a form Claude Code's `@`-import resolver silently drops (only `~/` and absolute paths resolve). Every such reference now resolves on `~/`, matching the already-working skill/command surface; double-quoted shell $HOME references are untouched. (#3544) diff --git a/.changeset/sunny-geese-hop.md b/.changeset/sunny-geese-hop.md deleted file mode 100644 index 5e1ef6231..000000000 --- a/.changeset/sunny-geese-hop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3513 ---- -**A phase with more than one `*-VERIFICATION.md` no longer reports the wrong one** — verification-report discovery took the alphabetically-first match, so an ad-hoc worksheet such as `03-CORRECTION-VERIFICATION.md` beat the real `03-VERIFICATION.md` sitting beside it and the phase could report `missing` while a passing report existed. Three further copies of the same lookup picked whichever file the filesystem happened to list first, making phase status and the reported `verification_path` vary between machines. All five now share one resolver that prefers the canonically-named report and is deterministic when it has to fall back. (#3357) diff --git a/.changeset/sunny-ravens-parade.md b/.changeset/sunny-ravens-parade.md deleted file mode 100644 index c20e3e15f..000000000 --- a/.changeset/sunny-ravens-parade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3259 ---- -**The plan drift guard now flags the same fact stated two ways** — when ROADMAP.md, PLAN.md, STATE.md and CONTEXT.md contradict each other about a phase status, a success criterion, a requirement ID or a domain term, plan review reports it in REVIEWS.md naming both locations and which one is authoritative, instead of letting a fresh-context agent act on the stale copy. The phase-status axis is decided deterministically rather than by judgment, so a STATE/ROADMAP contradiction is caught the same way every time — and a disagreement about whether a phase is *complete* is always reported, never written off as one document lagging the other. Advisory only; it never blocks convergence, and the judgment axes key on contradicting knowledge rather than similar-looking text. Runs under the existing `plan_review.source_grounding` switch — no new setting. (#1956) diff --git a/.changeset/sunny-wolves-gather.md b/.changeset/sunny-wolves-gather.md deleted file mode 100644 index 8339143e4..000000000 --- a/.changeset/sunny-wolves-gather.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3454 ---- -gsd-review no longer creates empty gsd-review-context.md / gsd-review-research.md section files (or hangs waiting on input) when a phase has no CONTEXT/RESEARCH notes: the build_prompt guards now test the glob expansion itself instead of probing with ls, which the block's nullglob setting had made always-true. diff --git a/.changeset/tame-river-song.md b/.changeset/tame-river-song.md deleted file mode 100644 index 862b1ccee..000000000 --- a/.changeset/tame-river-song.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3368 ---- -**Milestone phase counts no longer drop every letter-named phase directory** — `getMilestonePhaseFilter` now includes letter-named phase directories (`Phase A:`…`Phase L:`, GSD's own non-numeric phase convention per ADR-612) in milestone progress and plan counts. A greedy regex previously captured the whole hyphenated directory name (`A-tool-output-contract` was read as `A-tool-output-contract` instead of `A`), so every letter-named phase silently fell out of its milestone and the progress/plan totals were fabricated over whatever numeric directory happened to survive — a well-formed, plausible number that could even look correct at a phase boundary. Numeric and milestone-prefixed phases are unchanged. (#3213) diff --git a/.changeset/tidy-bears-chatter.md b/.changeset/tidy-bears-chatter.md deleted file mode 100644 index af65ceb84..000000000 --- a/.changeset/tidy-bears-chatter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3598 ---- -**GSD now warns when `.planning/` is gitignored but still tracked by git** — adding `.planning/` to `.gitignore` has no effect on files git already tracks, so planning docs kept landing in commits while `commit_docs` reported false. `validate health` now reports this as W029 with the `git rm -r --cached` remedy. (#3586) diff --git a/.changeset/tidy-bears-wave.md b/.changeset/tidy-bears-wave.md deleted file mode 100644 index 96ff2e7fe..000000000 --- a/.changeset/tidy-bears-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3402 ---- -**Diagnostic rules for `.planning/` health checks now have a single parsed subject to read from** — `src/planning-snapshot.cts` composes the already-consolidated milestone, phase, and plan derivations into one scope-carrying projection, so a rule can no longer re-derive a field's location from raw document text the way three now-inert `validate health` predicates once did (#3162). No command output changes yet — `validate health` migrates onto it in a follow-up phase. (#3308) diff --git a/.changeset/tidy-goats-jump.md b/.changeset/tidy-goats-jump.md deleted file mode 100644 index ec12a2a24..000000000 --- a/.changeset/tidy-goats-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Added -pr: 3592 ---- -**Quick tasks can now be archived at milestone close-out.** `/gsd-complete-milestone` offers an opt-in prompt to sweep `.planning/quick/` into `.planning/milestones/-quick/` with a generated `README.md` index and a reset `Quick Tasks Completed` table, and `/gsd-cleanup` offers the same archival retroactively for milestones that were already closed. (#2142) diff --git a/.changeset/tidy-lynx-sing.md b/.changeset/tidy-lynx-sing.md deleted file mode 100644 index e2f55aec7..000000000 --- a/.changeset/tidy-lynx-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3226 ---- -**Milestone names are no longer truncated at a parenthesis, and a phase heading is never mistaken for the milestone** — a ROADMAP whose `### Phase N` heading mentioned a version could cause a wrong `milestone:` to be written to `STATE.md`, and a milestone named `v3.3 — Portability (Windows)` was recorded and rendered as `Portability`. Milestone identity now has one implementation; when it cannot be determined it is reported as absent instead of defaulting to a plausible-looking `v1.0`/`milestone`. (#3216) diff --git a/.changeset/tidy-ravens-sing.md b/.changeset/tidy-ravens-sing.md deleted file mode 100644 index 579d6800f..000000000 --- a/.changeset/tidy-ravens-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3475 ---- -phase-plan-index silently drops short-form depends_on references (e.g. ["01"]), collapsing every plan into wave 1. The planner template's two worked dependency examples taught exactly that broken short form; they now teach the full-form plan id (e.g. ["01-01"]) the file's own frontmatter comment and other examples already document, so newly authored plans keep resolvable dependency edges. Resolver-side short-form handling is tracked separately in #3473. diff --git a/.changeset/vivid-jaguars-purr.md b/.changeset/vivid-jaguars-purr.md deleted file mode 100644 index 4c08ca7f2..000000000 --- a/.changeset/vivid-jaguars-purr.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3575 ---- -**Recorded why install materialization stays three loops, not one** — an architecture decision for epic #2866 phase 6. Measuring the three sites showed they diverge in mechanism rather than duplicate each other, so unifying them would have broken a prune that structurally cannot delete user files. (#3574) diff --git a/.changeset/vivid-moles-chatter.md b/.changeset/vivid-moles-chatter.md deleted file mode 100644 index b0422f200..000000000 --- a/.changeset/vivid-moles-chatter.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: Changed -pr: 3439 ---- -**`/gsd-progress` no longer implies re-execution when only the verification report is missing** — the routing message now explains that running `/gsd-execute-phase` on a historical phase resumes at the verification gates and does not re-run already-summarized plans, and softens the unrecognized-status message to acknowledge an intentional non-standard marker. (#1762) - diff --git a/.changeset/vivid-pumas-jump.md b/.changeset/vivid-pumas-jump.md deleted file mode 100644 index 30c6020a0..000000000 --- a/.changeset/vivid-pumas-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3246 ---- -**Dev-dependency `js-yaml` bumped to the patched 4.3.1, resolving a high-severity quadratic-CPU advisory** — the lockfile now pins the backported `!!omap` fix (GHSA-5p4m-2wfm-xmqj, CVSS 7.5), reachable via eslint. A non-breaking in-range bump (no overrides, no major bump, one package moved); production `npm audit --omit=dev` is unaffected (devDependency only). (#3238) diff --git a/.changeset/wise-jaguars-cheer.md b/.changeset/wise-jaguars-cheer.md deleted file mode 100644 index 9ee3202cb..000000000 --- a/.changeset/wise-jaguars-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3629 ---- -**Executor dispatch no longer blocks on a plugin-marketplace install** — the compiled runtime library is a build artifact produced at publish time and gitignored, so a plugin or git-clone install materializes a tree that never has it. Every hook that required one of those modules did so without the existing self-heal build seam that the CLI entrypoint already calls, so the agent-isolation guard's missing-module error landed in its fail-closed catch and was reported as `could not read or resolve dispatch-isolation configuration` — blocking every `gsd-executor` dispatch from the first dispatch of a session, while the statusline and update-check worker crashed at module load on the same tree. All seven affected hook files now self-heal first: the isolation guards surface the build seam's own actionable error instead of a misleading config message and stay fail-closed, and the cosmetic hooks degrade quietly rather than taking down the prompt. The guards also now emit a machine-readable `reason_code` alongside the human message. Installs from npm are unaffected — the seam's already-built fast path returns immediately. (#3582) diff --git a/.changeset/wise-seals-zip.md b/.changeset/wise-seals-zip.md deleted file mode 100644 index 2afeef471..000000000 --- a/.changeset/wise-seals-zip.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: Changed -pr: 3306 ---- -**Phase completion is now decided by a single disk-strict predicate — a ticked ROADMAP checkbox no longer carries machine authority.** `isPhaseComplete` (`src/verification.cts`) is the one owner: a phase is complete exactly when its `*-VERIFICATION.md` reads `passed`, read unconditionally — plan count is never a precondition. This changes four observable surfaces: `init manager` now reports a zero-plan phase with a passing verification as complete instead of the retired `not_required` sentinel (#3168); `roadmap analyze`'s checkbox override is removed, so a ticked checkbox with outstanding plans or no passing verification now reports incomplete instead of complete; `roadmap update-plan-progress` routes through the same owner (and, unchanged, still refuses to write a completion checkbox/date while any plan lacks a `*-SUMMARY.md`); and `gsd-core/workflows/mvp-phase.md` stops ORing a checkbox-derived `PHASE_COMPLETE` into its completion decision, deciding on disk status alone. A ticked checkbox is not deleted — only its authority over these commands is removed. - -`workstream list`/`workstream status`'s per-phase `complete` status (via `buildWorkstreamInventory`) is now routed through the same owner instead of its own `summaryCount >= planCount`-plus-verification-verdict rule — a zero-plan phase with a passing verification now reports `complete` there too, and a phase whose `*-VERIFICATION.md` is absent no longer reports `complete` on summary count alone (the pre-existing "verifier-disabled projects still complete" tolerance is retired under disk-strict). That tolerance was #2645's deliberate boundary — `missing`/`unknown`/`stale` verdicts counted as non-failing so a project that never runs the verifier would not report 0% forever. Disk-strict retires it and closes #2645's Goodhart hole from the other side: deleting a `*-VERIFICATION.md` now lowers the reported completion instead of raising it. A project that does not run the verifier will report its phases incomplete. (#3186) diff --git a/.changeset/witty-herons-march.md b/.changeset/witty-herons-march.md deleted file mode 100644 index b34c06643..000000000 --- a/.changeset/witty-herons-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3229 ---- -**npm-global installs can now actually fail the agents-installed gate** — `checkAgentsInstalled` resolved the claude agents directory relative to its own install location, so an npm-global install validated the package's bundled `agents/` against itself and `agents_installed` could never be `false`, silently disabling the halt/warn gates in `new-project` and `new-milestone`. When the install-relative path lies inside a `node_modules` tree the claude runtime now resolves `getGlobalConfigDir('claude')/agents` like every other runtime, honouring `CLAUDE_CONFIG_DIR`; repo runs and runtime-config-dir installs are unchanged, and the `GSD_AGENTS_DIR` override stays priority 1. (#3203) diff --git a/.changeset/witty-lynx-greet.md b/.changeset/witty-lynx-greet.md deleted file mode 100644 index 4622a3ec9..000000000 --- a/.changeset/witty-lynx-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3519 ---- -**state json and the state-mutating commands now agree with what is actually on disk** — a stale body annotation could beat a fresher curated frontmatter value in state json output, and commands reported fields as updated that the write pipeline had already discarded while staying silent about fields it restored. Preservation is now enforced in one place across every path, each command reconciles its report against the persisted file, and a value dropped because this write deliberately removed its body line is reported rather than silently lost. (#3471) diff --git a/.changeset/witty-wasps-bark.md b/.changeset/witty-wasps-bark.md deleted file mode 100644 index cd5bcc73f..000000000 --- a/.changeset/witty-wasps-bark.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3496 ---- -**`must_haves.key_links[].pattern` now uses RE2 syntax** — backreferences and look-around are no longer supported in a key-links pattern, because they are the constructs that require a backtracking engine and cannot be evaluated in guaranteed linear time. A pattern using them is reported as `pattern_neutralized: "unsupported"` with the link marked unverified, rather than being silently matched as literal text. Ordinary patterns, including every example shipped in the docs, are unaffected. (#3477) diff --git a/.changeset/zesty-ibex-hop.md b/.changeset/zesty-ibex-hop.md deleted file mode 100644 index baad828c0..000000000 --- a/.changeset/zesty-ibex-hop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3420 ---- -**Plan files with Windows-style CRLF line endings now correctly enforce their `must_haves` contract** — `truths`, `artifacts`, `key_links`, and `prohibitions` blocks previously parsed to an empty list on any CRLF-authored plan file, silently degrading goal-backward verification to LLM-derived truths instead of the authored contract, with no error surfaced for the most common failure shape. (#3360) diff --git a/.changeset/zesty-moles-tumble.md b/.changeset/zesty-moles-tumble.md deleted file mode 100644 index ab30ec7ec..000000000 --- a/.changeset/zesty-moles-tumble.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3537 ---- -**Installing GSD for Claude at both global and local scope no longer silently hides your project's specs.** Claude Code always resolves the personal skill over the project command, so a project with a local install previously ran the global workflow specs with no warning. The install now prints which scope wins and `/gsd-health` surfaces the same as diagnostic W028; at global scope, the winning skill's workflow reference now resolves your project's own specs first when present. (#2218) diff --git a/.changeset/zesty-pumas-hum.md b/.changeset/zesty-pumas-hum.md deleted file mode 100644 index 7eff390ae..000000000 --- a/.changeset/zesty-pumas-hum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3341 ---- -**Installer no longer crashes when a source file disappears mid-copy.** `copyWithPathReplacement` used to throw an unhandled ENOENT if a listed workflow/command file was deleted between its directory listing and the actual read — a rare filesystem race that could abort an entire install. It now skips the vanished file and continues installing everything else. (#3333) diff --git a/.changeset/zesty-rams-march.md b/.changeset/zesty-rams-march.md deleted file mode 100644 index 93af49d80..000000000 --- a/.changeset/zesty-rams-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Changed -pr: 3600 ---- -**Agent files now install identically whether you run a full install or apply a surface.** Every runtime materializes its agents from its capability descriptor, so `/gsd-surface --materialize` no longer skips agent files for Cline, Codex, Hermes, Kilo, OpenCode and Kimi Code — previously it wrote none for those runtimes, leaving an install missing the agents a fresh install would have created. Installed output is byte-identical to before for every runtime. (#2866) diff --git a/.changeset/zesty-tigers-forage.md b/.changeset/zesty-tigers-forage.md deleted file mode 100644 index db5f68f1d..000000000 --- a/.changeset/zesty-tigers-forage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: Fixed -pr: 3433 ---- -verify plan-structure now recognizes task child elements that carry attributes on their opening tag (e.g. ), so plans annotating verify mode (auto vs human) or other child-tag attributes no longer produce false "missing " / "missing " / etc. warnings. Bare tags continue to validate exactly as before. diff --git a/CHANGELOG.md b/CHANGELOG.md index af4edbe08..80c23e866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,213 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [1.11.0] - 2026-08-19 + +### Added + +- **`resolve-execution` now tells the truth about what the agent will run at** — the query reported only the config-cascade effort, which is not what an installed agent uses when its `effort:` frontmatter was hand-stripped or drifted. `--json` adds `effort_effective` (read from the installed agent frontmatter for the claude runtime; `"inherit"` when the key is absent) and `effort_effective_source` (`frontmatter` | `frontmatter-absent` | `resolved`). All existing fields, including `--pick effort`, are unchanged. (#3534) (#3542) +- **The install manifest now records which runtime and scope wrote it** — a global and a project-local install used to write two `gsd-file-manifest.json` files that neither named their own runtime nor their own scope, so nothing could answer "which GSD surfaces are installed, where". The manifest gains `manifestVersion`, `runtime` and `scope`, and a new read-only Installed Surface Resolver reads both scopes at once. Manifests written by earlier versions are read without error and need no reinstall. (#2872) (#3323) +- **Opt-in `.git/hooks/pre-commit` guard for `commit_docs`** — `gsd-tools commit-docs-guard enable`/`disable` writes (or removes) a pre-commit hook that shells out to the existing `check-commit` verb, refusing a commit that stages `.planning/` files while `commit_docs` resolves to `false`. Closes the one bypass earlier phases of epic #2292 could not reach: a plain `git add -A && git commit` run by hand or by a script outside GSD's own tooling. Fully opt-in by maintainer narrowing — no install path wires it in by default (regression-locked by `tests/commands.test.cjs`'s E2 row); `enable` refuses rather than overwrites an existing foreign `pre-commit` hook, refuses when `core.hooksPath` would make the written hook inert, and resolves the real hooks directory via `git rev-parse --git-path hooks` so a linked worktree or submodule (where `.git` is a file) is handled correctly rather than assuming a literal `.git/hooks` path. The hook is identified by a stable `# gsd-core:commit-docs-guard` marker line, checked by presence rather than byte-equality. (#3588) (#3609) +- **installRuntimeArtifacts() now returns the plan it executed** — per kind, per scope, including on the combined OpenCode/Kilo family path that previously returned nothing — so an install's correctness is a value a caller can assert, not something only re-readable from disk afterward. Install IO routes through a new injectable fs seam (`install-fs-adapter.cts`), letting a full install run end-to-end against a fake adapter with no real destination filesystem contact; failures still throw rather than becoming a value, and a best-effort cleanup that fails is now visible in the return instead of silently swallowed. Writes on disk are unchanged. Completes ADR-58's never-landed `cleanup` rollout step. (#2874) (#3568) +- **Capability skills are now named at the install consent prompt** — installing a third-party capability whose only contribution was skills printed "ships no executable surfaces (declarative only)" and listed nothing, even though each `SKILL.md` body lands verbatim in your agent's instruction context. The pre-install disclosure now names every contributed skill in its own section and states plainly that the bodies are not content-scanned. Values interpolated into the prompt are escaped across every disclosed surface, so a crafted name can no longer forge additional lines of disclosure text. No stored consent is disturbed and no re-consent prompt fires. (#3248) (#3253) +- **`validate agents` now reports Codex `.toml` model posture, not just presence** — on a `codex` install it flags any agent whose `.toml` pins a GSD tier alias or a `claude-*` id (which Codex rejects with a 400, so the agent never spawns) or carries a `model_reasoning_effort` with no `model`. Previously the check confirmed only that agent files existed, so a stale install from before the passive-model posture reported healthy right up until a typed agent failed to start. Read-only — it names the offending agent and value and never edits your files. Reports `not_codex` and reads nothing on other runtimes. (#3242) (#3290) +- **`effort sync` now repairs stale Codex `.toml` files without a reinstall** — on a `codex` install it strips a `model` pin that Codex rejects (a tier alias or a `claude-*` id) and an orphaned `model_reasoning_effort`, so agents fall back to the always-available session model. An explicit real-Codex pin is left alone. It is a **dry run by default** — pass `--apply` to write — and only the offending lines are removed: line endings, BOM, comments, key order, and any keys you added by hand are preserved byte-for-byte, so a repair is a two-line diff rather than a reformatted file. A file that cannot be parsed is refused and reported, never partially rewritten, and writes are atomic. Pairs with `validate agents`, which detects the same drift. The `claude` path is unchanged. (#3243) (#3296) +- **`~/.gsd/defaults.json` shadowing is now diagnosed instead of silent** — in any project with a `.planning/config.json`, global model-side keys (`model_profile`, `model_overrides`, `models`, `dynamic_routing`, `runtime`, …) were silently ignored for model resolution; a file named `defaults.json` applied to no real project with no signal. GSD now prints a one-time stderr warning naming the shadowed keys. Resolution precedence is unchanged; global `effort` keeps working via effort sync and never warns. (#3532) (#3540) +- **`/gsd-review` now records which model each reviewer actually used** — REVIEWS.md frontmatter gains `models:` and `model_sources:`, so an unpinned lane's verdict is no longer attributable to an unknown model. (#2295) (#3649) +- **The read-injection scanner now reports which rules fired as structured data** — its PostToolUse output carries a `findings` array of `{ruleId, match}` records alongside the human-readable advisory, so consumers no longer have to parse the advisory sentence to learn what was detected (the advisory text itself is unchanged). (#3523) (#3548) +- **Plans can now opt into a specialist executor via a per-plan `agent_hint:` frontmatter field** — `execute-phase` dispatches the named subagent instead of `gsd-executor` when it resolves on the active runtime, and falls back to `gsd-executor` when the field is absent, blank, or the named agent does not resolve (byte-identical to today). Resolution consults the active runtime's agent directory (project-local and user-global, across filename variants) via a new `gsd-tools resolve-agent` query, and the hint flows through `phase-plan-index` as `plan_json.agent_hint`. Default-on via `workflow.agent_hint_routing` (set `false` to disable); covers the `Agent()`-based dispatch (harness-worktree and sequential). (#1689) (#3417) +- resolveTriggerSurface (Runtime Artifact Layout Module) resolves the /gsd- trigger surface — winner, shadowedBy, and nested-router registration — per runtime/scope, and a new runtime.triggerPrecedence descriptor axis (required-with-default) decides same-trigger collisions; agents and kimi-agents are never trigger-bearing. (#3291) +- **Complexity-triggered refactor proposals** — after a phase runs, GSD can now measure the complexity of the code that phase touched and surface a scoped refactor proposal when a function crosses a threshold or drifts past its recorded anchor, so entropy gets caught while it is still one function instead of a rewrite. Advisory and off by default; enable with `gsd config-set refactor.trigger_enabled true`. (#1953) (#3261) +- **`check:contract-drift` — a machine-enforced agent-contract registry** — sentinel markers, read-tag gates, and deleted-file test references can no longer drift silently: the Agent Registry table in `gsd-core/references/agent-contracts.md` is now linted against what agents emit and what workflows consume, and `lint-removed-but-needed` catches tests that pin files your PR deleted. (#3565) (#3571) +- **`runtime-homes` now exports its non-registry config-home descriptors** — `KIMI_HOOKS_TOML_DESCRIPTOR`, `NON_REGISTRY_CONFIG_HOME_DESCRIPTORS`, `GSD_LOCATION_ENV_KEYS`, and the `ConfigHomeDescriptor` type are public, so consumers that need the *set* of config-location env vars (rather than a single resolved path) can derive it instead of hand-maintaining a copy. `resolveKimiHooksTomlDir()` behaviour is unchanged; its descriptor is simply named rather than inline (#3156). + + **The test-instrumentation scripts no longer ship in the npm package** — `scripts/run-tests.cjs`, `scripts/live-config-guard.cjs`, `scripts/affected-tests-lib.cjs`, and `scripts/run-affected-tests.cjs` are now excluded from the tarball (they are one closed require chain of repo-only test tooling). `npm test` in an installed package was already inoperable (`tests/` has never shipped); a deep import of `scripts/run-tests.cjs` from the published package — an unsupported surface — will now be `MODULE_NOT_FOUND` (#3156). (#2677) +- **Per-phase `commit_docs` override** — set `phase_commit_docs.` to commit one phase's `.planning/` artifacts (e.g. an architecture phase) while keeping other phases local, without flipping the project-wide `commit_docs` switch. (#3587) (#3601) +- **`audit-open acknowledge` now suppresses open audit items at future milestone closes** — deferring an item via /gsd-complete-milestone previously only wrote a human-readable note; the item resurfaced at every later close with no way to silence it short of resolving it for real. The new `audit-open acknowledge --category --milestone [--at ] ...` CLI verb writes a verdict-preserving `audit_acknowledged` marker that suppresses the item starting at the next audit scan, without ever touching the artifact's own `status:` field, and self-invalidates the moment the artifact's observed state changes again. `query audit-open --json` now also reports an `acknowledged` count per category alongside `counts`, so a clean close can be told apart from one that is clean only because prior items are still suppressed. (#3458) (#3555) +- **Effort now supports `inherit` — "follow the session" is a first-class, declarable choice** — `effort.agent_overrides`, `routing_tier_defaults`, and `effort.default` accept `inherit`; the install-time writer omits the `effort:` frontmatter key for agents resolving to it (Codex omits the `model_reasoning_effort` pin), and `effort sync --apply` no longer re-adds a hand-stripped key — an absent key under `inherit` is in-sync, and a present one is stripped. An explicit `inherit` never escalates on failed attempts. (#3533) (#3541) +- **A lint rule now keeps Windows binary resolution in one place** — re-implementing PATH/PATHEXT lookup outside the platform seam is rejected at lint time, so the four divergent resolvers epic #3411 removed cannot quietly come back. No change to how GSD behaves at runtime. (#3619) (#3636) +- **The EoS Registry now lists GSD for Reasonix** — discover the independently maintained `onionviolet/gsd-reasonix` protocol-v1 host integration for Reasonix, including exact install and uninstall commands, supported interface points, and negotiated host axes. (#3403) +- **Windows binary resolution now has one owner** — GSD resolves a command name to the file Windows can actually start, in the single platform seam, instead of four divergent copies. Reviewer lanes, `execTool`, and the capability spawn path all share it, so a `.cmd`/`.bat` shim resolves and runs where it previously failed with `spawn ENOENT`. macOS and Linux behavior is unchanged. (#3411) (#3621) +- **GSD now warns when `.planning/` is gitignored but still tracked by git** — adding `.planning/` to `.gitignore` has no effect on files git already tracks, so planning docs kept landing in commits while `commit_docs` reported false. `validate health` now reports this as W029 with the `git rm -r --cached` remedy. (#3586) (#3598) +- **Diagnostic rules for `.planning/` health checks now have a single parsed subject to read from** — `src/planning-snapshot.cts` composes the already-consolidated milestone, phase, and plan derivations into one scope-carrying projection, so a rule can no longer re-derive a field's location from raw document text the way three now-inert `validate health` predicates once did (#3162). No command output changes yet — `validate health` migrates onto it in a follow-up phase. (#3308) (#3402) +- **Quick tasks can now be archived at milestone close-out.** `/gsd-complete-milestone` offers an opt-in prompt to sweep `.planning/quick/` into `.planning/milestones/-quick/` with a generated `README.md` index and a reset `Quick Tasks Completed` table, and `/gsd-cleanup` offers the same archival retroactively for milestones that were already closed. (#2142) (#3592) + +### Changed + +- **The `plan-phase` AI-integration capability gate no longer lists substring-collidable keywords** — bare `eval` (a substring of ordinary phase-goal words like `evaluation` and `retrieval`) is replaced by `llm eval`, and the under-specified `ai system` is dropped, per maintainer triage on the linked issue. The gate is a capability prompt, not a hard block, so this is a precision improvement: phase goals like "add evaluation metrics" or "build the retrieval layer" no longer invite a spurious AI-SPEC branch, and genuinely AI-flavored goals still match on the precise framework and technique names. (#2115) (#3431) +- **`/gsd-explore` research passes now disposition each surfaced claim three ways** — **admit** (survives a prompted-to-refute pass and is grounded in a source, shown with the source), **refute** (a source contradicts it, dropped or corrected), or **abstain** (unverifiable, or a source-vs-prior conflict). Abstained claims go to a separate **Unresolved** ledger instead of being smoothed into confident prose, so you can see what the research could not stand behind. Refute and abstain are separated by whether the disagreeing source is *authoritative for that claim* — a blog post contradicting your `engines` field is an abstain, the `engines` field itself is a refute — and your own prior belief is never authoritative alone. A finding that comes back with no disposition at all is ledgered as an abstain rather than silently dropped or asserted as prose. Two guards ship with it: conflict-abstention (a source-vs-prior conflict routes to the ledger, not a silent pick-a-side) and a tier floor (a would-be admit is presented as an abstain when the researcher's resolved tier is budget-level or could not be determined, because an under-tiered or unverified researcher over-defers to whatever source it was handed; corrections are unaffected). Keying the floor on the resolved tier rather than the model id keeps it working on non-Claude installs, where the model id is often blank or substituted by the runtime. The floor narrows this gap rather than closing it — a config that deliberately repoints one tier at another tier's model can still report a higher tier than what actually runs. Claims-side analogue of the honest verifier. (#2229) (#2543) +- **STATE.md now records the commit it was written against** — a new `state_head` frontmatter stamp lets `/gsd-health` and smart-entry report how far the codebase has moved since STATE.md was last written, so a long-stale STATE.md can be discounted rather than read at face value. Health adds advisory `W024` once the gap reaches 20 commits. This is a freshness proxy, not a drift measurement: the count includes commits that never touched anything STATE.md describes, and the stamp refreshes on any state write — so it is always worded as approximate and never gates anything. The stamp is omitted entirely when the commit cannot be resolved to the project's *own* repository — a project nested inside an unrelated checkout reports unknown rather than borrowing that repo's freshness. (#2573) (#2622) +- **Live-plan counting now has one owner, so `superseded` plans stop being scheduled and nested-layout phases stop reporting zero** — `scanPhasePlans` is the sole source of which plans exist and which are outstanding. Twenty-one call sites that re-derived it from filenames now route through it, so a plan marked `status: superseded` is no longer scheduled into an execute-phase wave, phases using the nested `plans/` layout no longer report zero plans, and stray summaries no longer inflate completion. (#3183) (#3199) +- **A percentage is now withheld everywhere its scope is not `COMPLETE`, not just at the sites Phase 3 reached** — closing ADR-3180 §7.6 rule 4 at the two remaining gaps an isolated review caught: `state json`'s `buildStateFrontmatter` no longer hardcodes `SCOPE.COMPLETE` when deriving `progress.percent` (it now threads the real `listMilestonePhaseDirs` scope through `_diskScanCache`, including its prose-fallback path, so a genuinely unreadable `.planning/phases` directory can no longer surface a stale or falsely-earned number there while every other surface withholds), and `roadmap analyze --json` now exposes the scope that actually gates `progress_percent` as its own `progress_scope` field — distinct from the top-level `scope` (heading-windowing identity) — so a consumer can tell *why* `progress_percent` is `null` from the JSON alone instead of seeing `scope: "complete"` next to an unexplained `null`. `state update-progress` also now writes a `[gsd-tools] WARNING:` line to stderr when it silently no-ops on a non-`COMPLETE` scope, so the skip is not visible only to a JSON `reason` field most callers never read. **`state sync` now also withholds**: it no longer hardcodes `SCOPE.COMPLETE` when deriving the percentage it writes into `STATE.md`'s body — a non-`COMPLETE` scope (confirmed reproducible on `TRUNCATED` and `UNSCOPED` fixtures, not just the previously-checked `UNREADABLE` case) skips the `Progress:` write entirely and records a `Progress: skipped — …(#3217)` entry in `changes`, instead of persisting a fabricated percentage that could disagree with the same write's own (already-scoped) frontmatter `progress:` block. `0` under a genuinely `COMPLETE` scope is unaffected and still renders. Tier-2: `progress_percent`, `percent`, and `plan_percent` are `number | null`; `computeProgressPercent` requires a `scope` argument; `roadmap analyze --json` gains a new `progress_scope` field; `state sync --raw`'s `changes` array can now contain a scope-skip entry and correspondingly withhold a `Progress:` body write it would previously have made. (#3217) (#3318) +- **`gsd-plan-checker` now flags same-wave plans that are coupled but don't say so** — two plans in the same wave that share mutable state (a config key, table, migration, env var, singleton) or depend on each other's execution order, with no `depends_on` edge between them, are reported as an advisory Dimension 3 finding. The coupling gets settled at plan time instead of surfacing as an intermittent failure during parallel execution. `docs/AGENTS.md`'s plan-checker entry, which claimed eight verification dimensions and listed eight names matching none of the agent's actual fifteen, is corrected to the real list in the same change. (#1954) (#3237) +- state validate now runs its drift scan for STATE.md files whose phase lives only in frontmatter, instead of silently skipping the scan and reporting a false-clean result (#3162); it also no longer lets a frontmatter status: key shadow the body Status field. Its output gains a scope field (complete/truncated/unscoped/unreadable) reporting whether the check could actually run — valid still means no drift was found, and is not derived from scope. (#3187) + + state complete-phase's idempotency guard now consults frontmatter current_phase (via the same fallback chain as state validate), so a STATE.md whose phase lives only in frontmatter is no longer silently rolled back on a re-run of `state complete-phase --phase N`. It also gains a new refusal path: when the frontmatter cannot be parsed, the command now errors out ("Unable to read STATE.md frontmatter; refusing to run complete-phase to avoid a destructive rollback") instead of guessing. (#3187) + + workstream list/status/progress's per-workstream state projection (status, current_phase, last_activity) now resolves those fields from frontmatter when the body has no corresponding field, instead of reporting them absent — a frontmatter-only STATE.md's workstream inventory output changes accordingly. (#3187) (#3283) +- **`effort.routing_tier_defaults` now merges over the built-in tier defaults instead of replacing them** — previously, creating an `effort` block without `routing_tier_defaults` silently disabled the built-in tier ladder (light:low / standard:high / heavy:xhigh), collapsing every non-overridden agent to `high`; one `agent_overrides` entry could reshape 20+ agents you never named. A partial block now fills gaps from the built-ins, and an invalid value falls back to that tier's built-in. (#3531) (#3539) +- **milestone complete no longer lets a stale STATE.md body line overwrite fresher frontmatter** — it wrote through a path that re-derived frontmatter from the body with no preservation pass, so a stale Stopped-at line silently replaced a newer curated value, exactly as phase complete did before it was fixed. It now runs the same preservation the rest of the write path uses, and reports each field it protected in a new preservation_warnings array instead of staying silent about the divergence. (#3469) (#3501) +- **GSD now requires Node 24 or newer** — the `engines.node` floor moves from 22 to 24, and the Node 22 test lane is retired. Node 22 entered Maintenance LTS and this project tracks the Active LTS line; the change is what lets regex escaping delegate to the built-in `RegExp.escape` instead of a hand-rolled implementation. If you are on Node 22, upgrade before updating GSD. (#3416) +- **Worktree-wave merges now warn when a plan branch committed outside its declared scope** — the `execute-phase` cleanup gauntlet compares each branch's actual committed diff against the `files_modified` the plan declared and reports every path outside it. Advisory only: the merge still proceeds and the exit status is unchanged. (#2596) (#3264) +- **Progress percentages now come from one owner** — every `.planning/` completion percentage the CLI reports is computed by a single shared function instead of six hand-inlined copies, so a rounding or ceiling fix can no longer land on one command and silently miss the others. Reported values are unchanged. (#3180) (#3223) +- **Fallow binary resolution now shares the platform seam** — resolving the fallow binary uses the same PATH/PATHEXT logic as every other spawn, so on Windows a `fallow.cmd` shim resolves correctly and an extensionless npm shim is no longer picked up in its place. `node_modules/.bin` is still searched before `PATH`, and the POSIX executable-bit check is unchanged. (#3618) (#3633) +- **`validate health` splits two previously-conflated warning codes into their own codes** — W021 now covers only the phase-id-convention mismatch it originally meant; the STATE-vs-ROADMAP milestone-complete mismatch it used to also report moves to the new W026. Likewise W017 now covers only orphan worktrees; the stale-worktree case moves to the new W027. (#3405) +- **`validate consistency`'s `warnings` are now coded diagnostics** — each entry is a `{code, message, fix, repairable}` object instead of a bare string. Findings that overlap with `validate health` (a phase in ROADMAP.md with no directory on disk, or vice versa) now carry the exact same `W006`/`W007` codes `validate health` already uses for them, so there's one vocabulary for that finding, not two. The four subjects unique to this command (phase/plan numbering gaps, orphan summaries, plans missing `wave` frontmatter) get a new `C001`-`C004` code range. (#3407) +- **Digit-leading phase names now resolve consistently by bare number** — phases such as "24/7 Autonomy", "80/20 Cleanup", and "12-Factor Refactor" now resolve across every phase verb instead of appearing missing; ambiguous directory collisions now fail loudly with their candidate paths instead of silently selecting the first match. `/gsd` and `/gsd:progress` also stop under-reporting: their verify-failed check shares the same directory selection, so a failed verification in one of these phases is surfaced rather than read as a healthy phase, and phase directories carrying a project-code prefix (`MEM-05-…`) are no longer skipped by that check entirely. The same selection now backs every remaining consumer that had resolved directories on its own, so `phases list`, `phase remove`, `phase next-decimal`, the schema-drift gate, the init-manager overview, `roadmap analyze`, and the milestone-completion and health consistency checks stop reporting these phases as having no directory. `/gsd-health` no longer reports one of these phases as both missing from disk and absent from the roadmap at the same time (W006 + W007), and `phase remove` now refuses — without deleting or renumbering anything — when two directories claim the same bare phase number. `phase remove` also stops writing a phase count one too high into STATE.md when the phase it just deleted was one of these digit-leading directories (#2528). (#2559) +- **`state validate`'s `warnings` are now coded diagnostics, and the `drift` field is gone** — each entry is a `{code, severity, message, remedy}` object (seven codes, `S001`-`S007`) naming exactly what STATE.md disagrees with the filesystem about and how to fix it, instead of a bare string. The separate `drift` object every response used to carry is removed entirely; every condition it used to report (a conflicting phase reference, a missing phases directory, a plan-count mismatch, a stale executing status) is now one of the seven coded warnings, so no information is lost, it's just structured. `valid` and `scope` are unchanged. (#3407) +- **/gsd-progress and /gsd-execute-plan stop counting superseded plans as outstanding work** — seven prompt-layer sites across execute-plan.md, plan-phase.md, plan-review-convergence.md and progress.md counted plans with a raw `ls *-PLAN.md | wc -l`, so a plan marked `status: superseded` was still counted as outstanding, a phase on the nested plans/ layout (#3139) reported zero plans it actually had, and loosely-named plan files were missed entirely. Every site now calls `phase find`, which gains three additive fields — `plan_count`/`summary_count` (live, superseded excluded — 'how much is left') and `plan_count_all` (physical, every plan on disk — 'what did the planner write') — so what a workflow shows and what `phase find` reports for the same phase are now the same number. This also fixes a dead route: progress.md's Route 0 resume-incomplete-phase check read `.plans`/`.summaries` arrays that its producer, roadmap.analyze, never emitted (it emits plan_count/summary_count scalars), so both counts were always 0 and the check had never fired at all — it now fires correctly. **This is a behavior change you'll notice:** plan/summary counts shown by these workflows will move — toward being correct. (#3218) (#3327) +- **`validate health --repair` no longer resets config.json or regenerates STATE.md automatically** — these two repairs are destructive (they lose custom settings or session history), so they're now reported with their fix described but never auto-applied; run the suggested command yourself to apply them. (#3405) +- **Gap-closure planning no longer documents a completion marker nothing reads** — the planner emitted `## GAP CLOSURE PLANS CREATED` but no workflow had a dispatch branch for it, so completion was always detected via the `gap_closure: true` fix-plan artifacts anyway; the dead marker is retired and the artifact route (verify-work `--gaps` spawn → plans → `execute-phase --gaps-only`) is now the documented contract. (#3440) (#3443) +- **Progress, stats, and phase listings now stay within the current milestone.** `progress`, `stats`, and `phases list` no longer count backlog (`999.*`) or pre-milestone (`0-*`) directories as current-milestone phases, and `phases clear` / `milestone complete` no longer delete or archive those directories. `phases list --phase` and `--include-archived` are unaffected, since they intentionally look up or list beyond the current milestone. (#3185) (#3222) +- **Codex agents now inherit the session model instead of getting a pinned per-tier model** — if you install for `codex` with a `runtime` set and any `model_profile` other than `inherit`, GSD no longer writes a `model` (or `model_reasoning_effort`) line into `~/.codex/agents/.toml`. This fixes typed agents failing to spawn with `400 invalid_request_error: "The 'sonnet' model is not supported when using Codex with a ChatGPT account"`, which degraded the whole plan/execute flow to a generic-agent fallback. **To keep pinning a model, set an explicit real-Codex id in `model_overrides`** (e.g. `{"model_overrides": {"gsd-planner": "gpt-5.6-sol"}}`) — that path is unchanged. The installer prints a one-time notice when it drops a pin. Codex-only; all other runtimes are untouched. (#3241) (#3276) +- **`gsd-verifier` now says *why* a verified truth holds, not just that it does** — a truth that reaches `✓ VERIFIED` is additionally classified against three incidental-reliance patterns (an undeclared precondition, an ordering or side effect nothing enforces, a truth that is only true under the test fixture) and, when one matches, is reported as `✓ VERIFIED (coincidental-reliance)` with an entry in the new `coincidental_reliance_items` frontmatter list naming what to harden. Purely advisory: the base `✓ VERIFIED` token is unchanged, the truth still counts toward the score, the overall `status` is unaffected, and no human-verification item is emitted — a passing phase still passes. Only a consumer matching the truth-row verdict cell for exact equality (rather than as a substring) needs to tolerate the suffix. Two limits stated up front: the check is endogenous, and so measurably weaker than the exogenous `backstop` tag `gsd-core/references/honest-verifier.md` routes on — advisory status is the consequence, and its precision is unmeasured; and `gsd-core/workflows/verify-phase.md` is not edited, receiving the rule through its eager import of the verification-report template rather than a second inline copy, because it sits 29 bytes under its size hard cap. (#1955) (#3250) +- **Install scope is now resolved once, as a value** — the installer and the modules downstream of it no longer each re-derive whether an install is global or local from a bare string. One module owns the scope axis and reports its config home, its per-scope settings file, and whether it requires a consent record. No behavior changes for any install. (#2870) (#3278) +- **`runtime.hostBehaviors` is now a closed vocabulary** — the capability-manifest field that carries per-host install and adaptation switches was validated by nothing, so a typo'd or invented key was silently ignored forever. Its 59 keys are now enumerated, and a key outside the vocabulary is ignored with a non-fatal warning naming the capability and the key. It is never a validation error: a manifest authored against a newer GSD degrades visibly rather than failing the build, and an out-of-tree runtime descriptor carrying a bespoke key keeps installing. No shipped capability is affected. (#2801) (#3272) +- **The ADR gate now resolves documentation links and checks H1 status brackets** — a link in `docs/adr/` that pointed nowhere, and an H1 whose trailing `[Status]` bracket contradicted its own `Status:` field, both passed CI green; readers and agents following those citations hit dead ends the build had already blessed. `gen-adr-index.cjs --check` now fails on either, naming the file, the line, and the unresolved target. Links inside fenced or inline code are left alone, and resolution is case-exact on every platform. A new `--json` flag reports the same findings as a structured document with stable `reason` codes, so tooling never has to pattern-match an error message. (#2704) (#3266) +- **The `claude` reviewer in `/gsd:review` no longer inherits your CLAUDE.md or auto-memory** — the lane now declares `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` (CLAUDE.md loading and auto-memory are independently-toggled mechanisms, so each gets its own variable), merged into that one spawn's environment, so it reviews the same self-contained prompt the gemini and codex reviewers already receive. It was previously the only reviewer additionally seeing your global CLAUDE.md, the project CLAUDE.md, and Claude Code auto-memory — a context asymmetry against the workflow's own independent-review premise, and a measured ~4k extra input tokens per spawn. Carried as declared lane data (`invoke.env`, ADR-2782), not a bespoke handler; nothing reaches the orchestrating session or any other lane in the run. Affects `/gsd:review` (and the convergence flow that reuses it) invoked from a non-Claude-Code runtime; inside Claude Code the claude reviewer already self-skips for independence. (#2483) (#2493) +- **Agent required-reading enforcement now actually fires** — spawner workflows and commands emitted `` while agents gate on ``, so the "you MUST Read every listed file" clause never triggered; the canonical tag is now `` everywhere (46 spawn blocks across 24 workflows), with a repo guard banning the legacy tag so the two vocabularies can never drift apart again. (#3423) (#3432) +- **The installer module no longer re-exports internals it does not own.** `bin/install.js` exported 197 names, 70 of which were either dead or plain pass-throughs to the modules that actually implement them — kept for "existing consumers" that turned out not to exist, since no production code has ever required the file. Those 70 are gone and their tests now import the owning modules directly. No installed output changes. (#2876) (#3615) +- **A truncated milestone window is no longer reported as an empty milestone** — `roadmap analyze` now emits a `scope` field (`complete`/`truncated`/`unscoped`/`unreadable`) so `phase_count: 0` from a genuinely fresh milestone is distinguishable from a window that closed before reaching the roadmap's phase sections, and `milestone complete` refuses to archive on a truncated window instead of moving every phase directory in the project. (#3184) (#3209) +- **`init` now reports the host runtime it is actually running under** — inside a Codex session GSD reported `agent_runtime: claude`, and checked the wrong directory for installed agents, because runtime identity was only ever read from `GSD_RUNTIME` or an explicit `runtime` in `.planning/config.json`. A detection rung now sits beneath both explicit sources, resolving `codex` from Codex's own session environment. Explicit settings still win, no shared defaults are written, and model resolution is untouched. (#3245) (#3307) +- **The plan drift guard now flags the same fact stated two ways** — when ROADMAP.md, PLAN.md, STATE.md and CONTEXT.md contradict each other about a phase status, a success criterion, a requirement ID or a domain term, plan review reports it in REVIEWS.md naming both locations and which one is authoritative, instead of letting a fresh-context agent act on the stale copy. The phase-status axis is decided deterministically rather than by judgment, so a STATE/ROADMAP contradiction is caught the same way every time — and a disagreement about whether a phase is *complete* is always reported, never written off as one document lagging the other. Advisory only; it never blocks convergence, and the judgment axes key on contradicting knowledge rather than similar-looking text. Runs under the existing `plan_review.source_grounding` switch — no new setting. (#1956) (#3259) +- **Milestone names are no longer truncated at a parenthesis, and a phase heading is never mistaken for the milestone** — a ROADMAP whose `### Phase N` heading mentioned a version could cause a wrong `milestone:` to be written to `STATE.md`, and a milestone named `v3.3 — Portability (Windows)` was recorded and rendered as `Portability`. Milestone identity now has one implementation; when it cannot be determined it is reported as absent instead of defaulting to a plausible-looking `v1.0`/`milestone`. (#3216) (#3226) +- **`/gsd-progress` no longer implies re-execution when only the verification report is missing** — the routing message now explains that running `/gsd-execute-phase` on a historical phase resumes at the verification gates and does not re-run already-summarized plans, and softens the unrecognized-status message to acknowledge an intentional non-standard marker. (#1762) (#3439) +- **Phase completion is now decided by a single disk-strict predicate — a ticked ROADMAP checkbox no longer carries machine authority.** `isPhaseComplete` (`src/verification.cts`) is the one owner: a phase is complete exactly when its `*-VERIFICATION.md` reads `passed`, read unconditionally — plan count is never a precondition. This changes four observable surfaces: `init manager` now reports a zero-plan phase with a passing verification as complete instead of the retired `not_required` sentinel (#3168); `roadmap analyze`'s checkbox override is removed, so a ticked checkbox with outstanding plans or no passing verification now reports incomplete instead of complete; `roadmap update-plan-progress` routes through the same owner (and, unchanged, still refuses to write a completion checkbox/date while any plan lacks a `*-SUMMARY.md`); and `gsd-core/workflows/mvp-phase.md` stops ORing a checkbox-derived `PHASE_COMPLETE` into its completion decision, deciding on disk status alone. A ticked checkbox is not deleted — only its authority over these commands is removed. + + `workstream list`/`workstream status`'s per-phase `complete` status (via `buildWorkstreamInventory`) is now routed through the same owner instead of its own `summaryCount >= planCount`-plus-verification-verdict rule — a zero-plan phase with a passing verification now reports `complete` there too, and a phase whose `*-VERIFICATION.md` is absent no longer reports `complete` on summary count alone (the pre-existing "verifier-disabled projects still complete" tolerance is retired under disk-strict). That tolerance was #2645's deliberate boundary — `missing`/`unknown`/`stale` verdicts counted as non-failing so a project that never runs the verifier would not report 0% forever. Disk-strict retires it and closes #2645's Goodhart hole from the other side: deleting a `*-VERIFICATION.md` now lowers the reported completion instead of raising it. A project that does not run the verifier will report its phases incomplete. (#3186) (#3306) +- **state json and the state-mutating commands now agree with what is actually on disk** — a stale body annotation could beat a fresher curated frontmatter value in state json output, and commands reported fields as updated that the write pipeline had already discarded while staying silent about fields it restored. Preservation is now enforced in one place across every path, each command reconciles its report against the persisted file, and a value dropped because this write deliberately removed its body line is reported rather than silently lost. (#3471) (#3519) +- **`must_haves.key_links[].pattern` now uses RE2 syntax** — backreferences and look-around are no longer supported in a key-links pattern, because they are the constructs that require a backtracking engine and cannot be evaluated in guaranteed linear time. A pattern using them is reported as `pattern_neutralized: "unsupported"` with the link marked unverified, rather than being silently matched as literal text. Ordinary patterns, including every example shipped in the docs, are unaffected. (#3477) (#3496) +- **Agent files now install identically whether you run a full install or apply a surface.** Every runtime materializes its agents from its capability descriptor, so `/gsd-surface --materialize` no longer skips agent files for Cline, Codex, Hermes, Kilo, OpenCode and Kimi Code — previously it wrote none for those runtimes, leaving an install missing the agents a fresh install would have created. Installed output is byte-identical to before for every runtime. (#2866) (#3600) + +### Removed + +- **The undocumented `runtime.hostBehaviors.reviewerCli` capability field has been removed** — it was superseded by the declared `reviewer` body in 1.9.0 and kept working for one release as a derived alias. A manifest that still sets it contributes no reviewer lane and now reports a non-fatal warning naming the capability, at build time on stderr and at install time through the overlay loader; nothing crashes and no other behavior changes. Every shipped reviewer lane already declares a `reviewer` body, so the roster is unchanged — if you maintain an out-of-tree runtime descriptor that relied on the flag, declare a `reviewer` body to restore the lane. (#2801) (#3272) +- **Two workflow files that shipped to every runtime but were never loaded are gone** — `discovery-phase.md` and `plan-milestone-gaps.md` had no command, agent, or skill referencing them, and `docs/INVENTORY.md` claimed callers for one that did not exist. A new lint rule now fails the build if any shipped workflow becomes unreachable again. (#3560) (#3564) +- **Removed the orphaned `verify-phase` workflow (~40 KB shipped to every runtime, never loaded)** — its still-live verification gates (decision-coverage validation, test-quality audit, infrastructure-phase human-verification scoping) moved to a reference the verifier agent actually loads, so they run again instead of shipping as dead prose; installs are ~40 KB lighter and PRs to the verifier no longer mirror a dead twin to keep lockstep tests green. (#1891) (#3422) + +### Fixed + +- **`withPlanningLock` no longer reports a phantom "held by a live process" timeout when `.planning/` cannot be created** — a best-effort `try { platformEnsureDir(...) } catch { /* ok */ }` swallowed the real mkdir failure (EACCES/ENOSPC/EROFS), so the subsequent lock write failed with ENOENT (parent missing), and because ENOENT is in the lock's retry set (added for a Docker overlay-fs race) the loop spun the full 10 s budget before throwing a misattributed contention error that pointed operators at a nonexistent lock-holder. The mkdir failure now propagates immediately with its real filesystem errno and message, so an unwritable or full disk is reported as itself, not as concurrent-writer contention. The Docker overlay-fs ENOENT *lock-write* race (directory present) is still retried as before, and every code path where `.planning/` already exists or can be created is unchanged. Part of epic #1879 (distinguish "absent" from "corrupt/permission-denied" across engine read paths). (#1884) (#3472) +- **The idle/staleness detector now fires when `last_activity` carries a description** — a `last_activity` written in the shape `templates/state.md` prescribes (`[YYYY-MM-DD] — [What happened]`) parsed to `NaN`, and because the detector treats an unparseable value as "not stale" it failed open to `false`. Any project whose `last_activity` kept its description was never reported idle, no matter how long it had sat. The leading date is now parsed out of the value, so the description no longer blinds the only staleness signal in the front door. An impossible calendar date such as `2026-02-30` is now rejected outright rather than silently rolling forward to a real — and wrong — date. (#2570) (#2571) +- **`/gsd-ship` now detects and recovers a PR wedged by the ship-note commit** — when the `[ci skip]` ship note leaves required checks unstarted, ship re-triggers CI instead of leaving the PR unmergeable. (#2783) + + *Note: This introduces a latency tradeoff. All `/gsd-ship` invocations now poll GitHub PR state for up to 15 seconds to ensure the commit was processed and check if recovery is needed, even for repositories without required checks.* (#2818) +- **`spec-phase` Step 5.5 now surfaces the edge-probe's proposed edges to the resolution loop instead of discarding them** — the deterministic coverage report was computed, validated, then reduced to a single applicable-count, so the resolution loop re-derived edge categories from requirement prose and the engine's proposals never reached it. The report is now rendered into context and its rows are consumed as a *floor* the model unions with its own classification (still adding any category the classifier missed), so the written `## Edge Coverage` reflects the engine's deterministic taxonomy rather than model-invented categories; `--auto` gets the same floor. (#3102) (#3391) +- **`/gsd-quick --validate` no longer trusts a verification result it cannot actually read** — quick parsed the verifier's status by grepping the whole report rather than its frontmatter, so a `status:` line in the report's prose could be picked up alongside or instead of the real one, staleness was never detected at all, and a range of valid and malformed reports alike resolved to a value no routing arm matched — leaving the orchestrator to improvise at the moment the pipeline had failed. Quick now reads the same frontmatter-anchored, staleness-aware `verification.status` query that `execute-phase`, `verify-work` and `progress` already use, and routes `missing` / `unknown` / `stale` through an explicit arm instead of falling through. (#3174) (#3205) +- **The verifier's non-inferable (`backstop`) abstention rule now defines "explicit evidence" where the verifier is guaranteed to read it.** Step 3 item 5b used the term undefined — its definition was stranded in `gsd-core/references/honest-verifier.md` behind a stale `references/` cite that does not resolve, so the term fell back to the verifier's default notion of evidence (symbol presence + wiring), the exact false-pass the #1154 abstention protocol exists to refuse. 5b now carries the definition inline (a passing wired held-out/property-based test or directly observed behavior; presence + wiring never qualifies), the AFK never-silent/never-halt completion line and the `insufficient_spec`-vs-manual-UAT distinction ship in the eagerly-loaded `verifier-phase-gates.md` reference, and the agent file's three stale bare `references/` cites are gone: the two at 5c and the MVP-mode section now resolve under the `gsd-core/` prefix, and 5b's is superseded by the inline definition itself. (#3206) (#3435) +- **Autonomous/auto-mode no longer auto-approves unmet `` checkpoints, and the blocker loop now halts `needs_human` instead of retrying forever** — the checkpoint an executor returns when a task's `` is unmet (an unmet `user_setup` step, a missing env var, an absent prior-phase artifact) now carries `gate="blocking-human"`, which both auto-mode bypass layers (executor checkpoint protocol and execute-phase checkpoint handling) honor, so it always stops for a human instead of being silently approved with a synthetic "approved" and then failing `` on the still-missing prerequisite. Independently, `/gsd:autonomous`'s blocker handler now counts "Fix and retry" attempts per phase step and, after 3 failed attempts, escalates to a terminal `needs_human` halt that surfaces the unmet items and records a `## Needs Human` STATE.md row, ending the observed multi-hour retry loops on operator-gated plans. (#3210) (#3528) +- **`state add-roadmap-evolution` and `state add-decision` no longer persist a literal `Phase ?` when `--phase` is omitted** — both commands built their entry from the raw CLI flag's `?` fallback instead of the phase already recorded in STATE.md, even with `current_phase: 3` present in frontmatter. Both now resolve the phase through a strict write-path ladder (frontmatter `current_phase` → body `Current Phase` → `Phase: X of Y` scoped strictly to `## Current Position`), leaving `?` only when genuinely unresolvable; an explicit `--phase` still wins. The resolver deliberately does not reuse the read-path `resolveStatePhase`, whose document-wide fallback could adopt a stale historical `| Phase | N |` table row. A guard test now sweeps `src/*.cts` for any new raw `phase || '?'` call site. (#3481) (#3522) +- **Frontmatter round-trips no longer double backslashes on every state write** — `escapeDoubleQuoted` escaped `\`, `"`, and control characters on each serialize while the parser only stripped the outer quote delimiters, so every read-modify-write cycle doubled existing escapes (2ⁿ−1 backslashes after n cycles). `syncStateFrontmatter` carries `last_activity_desc` through that seam on every state command, growing STATE.md unboundedly — the reported 134 MB file OOMed `state.record-session` after 26 writes. Double-quoted scalars are now un-escaped on parse via the exact inverse of the escaper, making serialize→parse a fixed point; unrecognized escapes are kept literally so hand-authored files parse unchanged. (#3497) (#3521) +- **`/gsd:code-review` now derives the phase diff base from GSD's own commit scopes instead of a prose phrase, ending silently wrong review scopes** — the diff base fed to the reviewer file-list fallback, the SUMMARY↔diff cross-check union, the reviewer agent's `diff_base`, and the fallow `--changed-since` structural pass was greped from commit messages for the literal "Phase N" and kept the oldest match, so any prose mention anywhere in history (a planning commit deferring work "to Phase N per D-09", a doc commit using "### Phase N" as a format example) silently set the base months before the phase existed — on a real repo ~4 phases too early, inflating the reviewer's reading list ~78% with no warning — while GSD's own commits (`docs(phase-N):`, `feat(N-MM):`, `docs(N):`), which never contain the literal phrase, were never matched at all. All three derivations now anchor on the subject-line conventional-commit phase scope (both padded `06` and unpadded `6` spellings, since workflows emit the unpadded roadmap number), commit bodies can no longer capture the base, and a history with no scope-style commits fails loudly with the existing no-base warning and `--files` escape hatch instead of silently picking an arbitrary commit. (#3503) (#3526) +- **`uat_path` is now pinned to the phase's own UAT artifact instead of being picked by unsorted directory-listing order** — both `uat_path` projections (`init plan-phase` and `init phase-op`) selected the phase's `*-UAT.md` with a bare first-match `.find()` that had no phase-membership check and no ordering, so a stray or cross-phase `04-UAT.md` sitting in phase 03's directory could become phase 03's `uat_path`, and which file won was filesystem-dependent (creation order on APFS, hash order on ext4/XFS) — meaning two machines on the same commit could emit different `uat_path` values for the same phase, sending downstream workflows to read another phase's UAT state. Both sites now route through a shared phase-pinned resolver (`resolveUatFile`, sibling of the `resolveVerificationFile` rule from #3357/#3492): the phase's own `-UAT.md` always wins, otherwise the alphabetically-first dashed candidate, deterministically on every machine. (#3518) (#3525) +- **MemPalace sub-features whose defaults are enabled now run when their config keys are absent** — the earlier `capture_artifacts` absent-key fix (#2982) had been applied to only one of six hand-written config gates; the remaining gates for `mempalace.mirror_kg` (knowledge-graph mirroring in the capture and recall skills, their command mirrors, and the curator agent) and `mempalace.diary_journal` (per-agent diary entries at ship) still required the key to be explicitly present and `true`, so a project that enabled MemPalace without writing every sub-toggle silently never mirrored KG facts or wrote diary entries, with no warning. All six gates now treat an absent key as enabled (matching the capability registry's declared `default: true`) and disable the behavior only on an explicit `false`; default-off switches (`mempalace.enabled`, `cross_project_tunnels`) still require explicit opt-in, and a registry-parity regression test keeps future default-true keys from reintroducing the inversion. (#3479) (#3527) +- **Roadmap `Plans:` lines keep their hand-written text instead of being overwritten with a plan count** — `roadmap update-plan-progress` replaced everything after the `Plans:` label whenever the line did not already begin with a canonical `N/N plans` token, silently destroying freeform prose, a `TBD` note, or a hand-written annotation. A sentence that wrapped onto a second line lost only its first line, leaving the continuation stranded so the roadmap asserted something nobody wrote — at exit 0, in a diff that read as a routine count bump. The count is now written only over a real count token or the fresh-template placeholder, and a single-plan phase (`1 plan`) is recognized rather than frozen. (#3584) (#3635) +- **Running a capability's own test suite no longer silently deactivates it** — `bundleContentHash` digested every entry under a capability bundle with no exclusions, so ordinary Python bytecode caching (`__pycache__/*.pyc`, written by any plain `python3` run) changed the consent-binding hash. The capability then reported `inactive` with no error and no warning, and `loop render-hooks` quietly dropped its step and gate — indistinguishable from never having installed it. An *empty* `__pycache__` directory was enough to trigger it, since the digest binds directory existence. Only a `*.pyc`/`*.pyo` file sitting directly inside a `__pycache__` directory is now excluded from the digest; a `.pyc`/`.pyo` file anywhere else stays bound, since a sourceless legacy `.pyc` there is still importable and executable. A `__pycache__`/`.pytest_cache` directory has only its own marker suppressed — its contents still bind the digest normally. `node_modules` and other executable content stay bound, excluded entries still count toward the walk's caps, and the filter runs after the symlink rejection so it cannot smuggle one past. (#3631) (#3650) +- **Corrected `model-profiles.md`: `model` and `effort` do not resolve through one shared precedence ladder** — the reference previously claimed a `models[phase_type]` or `dynamic_routing` override flips both, and that an effort config change takes effect like a model change. In reality effort (claude runtime) is baked into agent frontmatter at install time and requires `node gsd-tools.cjs effort sync --apply` to change; Codex agents pin `model_reasoning_effort` in generated `.toml` files. (#3530) (#3536) +- **`requirements mark-complete` now flips the traceability row when `## Traceability` holds more than one table** — `updateTableCell` no longer binds to the first table in the section; it scans for the table that actually carries the requested column. A section with a phase-summary table above the requirement rows previously made the Status write silently bail (`table_unmatched`) while the checkbox still flipped, leaving the row at `Pending` indefinitely. Single-table sections are unchanged. (#3255) (#3377) +- **`init execute-phase` no longer hands a directory slug to the phase-start flow as the phase display name.** When a phase's working directory already exists on disk, the disk-lookup path derived `phase_name` from the directory-name remainder — itself an already-slugified value (`phase.add` writes `${num}-${slug}` dirs) — so `phase_name` and `phase_slug` came out byte-identical. The execute-phase workflow forwards `phase_name` into `state begin-phase --name`, which wrote that raw slug into STATE.md's `current_phase_name` on every phase start (`loop-termination-and-baseline-correctness` instead of `Loop-Termination and Baseline Correctness`). `init execute-phase` now prefers the ROADMAP's curated display name (`### Phase N: `) for `phase_name`, matching the no-disk fallback path that already did this correctly; `phase_slug` is unchanged so branch-name construction is unaffected. The `state begin-phase` override mechanism (#2821/#2736) is untouched. (#3171) (#3429) +- **Two GSD workflows told agents that a Claude Code `Agent()` spawn blocks until the subagent finishes** — Claude Code backgrounds subagents by default, so `/gsd-execute-phase` could treat a wave as returned when it had not, and `/gsd-debug` lost its session-manager handoff in exactly the way #2196 was filed to fix. The dispatch notes now match this package's own shipped capability matrix, and both debug spawns carry the `run_in_background: false` opt-out they always needed. (#3177) (#3281) +- Executor dispatch prompts now state checkpoint gate semantics: gate="blocking" (the default) is auto-approvable in auto-mode, only gate="blocking-human" always surfaces to a human. The phase-level and single-plan-level orchestrators no longer leave room to compose dispatch text that refuses auto-approval, which stalled autonomous runs at ordinary blocking checkpoints. (#3478) +- **`gsd-tools validate health` no longer flags `.planning/WINDOWS.md` as an unrecognized file** — the broken-windows ledger that gsd-core's own `windows` command writes is now registered as a canonical `.planning/` artifact. Previously the W019 warning advised archiving or deleting a file that, with `workflow.windows_enforce` on, gates `/gsd-ship`. (#3224) (#3369) +- gap-analysis check gap-analysis.plan-post no longer reports prose trailing the requirement ID list as missing requirements. ROADMAP Requirements lines routinely carry locked-decision annotations, ambiguity scores, and prohibition notes after the ID list; passing that value verbatim into --phase-req-ids previously caused every prose word to be reported as an individually-missing requirement, drowning the real coverage signal. Tokens that cannot be requirement IDs (prose, punctuation, dates) are now dropped after range expansion. (#3438) +- state.patch now reports a field as updated only when its post-write on-disk value matches the requested value; fields the write pipeline re-derives away (e.g. current_phase, current_phase_name) are reported as failed instead of phantom updated (#3487) +- **User profile and dev-preferences files are no longer lost when an install or uninstall is interrupted.** These files were held only in memory while GSD deleted and rebuilt the directory containing them, so pressing Ctrl-C — or any crash during the copy — destroyed them permanently. On the main install path that window spanned the entire gsd-core tree rebuild. They are now staged to disk before anything is deleted, and any copy orphaned by an interrupted run is restored automatically on the next install or uninstall. (#1874) (#3600) +- **`/gsd:code-review-fix --auto` now commits the converged REVIEW.md alongside REVIEW-FIX.md and reliably commits REVIEW-FIX.md at all** — the --auto re-review loop overwrote REVIEW.md every iteration but the workflow's single docs commit staged only REVIEW-FIX.md, so the committed REVIEW.md stayed at iteration 1 and contradicted the committed REVIEW-FIX.md (and the converged REVIEW.md plus .iterN.md backups survived only as uncommitted working-tree state). Separately, the two inline frontmatter validators exported REVIEW_PATH into a node -e body that reads process.env.FIX_REPORT_PATH, so the status check was always empty and REVIEW-FIX.md was never committed (the user was wrongly told the agent produced malformed output). The validators now export FIX_REPORT_PATH, the --auto commit stages REVIEW.md too, and spent .iterN.md backups are removed on successful convergence (retained on degradation). Non-auto single-pass runs are unchanged. (#3190) (#3434) +- **Three shell guards that could never fire now do** — the planner's Walking Skeleton mode never activated on any project, phase planning recorded an empty requirement list instead of `TBD`, and completing a milestone with no phase summaries could hang instead of finishing. Each read a value that came back empty on success, so the fallback written to handle it was unreachable. (#3409) (#3558) +- **Shipped workflow/agent citations resolve again** — 43 backticked `references/.md` cites across 19 shipped files were dead pointers from every install location; all repaired to the canonical `gsd-core/references/.md` form, and a new sweep gate fails the build on any future bare cite across the runtime-loaded trees. (#3576) (#3596) +- A genuinely milestone-sectioned ROADMAP whose STATE.md asserts a milestone token matching no heading no longer has progress.total_phases clobbered to the on-disk phase-directory count (e.g. 25 -> 4) on every state-mutating command. The stored total is preserved (or the key omitted when nothing is stored), a stderr warning names the unbounded milestone token, and progress.percent stays withheld as before. (#3480) +- Phase-directory collisions in .planning/phases/ (two in-scope dirs normalizing to the same phase number) no longer resolve by filesystem mtime — a checkout-order signal that made progress.total_plans and completed_plans differ across clones of the same commit. The survivor is now chosen deterministically by lexicographic directory name, and the collision is surfaced as a stderr warning naming both directories. (#3486) +- code-review: every phase diff-base derivation now uses the same anchored, POSIX-portable phase-mention grep. Fixes wrong review scope from /gsd:code-review when a phase has no SUMMARY artifacts: the reviewer diff_base and the fallow --changed-since base no longer resolve to old unrelated commits whose messages merely contain the phase digits, and the anchored search now actually matches on macOS (the previous \b word boundary is not POSIX ERE and silently matched nothing there). (#3437) +- **`phase add` no longer files new phases inside archived roadmap history** — the insertion point used the file's last horizontal rule, which on a long roadmap sits deep in shipped/archive content, so new phases landed under an unrelated archived phase's heading instead of at the end of the active phase list. Insertion is now scoped to the current milestone. (#3163) (#3400) +- **Agent isolation guard enforces on multi-runtime machines** — the isolation guard (and Cursor's subagent-start fallback) resolved the project runtime from the host-wide ~/.gsd/defaults.json, which names whichever runtime installed last; on machines with two runtimes this confidently picked the wrong runtime and silently disabled executor worktree policing. Both now read the per-install .gsd-runtime marker above that file. (#3566) (#3589) +- **`state update-progress` no longer writes two different completion percentages in one call** — the verb printed plan throughput (summaries/plans) to stdout and into the body `Progress:` bar, while the same write independently derived the frontmatter `progress.percent` as the deliberate `min(plan, phase)` cap. Mid-phase, when plan throughput runs ahead of phase completion, STATE.md contradicted itself and `state json` disagreed with the command that had just written it — silently, at exit 0. All surfaces now derive from the single canonical computation, and its reported plan counts come from the same milestone window as the percent, so the verb's own output can no longer disagree with itself. When that computation withholds a percent, the verb withholds too rather than substituting a different metric. The min-cap definition is unchanged. (#3583) (#3634) +- **GSD skills no longer override the caller's effort level** (#3151) — invoking `/gsd-plan-phase`, `/gsd-execute-phase`, `/gsd-autonomous`, `/gsd-next`, `/gsd-progress`, or `/gsd-stats` previously set `output_config.effort` to a static value baked into the skill frontmatter; when that differed from the session's effort (which it did ~76% of the time), it invalidated the entire prompt cache at both scope boundaries (skill entry and exit). These skills now run at the session's existing effort level (no `effort:` emitted into SKILL.md). The elevated-effort intent is preserved on the source command files; only the skill-frontmatter emission is dropped. The separate agent-effort surface is unaffected. (#3425) +- **Global OpenCode/Kilo installs no longer pin a tier-default model over your session selection** — a project's `model_profile: "inherit"` was invisible to the install-time resolver on global installs (it probes from the install dir and never reaches the project), so the `balanced` default silently baked e.g. `anthropic/claude-opus-4-8` into the agent frontmatter, which those runtimes use over the live `/model` selection — producing "Model not found" on providers without that exact id. A profile that cannot be verified now bakes no `model:` line, so subagents follow the session model as documented; declare `model_profile` in `~/.gsd/defaults.json` to pin tiers machine-wide. (#3543) (#3563) +- **`phase remove` no longer corrupts STATE.md after removing an inserted (decimal) phase** — the removed-phase write prepended a second, partially-wrong frontmatter block (and left the phase's ROADMAP heading behind, so total_phases kept counting it); removal now updates STATE.md in place as a single block, drops the heading, and clamps phase counts at zero. (#3572) (#3594) +- **`validate health --backfill` now works without also passing `--repair`** — previously it silently did nothing unless `--repair` was also set, due to an unreachable internal gate. (#3405) +- gsd-health's STATE/ROADMAP staleness warning (W011) now reads the current phase from the YAML frontmatter format gsd-tools itself writes (current_phase), in addition to the legacy prose, canonical body, and pipe-table forms, and suppresses the warning when the recorded status reports completion in the state writer's own vocabulary (status: completed). The stale-worktree warning (W027) no longer advises unconditional forced removal: its remediation now directs checking for uncommitted work first (git -C status --porcelain), removing non-destructively when clean, with --force presented as an explicit opt-in to discard changes. (#3452) +- **Completing one phase no longer marks the whole milestone done** — `state complete-phase` wrote the body prose `Phase N complete`, and the status normalizer matches `complete` as a substring, so finishing phase 2 of 4 collapsed the milestone-level STATE.md frontmatter to `status: completed` while the very same call correctly recorded `completed_phases: 2` of `total_phases: 4`. Downstream automation that gates on milestone status — auto-advance, archival, ship gating — was told a half-open milestone was finished. Milestone status is now derived from those counters instead of from phase-level prose. (#3578) (#3614) +- **`progress`, `stats`, and `query progress` now report a real percentage inside a workstream** — under `--ws`, these commands counted the workstream's own phases and plans but read the milestone window from the project root, which `workstream create` has already migrated away. The scope resolved as unreadable and the percentage was withheld, so a fully-complete workstream reported no progress at all. **`milestone complete` no longer archives every phase directory when its milestone window is unreadable** — it previously fell back to moving everything on disk in that case; it now declines to archive and reports why, leaving the phase directories in place. (#3597) (#3607) +- **`gsd-tools stats` no longer counts phantom phases from inline code** — prose mentioning `### Phase N:` inside an inline code span (e.g. a roadmap explaining its own numbering) inflated phases_total with a never-completing Not-Started row and deflated completion percent; stats now requires the same digit-bearing phase id shape roadmap analyze uses, so the two agree. (#3569) (#3591) +- parseDeferredItems now counts heading-delimited deferred items as ONE entry (a heading plus its descriptive sub-bullets) instead of one per bullet, across flat, container-heading, and mixed-depth files; headless one-bullet-per-item files are unchanged. A bolded `- **Status:** resolved` marker now resolves its item instead of surfacing as a bogus unresolved entry. (#3488) +- **`state.record-session` no longer shrinks your phase count** — a project whose ROADMAP declares more phases than it has directories on disk (phases 5 and 6 planned but not started yet) had `progress.total_phases` silently overwritten with the directory count, converging on the right number only once the last phase directory happened to exist. A flat roadmap carrying an ordinary heading like `## Progress` was being misread as milestone-sectioned. Known limit: two milestone sections carrying no version token, no status marker and not the word "Milestone" are still not detected as sectioning. (#3204) (#3230) +- Parallel phases running in the same working tree no longer corrupt STATE.md silently: state.begin-phase, state.advance-plan and phase.complete now consult a milestone claim (.planning/milestone.lock) keyed by phase + session id, and surface a visible milestone_conflict warning (stderr plus a typed JSON field, and phase.complete's warnings[]) when another live session holds a different phase — instead of silently overwriting the single Current Position slot. (#3455) +- **`/gsd-map-codebase --fast` now actually runs the fast scan** — the flag routed to "the scan workflow" in prose but named no path any runtime could resolve, and the command loaded only the full four-agent map workflow, so `scan.md` was never read and the single-agent scan was improvised rather than executed. (#3561) (#3562) +- **State validation properly detects drift** — Resolved an issue where state validation would silently fail to detect drift because it skipped scanning entirely when the shipped template lacked a specific field. (#3208) +- Phase writes now guard the current milestone's scope. phase add/add-batch/insert reject a description containing a level 1-3 heading with a milestone marker (version token, status marker, or the word Milestone) before anything is written, and the edit-phase workflow captures roadmap milestone-scope (new read-only probe) around its in-place section write and rolls the edit back with an explicit error if the milestone window's scope or phase set changed. (#3446) +- **`/gsd-quick` and the UAT-diagnosis step no longer abort with a FATAL on a non-Claude runtime that can actually isolate** — both dispatch sites resolved worktree isolation from a hardcoded `RUNTIME != "claude"` test, so every non-Claude host was refused regardless of what it could actually do. They now read the negotiated `dispatch.isolation` capability (#2584), and installs for runtimes that declare worktree support no longer stamp `workflow.use_worktrees` to `false`, which had pre-empted that negotiation. A runtime is judged by what it declares rather than by its name. A host that declares no isolation primitive at all still fails closed when worktrees are explicitly enabled — that FATAL is the fail-closed contract, not the bug — and a host whose isolation model the single-agent sites cannot express degrades to sequential, one agent at a time, on the main working tree. (#2728) +- Reviewer lanes that declare source-grounded evidence are now verified at run time: a review citing zero file:line source evidence is stamped [reviewed-without-source-citations] and down-weighted in the Consensus Summary, instead of silently riding its declared evidence class at full weight (gemini plan-only reviews were measured doing exactly this). (#3436) +- Managed /gsd:debug auto-resume no longer stalls after an answered checkpoint: the respawned session manager now receives the recorded next action and checkpoint status, plus the disposition that prior checkpoints were already answered, so the debug loop proceeds on the persisted next step instead of stopping behind the no-progress guard. (#3476) +- **Concurrent Claude Code sessions no longer share one active-workstream pointer** — Claude Code exports its session id as `CLAUDE_CODE_SESSION_ID`, but the session-identity probe only listened for `CLAUDE_SESSION_ID`, so session-scoped workstream isolation never engaged on Claude Code: every session in a working tree resolved through the single shared `.planning/active-workstream` pointer, and a `STATE.md` update belonging to one workstream could be written silently into another's directory. The probe now accepts `CLAUDE_CODE_SESSION_ID` (no other key's precedence changed); concurrent sessions each keep their own session-scoped pointer again. (#3557) (#3570) +- plan-phase: a completed --gaps planning run's Next Up handoff now recommends /gsd:execute-phase --gaps-only (matching the gap-closure scope just planned) instead of the whole-phase /gsd:execute-phase . Standard and --reviews runs are unchanged. (#3453) +- **GSD no longer commits `.planning/` files you told it to ignore** — several workflow steps staged planning artifacts with raw `git add`, bypassing the `commit_docs` setting and the `.gitignore` auto-detect entirely, so planning docs reached shared history anyway. (#3585) (#3590) +- **Global Claude installs load skill content correctly again** — the installer rewrote `@~/.claude/` file references to `@$HOME/.claude/`, which Claude Code does not expand, silently leaving every GSD skill with an empty execution_context (the model got scaffolding but never the workflow body). @-references now stay on the tilde form Claude resolves. (#3133) (#3393) +- **Twenty-five folded test suites no longer run twice on every CI lane** — three consolidated install suites each carried a verbatim second copy of a contiguous run of folded regression blocks (~5,800 lines), left behind by a stale-base re-application during the test-consolidation epic. Every duplicated block registered and passed twice, so nothing reported it, and a contributor fixing one of those regressions could edit one copy and leave the other asserting the old behavior with the suite still green. The duplicates are deleted, and a new `local/no-duplicate-fold-marker` ESLint rule fails the build if a folded suite ever appears twice in one host file again. (#3271) (#3285) +- **Items left unresolved when a milestone closes are no longer invisible to every later audit** — `query audit-open`'s four phase-scoped scanners read only `.planning/phases/`, so once a milestone closed and its phase directories moved to `.planning/milestones/vX.Y-phases/`, any UAT gap, verification gap, context question or deferred item still open at that moment vanished from the pre-close audit permanently. In a fully-archived project the scanners returned nothing at all, which is indistinguishable from a clean tree — and because the audit sums every category into one `has_open_items` boolean, that could report a clean close it had not verified. All four now scan the archived milestone directories as well, and each item says which milestone it came from. (#3458) (#3555) +- **`roadmap` tools recognize table-style phase listings** — a ROADMAP whose current-milestone phases are declared as markdown table rows (`| 20 | … |`) reported phase_count: 0 and found: false across roadmap.analyze, roadmap.get-phase, init.phase-op, and the milestone filter; all four surfaces now resolve table-declared phases (progress tables and fenced examples excluded). (#3577) (#3599) +- **A terminal session now follows the workstream your repo says is active** — with `.planning/active-workstream` naming a workstream, any invocation that had never run `workstream use` silently resolved the flat `.planning/` tree instead: it misreported milestone, phase and progress on reads, and wrote to the superseded flat `STATE.md`. Because the stale tree is well-formed, nothing warned, and the documented workaround was to prepend `GSD_WORKSTREAM=` or `--ws` to every command. A session that has never set its own pointer now inherits the repo marker. Session isolation is unchanged — a session that owns a pointer is never repointed — and the two workstream-mode fail-safe guards now say whether a marker exists but failed to resolve, instead of claiming none is set. Note that clearing a session's pointer returns it to inheriting the marker rather than forcing flat mode. (#3579) (#3616) +- **`branching_strategy: "phase"`/`"milestone"` once again lands the first strategy-scoped commit on the strategy branch** — `gsd-tools query commit` now creates *and* switches to a brand-new phase/milestone branch (restoring the #1278 intent), instead of creating it without switching and leaving the commit on the base branch. The #3079 protection is preserved: an *already-existing* strategy branch is still never silently switched to (it warns and commits on the current branch). The first fresh create is now logged to stderr instead of being silent, and the misleading "already exists" warning no longer recurs on every subsequent commit once HEAD is on the strategy branch. (#3207) (#3363) +- **A file belonging to another phase no longer blocks the phase you are in** — sixteen scans (plus the single-pick fallback inside `resolveVerificationFile`) collected verification and UAT artifacts from a phase directory without checking they belonged to that phase, so a stray or copied file such as `04-VERIFICATION.md` sitting in phase 03's directory contributed its status to phase 03. The worst case was not cosmetic: a stray file carrying `gaps_found` or `human_needed` pushed a blocker that flipped the UAT-passed predicate to false, and `transition` gates on that — so a leftover file could refuse to let a phase advance. Some scans could also claim the opposite, reporting verification passed on the strength of a file the phase does not own. All of them now check phase membership. Where a directory's own phase cannot be determined from its name, every file is still included, so no scan silently loses a phase's real blockers; where it can, a phase holding only another phase's report now correctly reports having none of its own rather than adopting it. (#3511) (#3535) +- **The build no longer requires Node 24: `escapeRegex` falls back to an in-file metachar escape when `RegExp.escape` is absent** (#3498) — `RegExp.escape` is ES2026 (Node 24+), and `src/pattern.cts` called it unconditionally, so `npm run build` itself failed on Node 22 (`gen-loop-host-contract` consumes the module), breaking the gsd-test `linux-node22` verification lane. The seam now prefers the built-in when present and falls back otherwise — still the single owner of escaping (#3212 invariant preserved). Behavior on Node 24+ is unchanged; match behavior below Node 24 is verified equivalent by regression tests that neuter `RegExp.escape` in a child process. (#3499) +- **Full-line `#` comments in `.planning/STATE.md` (and every frontmatter surface) now survive a mutating write** — `parseYamlRegion` carries column-0 comments through to `reconstructFrontmatter` via a Symbol-keyed channel, and `syncStateFrontmatter` propagates that channel across its fresh-rebuild of the frontmatter object, so a comment like `# NOTE: current_phase is hand-maintained` is no longer silently destroyed on the next `state` verb. Comment-less frontmatter is unchanged; data identity (keys/values/arrays/nested) is preserved alongside the comments. (#3257) (#3387) +- A plan SUMMARY whose frontmatter declares status: blocked is no longer counted as a completed plan. Previously both the progress counters written to STATE.md (state planned-phase / begin-phase / record-session) and the phase-plan-index read path paired PLAN and SUMMARY files by filename existence alone, so a blocked plan counted as done and was omitted from the incomplete list. Filename existence remains the fallback when a SUMMARY carries no status field, and status: halted summaries still count as completion records (a designed stop), so untouched projects are unaffected. (#3459) +- **Auto-chain phase completion now runs the same post-processing as a normal transition** (#1526) — completing a phase via `/gsd:execute-phase` (auto-chain) previously skipped the transition workflow's graduation scan, session-continuity, project-reference, accumulated-context, and current-position updates, leaving project state different from a normal transition. execute-phase now delegates post-completion processing to the transition workflow (post-completion mode: skips re-verify + re-running `phase.complete` to avoid a double-write). Identity/standalone transition behavior is unchanged. (#3419) +- **Capability skill bodies are now documented as an instruction surface** — the capability trust model previously grouped skills with inert assets as "non-executable" surfaces whose consent is lighter *because they do not execute code*. A skill body does not execute code; it instructs the agent that does. The docs now state that a capability's SKILL.md bodies reach your agent's instruction context verbatim and are not content-scanned, and capability authors are told the same on the authoring side. No behavior changed and no existing consent was invalidated. (#3247) (#3249) +- **A capability's `ship:pre` gate now actually blocks the ship** — the ship preflight resolved every declared `ship:pre` gate but enforced only the built-in `security` and `broken-windows` capabilities, so any other capability's `blocking: true` gate was resolved, evaluable, and then silently dropped: a phase shipped past its own declared failing condition with nothing evaluated, nothing warned, and nothing logged. Preflight now dispatches every active gate generically — honoring each gate's own `blocking` and `onError` — matching the contract `execute:wave:post`, `execute:post` and `plan:post` already implement. (#3559) (#3608) +- package-legitimacy-gate tests now locate the executor RULE 3 section by its heading inside the deviation rules block, so unrelated prompt edits can no longer redirect or silently defeat the package-install guardrail assertions (#3489) +- **Known-defect warnings that lived only in docs are now enforced checks** — six failure modes that `CONTEXT.md` merely described are now caught automatically, including unbounded subprocesses that could hang a run indefinitely and an unscoped frontmatter read that could pick up a body line. Writing the checks surfaced nine live instances, all fixed. (#2896) (#3325) +- The `init phase-op`, `init plan-phase` and `init execute-phase` queries no longer hand consumers a fully-formed absolute path for a `REQUIREMENTS.md`/`STATE.md`/`ROADMAP.md` that does not exist. Those three fields were built with a bare path.join and no existence check, so a non-null value was indistinguishable from the file actually being there — even as the conditional sibling fields in the same payload (`patterns_path`, `context_path`, ...) already returned null for absent files, and `ultraplan-phase.md` explicitly gates its REQUIREMENTS.md read on `requirements_path is not null`. Each of the three reading sites now returns null when the file is absent and its absolute path when present. The project/milestone-bootstrap and doc-ingest emitters that use these paths as write-targets for not-yet-created files are intentionally unchanged. (#3188) (#3430) +- phase.complete no longer rewrites STATE.md frontmatter stopped_at with a stale body 'Stopped at:' line: the completion now refreshes the session continuity line it implies ('Phase N complete, ready to plan Phase N+1') and applies the standard field-preservation policy on its atomic commit path. state record-session no longer reports 'Stopped At' as updated when the value is already current. (#3491) +- Cross-AI reviewer lanes now run on Windows: the declared bare CLI name is resolved through one shared PATH+PATHEXT lookup before spawning, so npm-installed .cmd/.bat shims start via cmd.exe mediation instead of failing with spawn ENOENT (#3275). (#3445) +- Six STATE.md frontmatter fields whose preservation policy is declared in the field-classification table were not honored by the table-driven preservation pass — `last_activity_desc`, `paused_at`, `current_phase`, `current_plan` (preserve-when-unchanged) and `milestone`, `milestone_name` (preserve-if-placeholder). The pass now implements every declared row, so editing a preservation row is a one-row table edit as the table's own contract documents. Curated frontmatter values for `paused_at` / `current_phase` / `current_plan` now survive a body-only write (e.g. `state update`) even when the body carries a stale-but-present derived value — previously only an absent derived value triggered the fallback, so a stale body value silently overwrote the curated frontmatter value. `last_activity_desc` is now governed by a single rule (the table row) rather than a separate date-comparison guard that could disagree with it. (#3258) (#3447) +- **`state` writes no longer shrink progress.total_phases to the started-phase count when ROADMAP.md is absent** — with no readable roadmap, every state command persisted the on-disk phase-directory count as the declared total (only phases that had started counted, so a 5-phase project read 50-100% complete with 3-4 phases unstarted); the stored frontmatter total now wins, with a warning, and `state json` reports the same preserved value. (#3573) (#3595) +- ui-plan-gate no longer blocks planning on a UI-token match alone: the gate now requires static frontend evidence (a package.json UI-framework dependency or a component-framework file in the tree) before blocking, so a phase section naming a hyphenated repo like dashboard-financeiro no longer trips the gate in a repo with no frontend. The gate result also surfaces matchedToken/matchedLine so operators can see what triggered the flag. (#3451) +- Windows/Claude Code: /gsd-update and re-running the installer now migrate stale `.sh` managed hook commands (gsd-validate-commit, gsd-graphify-update, gsd-session-state, gsd-phase-boundary) in settings.json/settings.local.json to the current bash-runner-omission format — removing the redundant nested bash that the pre-#580/#3393 shape spawns on every hook fire. Custom user hooks are never touched. (#3460) +- **`gsd-tools query state update-progress` no longer rewrites Progress to 0% after a milestone close** — when the current-milestone phase scan finds zero plans (the post-archive state, where `.planning/phases/` is empty), the command is now a no-op that leaves STATE.md unchanged, instead of mapping 0/0 to 0% and destroying the shipped `[██████████] 100%` record. The legitimate 0% case (plans exist, none summarized) still writes 0%. (#3233) (#3375) +- **STATE.md preservation now enforces every policy its own table declares** — a field could be declared `preserve-when-unchanged` and quietly go unenforced, because the executor branched on field names rather than on the declared policy, so four of eight rows were honored by a weaker mechanism elsewhere and two policies had no implementation at all. Preservation is now dispatched from the classification table, a declared row nothing enforces fails loudly instead of silently, and a whitespace-only curated value is no longer treated as a real one. (#3468) (#3495) +- state planned-phase now refreshes the Current Position Phase: line (the body source current_phase is re-derived from) instead of leaving a stale previous-phase line behind, so STATE.md frontmatter, body prose, and state json stay coherent; the --name argument is persisted into the Phase line and current_phase_name instead of being silently dropped. (#3490) +- Executor dispatch prompts no longer list companion files as raw @-include lines that Claude Code never expands inside an Agent() prompt string. The orchestrator now build-time embeds execute-plan.md and its companion references (summary template, checkpoints, tdd, worktree-path-safety, executor-examples) into the dispatched gsd-executor prompt, so execute-plan-only steps (segment_execution, previous_phase_check, verification_failure_gate, update_codebase_map) actually reach executors instead of silently never running. (#3462) +- roadmap validate now emits a V005 warning and exits non-zero when the active milestone's window is truncated — phase entries exist in ROADMAP.md but are excluded from the milestone's resolved section (e.g. an intervening version-bearing heading closes the window before its own Phase sections). Previously this passed silently with {"warnings":[]}. (#3444) +- **`/gsd-plan-phase`'s §13a Decision Coverage Gate no longer reports false total-coverage failures when a decision's own body contains a bulleted cross-reference to a sibling decision** — a bullet nested (indented) under an already-open decision, elaborating on how it relates to another decision, was previously indistinguishable from a malformed top-level declaration attempt. A single such bullet forced the whole coverage analysis to `could-not-parse`, discarding every decision that DID parse correctly and reporting `covered: 0` even when every decision was, in fact, fully covered by the phase's plans. (#3169) (#3424) +- ZCode installs now strip mcp__* tool grants from installed GSD subagents at install time. ZCode's dispatcher treats every mcp____* entry in an agent's tools: frontmatter as a required MCP server and hard-fails the subagent spawn (CONFIGURATION_ERROR) when it is not connected, so /gsd-quick --full and plan/execute-phase flows failed out of the box with zero MCP servers configured. Installed ZCode agents now declare only core tools; MCP tools remain available when servers are connected. Claude Code installs are unchanged. (#3483) +- **`/gsd-sync-skills` now refuses cross-runtime skill sync** — skill content and directory layout are runtime-specific (the installer applies per-runtime converters/adapter headers/brand swaps/layout rules), and two runtimes alias another runtime's skills root, so a verbatim cross-runtime copy silently corrupted destination skills and could overwrite a runtime the user never named. sync now refuses any `--to` that differs from `--from` and points at the installer, keeping identity sync (`--from` == `--to`) as a no-op. (#3025) (#3404) +- **`milestone.complete` no longer records the wrong line as a release's accomplishment** — the one-liner was extracted from the first bold text under the SUMMARY's first heading, so an incidental first heading (a rule list, deviation notes) could contribute `Rule 1 - Bug` or `NeutralPath` as the milestone's permanent accomplishment in MILESTONES.md. Extraction now anchors to a Summary/Overview/Accomplishments heading and falls back to empty when none is present. (#3170) (#3401) +- **ESLint now actually runs on 56 previously-unlinted source files** — a file matching no `files:` glob was not linted-and-clean, it was skipped entirely while `eslint .` still exited 0. All of `hooks/` and `eslint-rules/` sat in that blind spot. A new drift guard fails the build if any tracked source file resolves to zero rules without a recorded reason, so the class cannot silently regrow. (#3059) (#3277) +- **`gsd-tools validate health` and `validate consistency` no longer flag sentinel phase directories (999.x backlog/interim, 0.x drafts)** — the disk-vs-roadmap comparison now applies the `isSentinelPhaseId` guard that the phase commands already had. Sentinel ids are defined as never-on-roadmap, so a `999-interim` directory previously produced a permanent spurious W007 ("Phase 999 exists on disk but not in ROADMAP.md", advice to add it to the roadmap or delete it — both wrong) and a spurious "Gap in phase numbering: N → 999". Real (non-sentinel) orphans and genuine numbering gaps still warn. (#3225) (#3371) +- **`roadmap.analyze` now reports the real phase count instead of a silent `phase_count: 0`** when a CLOSED milestone heading sits between the active milestone heading and its own phase-detail sections. A prior refactor (#3184) already added a `scope` discriminator so the empty result was distinguishable from a genuinely empty milestone; this closes the other half of the issue — the consuming resume gate (`workflows/next.md` Route 0) iterates `.phases[]`, so an empty array silently disarmed the safety invariant regardless of the scope field. When the scoped window comes back empty, is non-COMPLETE scope, and phase directories exist on disk, the query re-scans the shipped-milestone-stripped document and populates the phase list while keeping `scope` non-COMPLETE so the result remains flagged as best-effort. (#3165) (#3428) +- **The 1.4.0 changelog entry for Cursor slash commands now credits the PR that shipped it** — the entry describing `gsd install --cursor` writing `.cursor/commands/` cited #803 (the Cline PR, which the adjacent entry cites correctly) instead of #805, so anyone tracing the Cursor commands surface landed in an unrelated change. (#2359) (#3252) +- **Settings no longer offer worktree isolation on runtimes that cannot honor it, and health warns before execution fails closed** — previously `/gsd-settings` recommended "Yes" and persisted `workflow.use_worktrees: true` on every runtime, handing installs whose declared `dispatch.isolation` capability is `none` the exact value `/gsd-execute-phase` and `/gsd-quick` fail closed on. On those runtimes the Worktrees question now offers "No (Recommended)" / "Leave unchanged" (never an enabling option), warns when the config carries an inherited explicit `true`, and `/gsd-health` surfaces such a config as new warning W025 with a DEGRADED status before execution-time failure. Runtimes that declare `harness-worktree` or `orchestrator-worktree` are unaffected — the gate is the declared capability, never the runtime name. Both surfaces resolve isolation through the new `inspect-dispatch-isolation` query, a sentinel-free sibling of `dispatch-isolation`: the dispatch verb records its decision to the executor-isolation sentinel by design, which a read-only diagnostic must never trigger. The inspection verb rejects `--force-isolation`, `--phase` and `--plan` as usage errors rather than accepting and ignoring them — the recording verb applies `--force-isolation` after resolution, so silently ignoring it would hand the same argv two different answers. Both surfaces also distinguish "this runtime declares no isolation primitive" from "the capability could not be resolved", and say which one happened instead of reporting a resolver failure as a capability verdict. (#2486) (#2531) +- The /gsd slash command in Pi now visibly renders its output (progress, errors) via Pi's ctx.ui.notify mechanism instead of a return value Pi silently discards. (#3485) +- **The optional pre-commit hook now actually checks command-alias drift** — every guard in `.githooks/pre-commit` was inert: nine matched paths under the retired `sdk/` tree and invoked npm scripts that no longer exist, and the tenth watched gitignored build outputs that git can never stage. Staging `src/command-aliases.cts` now runs `check:alias-drift` instead of passing silently. (#2725) (#3273) +- **`init.progress` no longer infers the next phase from stray out-of-order artifacts** — a phase directory created out of order (e.g. a phase-9 UAT evidence file while roadmap phase 8 was still pending and unscaffolded) dragged the reported frontier forward, making `init.progress` skip Phase 8 and disagree with `roadmap.analyze`; the frontier is now derived from roadmap order, with artifacts as corroborating evidence only. (#3581) (#3603) +- phase complete now selects the lowest genuinely-outstanding lower-numbered phase as next_phase instead of a merely-positionally-next higher phase heading, and keeps STATE.md frontmatter current_phase and current_phase_name paired (both describe the same phase) even for narrative-prose STATE.md files (#3482) +- planning-config.md documented "light" as an allowed workflow.code_review_depth value, but config-set only accepts quick/standard/deep — the reference now matches the validator, pinned by a doc↔capability-registry parity test. The agent_skills row now also documents the array-of-strings form for assigning multiple skill sets to one agent type. (#3449) +- **Documentation no longer points at files that were renamed or deleted** — `docs/INVENTORY.md` claimed its roster was anchored by six drift-control tests when five had been deleted, and the four translations named a seventh that the English file had already dropped. `CONTEXT.md`, `VERSIONING.md`, `docs/CONFIGURATION.md` and `docs/skills/discovery-contract.md` pointed at `issue-NNN-` test filenames and `sdk/` paths that no longer exist, and `VERSIONING.md` described an SDK bundling step the release workflow does not perform. Most consequentially, `docs/TESTING-SUITES.md` instructed contributors to add drift acknowledgments to a file `CONTRIBUTING.md` says to never use — following it put the entry where the contributing guide forbids. (#3620) (#3658) +- Workflow-backend waves (claude-orchestration, BETA) no longer strand executor commits on worktree-wf_* branches: the emitted Workflow script now returns each agent's worktree metadata, and the orchestrator records it into the wave manifest so the existing merge-and-cleanup step lands every plan's commits. Missing metadata now halts the wave loudly instead of reporting success with an empty worklist. (#3450) +- **Global Claude Code installs now load their referenced workflow context** — `gsd-core/workflows/*.md` and other spec-tree files previously emitted `@$HOME/.claude/...` `@`-file-references, a form Claude Code's `@`-import resolver silently drops (only `~/` and absolute paths resolve). Every such reference now resolves on `~/`, matching the already-working skill/command surface; double-quoted shell $HOME references are untouched. (#3544) (#3551) +- **A phase with more than one `*-VERIFICATION.md` no longer reports the wrong one** — verification-report discovery took the alphabetically-first match, so an ad-hoc worksheet such as `03-CORRECTION-VERIFICATION.md` beat the real `03-VERIFICATION.md` sitting beside it and the phase could report `missing` while a passing report existed. Three further copies of the same lookup picked whichever file the filesystem happened to list first, making phase status and the reported `verification_path` vary between machines. All five now share one resolver that prefers the canonically-named report and is deterministic when it has to fall back. (#3357) (#3513) +- gsd-review no longer creates empty gsd-review-context.md / gsd-review-research.md section files (or hangs waiting on input) when a phase has no CONTEXT/RESEARCH notes: the build_prompt guards now test the glob expansion itself instead of probing with ls, which the block's nullglob setting had made always-true. (#3454) +- **Milestone phase counts no longer drop every letter-named phase directory** — `getMilestonePhaseFilter` now includes letter-named phase directories (`Phase A:`…`Phase L:`, GSD's own non-numeric phase convention per ADR-612) in milestone progress and plan counts. A greedy regex previously captured the whole hyphenated directory name (`A-tool-output-contract` was read as `A-tool-output-contract` instead of `A`), so every letter-named phase silently fell out of its milestone and the progress/plan totals were fabricated over whatever numeric directory happened to survive — a well-formed, plausible number that could even look correct at a phase boundary. Numeric and milestone-prefixed phases are unchanged. (#3213) (#3368) +- phase-plan-index silently drops short-form depends_on references (e.g. ["01"]), collapsing every plan into wave 1. The planner template's two worked dependency examples taught exactly that broken short form; they now teach the full-form plan id (e.g. ["01-01"]) the file's own frontmatter comment and other examples already document, so newly authored plans keep resolvable dependency edges. Resolver-side short-form handling is tracked separately in #3473. (#3475) +- **Recorded why install materialization stays three loops, not one** — an architecture decision for epic #2866 phase 6. Measuring the three sites showed they diverge in mechanism rather than duplicate each other, so unifying them would have broken a prune that structurally cannot delete user files. (#3574) (#3575) +- **Dev-dependency `js-yaml` bumped to the patched 4.3.1, resolving a high-severity quadratic-CPU advisory** — the lockfile now pins the backported `!!omap` fix (GHSA-5p4m-2wfm-xmqj, CVSS 7.5), reachable via eslint. A non-breaking in-range bump (no overrides, no major bump, one package moved); production `npm audit --omit=dev` is unaffected (devDependency only). (#3238) (#3246) +- **Executor dispatch no longer blocks on a plugin-marketplace install** — the compiled runtime library is a build artifact produced at publish time and gitignored, so a plugin or git-clone install materializes a tree that never has it. Every hook that required one of those modules did so without the existing self-heal build seam that the CLI entrypoint already calls, so the agent-isolation guard's missing-module error landed in its fail-closed catch and was reported as `could not read or resolve dispatch-isolation configuration` — blocking every `gsd-executor` dispatch from the first dispatch of a session, while the statusline and update-check worker crashed at module load on the same tree. All seven affected hook files now self-heal first: the isolation guards surface the build seam's own actionable error instead of a misleading config message and stay fail-closed, and the cosmetic hooks degrade quietly rather than taking down the prompt. The guards also now emit a machine-readable `reason_code` alongside the human message. Installs from npm are unaffected — the seam's already-built fast path returns immediately. (#3582) (#3629) +- **npm-global installs can now actually fail the agents-installed gate** — `checkAgentsInstalled` resolved the claude agents directory relative to its own install location, so an npm-global install validated the package's bundled `agents/` against itself and `agents_installed` could never be `false`, silently disabling the halt/warn gates in `new-project` and `new-milestone`. When the install-relative path lies inside a `node_modules` tree the claude runtime now resolves `getGlobalConfigDir('claude')/agents` like every other runtime, honouring `CLAUDE_CONFIG_DIR`; repo runs and runtime-config-dir installs are unchanged, and the `GSD_AGENTS_DIR` override stays priority 1. (#3203) (#3229) +- **Plan files with Windows-style CRLF line endings now correctly enforce their `must_haves` contract** — `truths`, `artifacts`, `key_links`, and `prohibitions` blocks previously parsed to an empty list on any CRLF-authored plan file, silently degrading goal-backward verification to LLM-derived truths instead of the authored contract, with no error surfaced for the most common failure shape. (#3360) (#3420) +- **Installing GSD for Claude at both global and local scope no longer silently hides your project's specs.** Claude Code always resolves the personal skill over the project command, so a project with a local install previously ran the global workflow specs with no warning. The install now prints which scope wins and `/gsd-health` surfaces the same as diagnostic W028; at global scope, the winning skill's workflow reference now resolves your project's own specs first when present. (#2218) (#3537) +- **Installer no longer crashes when a source file disappears mid-copy.** `copyWithPathReplacement` used to throw an unhandled ENOENT if a listed workflow/command file was deleted between its directory listing and the actual read — a rare filesystem race that could abort an entire install. It now skips the vanished file and continues installing everything else. (#3333) (#3341) +- verify plan-structure now recognizes task child elements that carry attributes on their opening tag (e.g. ), so plans annotating verify mode (auto vs human) or other child-tag attributes no longer produce false "missing " / "missing " / etc. warnings. Bare tags continue to validate exactly as before. (#3433) + +### Security + +- **MCP server configs are now explicitly flagged as unconfined in the capability consent prompt** — a capability's MCP servers can legitimately point at commands, args, env, and working directories anywhere on the machine (unlike its hooks, which are confined to the installed bundle), and the consent disclosure now says so plainly for every spawned server instead of leaving the asymmetry unstated. (#3515) (#3517) +- **Hook security hardening — shared injection patterns + fail-closed force-add guard** — the prompt-injection pattern list is now one shared module used by both the write-guard and the read-scanner hooks, so the two surfaces can no longer silently drift apart; and the opt-in workflow guard's force-add block on agent branches now fails closed on internal error instead of silently allowing. (#3504) (#3510) +- **Capability installs no longer fetch from internal hosts, and unpinned installs say so in the consent prompt** — the URL importer refuses loopback/link-local/metadata hosts (including the cloud metadata addresses and localhost) before any bytes leave, and an http:// tarball URL fails with a clear https-only reason instead of a raw protocol error. Installs without an integrity pin now show a distinct 'NO PINNED HASH — staged unverified' line in the consent disclosure. (#3514) (#3516) +- **Path validation no longer accepts a symbolic link whose target is missing** — `validatePath` canonicalizes a path with `realpath`, and for a path that does not exist yet it fell back to canonicalizing the parent directory instead. A symlink inside the project pointing at a **non-existent** location outside it took that fallback and was accepted, while a symlink pointing at an **existing** outside location was correctly refused — a difference an attacker could use to test whether arbitrary absolute paths exist. Such a link is now refused outright. The same fallback also compared an uncanonicalized path against a canonicalized base when several leading directories were missing, wrongly refusing legitimate not-yet-created paths on any non-canonical working directory (every macOS temp directory, for one); it now canonicalizes from the nearest existing ancestor. (#3493) (#3506) +- **`verify key-links` can no longer be hung by a plan's `key_links` pattern** — the pattern was compiled straight from plan frontmatter with a backtracking engine and tested against whole file contents, so a nested-quantifier pattern such as `(a+)+$` pinned a CPU core indefinitely and stalled any `verify-phase` run that reached it. Untrusted patterns now execute on the RE2 engine, whose match time is linear in the input length, and a pattern that cannot be compiled is refused outright rather than guessed at — a refused pattern can never report a match. (#3477) (#3496) +- **A reviewer lane's `invoke` fields are now disclosed at install and bound to the consent signature** — an installed third-party capability could declare `env` on its `reviewer` lane and have those variables applied to the spawned reviewer process without that ever appearing in the consent prompt, which makes `NODE_OPTIONS=--require ./evil.js` an undisclosed code-execution path. Overlay reviewer lanes only became executable in #3062, and the disclosure did not move with them. The consent prompt now shows each `env` key and value (highlighting names that are execution primitives) and the manifest's own `defaultHost`, which the runtime uses whenever the configured host key resolves to nothing — previously such a lane displayed "(unresolved)" while still sending plan and review text to the address the manifest chose. Every other declared `invoke` field is covered by a residual, so a future field cannot repeat this. No already-installed capability is re-prompted by this change — consent is bound to the bundle's content hash, not to the disclosure signature. What changes is that an upgrade which edits any declared `invoke` field now counts as an executable-surface change and asks for consent again, where before it could alter what the lane runs in silence. (#2483) (#2493) +- **`verify key-links` no longer reads files outside the project** — `from:` and `to:` were taken verbatim from plan frontmatter and resolved with `path.join(cwd, …)`, which normalizes `../` rather than rejecting it, so a plan carried in an untrusted repository could name any file the process could read and learn from the reported result whether a supplied pattern matched its contents. Both paths now resolve through the project's realpath-based confinement seam; a path that escapes is refused without being read, reported as `path_rejected`, and never counts as verified. (#3493) (#3506) + ## [1.10.0] - 2026-08-08 ### Added