Commit Graph

4890 Commits

Author SHA1 Message Date
github-actions[bot]
33ba6ab84c fix(changeset): valid pr fields + correct migration name in 1.4.3 fragments 2026-06-09 13:38:12 -04:00
github-actions[bot]
f30c51a2a9 chore: bump version to 1.4.3 for hotfix 2026-06-09 13:30:29 -04:00
Tom Boucher
8605443bde fix(#934): reapply verifier handles missing pristine baseline post-rename (#937)
* fix(#934): reapply verifier handles missing pristine baseline post-rename

Gap 1 (verify-reapply-patches.cjs): when backup-meta.json records a
pristine_hash for a file but gsd-pristine/ has no corresponding snapshot
on disk, the verifier fell to over-broad mode and produced false
FAIL_USER_LINES_MISSING. Fix: return advisory OK_NO_BASELINE (non-blocking,
exit 0) so the verifier does not block on files it cannot reason about.

Gap 2 (new migration 004): migration 003 removed legacy get-shit-done/
runtime files but left gsd-pristine/get-shit-done/ orphan snapshots in
place. Those stale snapshots referenced get-shit-done/... key paths that
no longer match the active gsd-core/... layout. Fix: add migration
004-prune-stale-pristine-get-shit-done (NOT editing 003, preserving its
checksum — ref #670 guard) to remove all files under
gsd-pristine/get-shit-done/ as GSD-managed pristine snapshots.

Includes tests: bug-934 OK_NO_BASELINE assertions in the verifier test,
new installer-migration-prune-stale-pristine.test.cjs, updated
installer-migrations baseline-lock checksum for 004.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#934): rename migration to satisfy legacy-name guard + mark intentional path refs

Rename src/installer-migrations/004-prune-stale-pristine-get-shit-done.cts
→ 004-prune-stale-pristine-snapshots.cts so the filename no longer contains the
forbidden token.  Update .gitignore and eslint.config.mjs to track the new built
path.  Add gsd-allow-legacy-name markers to the remaining intentional uses of the
legacy path string in the migration body (lines 3 and 100) and in tests
(installer-migration-prune-stale-pristine.test.cjs lines 202 and 226; and the
baseline-lock key in installer-migrations.test.cjs:1469).  Update the baseline
checksum for migration 2026-06-09-prune-stale-pristine-get-shit-done to reflect
the two new marker comments added to its body.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 74a121bb4f)
2026-06-09 13:29:40 -04:00
Tom Boucher
16d9b2e8e7 fix(#936): run plan-phase inline in convergence; guard against nested spawner wraps (#939)
Both sites in plan-review-convergence.md that wrapped gsd-plan-phase in
Agent() (initial planning + replan loop) are now bare Skill() calls at depth 0.
On Claude Code, a depth-1 Agent has no Agent tool so wrapped plan-phase could
never spawn gsd-planner/gsd-plan-checker — the replan loop silently produced no
revised plan when HIGHs were found. Running plan-phase inline from the depth-0
orchestrator (which retains the Agent tool) restores the full sub-agent chain.

A full audit of all workflow files confirmed these two sites were the only
instances of the anti-pattern (no other workflow wraps a spawner orchestrator
in Agent() without a RUNTIME carve-out).

Added structural guard test bug-936-no-nested-spawner-wrap.test.cjs that
dynamically derives the spawner set (workflows containing subagent_type=) and
asserts no workflow wraps a spawner inside Agent() without a RUNTIME != claude
carve-out — prevents silent regression. Test passes on fixed code, would fail
on pre-fix code at the two de-wrapped sites.

Also applied two low-severity prose nits flagged in review:
- commands/gsd/plan-review-convergence.md: orchestrator role updated to
  describe inline plan-phase + Agent for review (was generic "spawn Agents")
- gsd-core/workflows/plan-review-convergence.md success_criteria: narrowed
  "Each Agent fully completes" to the review Agent (plan-phase is inline now)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit e4e8a9fcb8)
2026-06-09 13:29:40 -04:00
Tom Boucher
690f7d0c8c fix(#935): install changeset CLI so /gsd-update changelog preview works (#938)
- bin/install.js now copies scripts/changeset/ and scripts/lib/ into
  <configDir>/scripts/ so $GSD_DIR/scripts/changeset/cli.cjs resolves
  at runtime; aborts install with an explicit failure if the source
  directory is missing from the package.
- gsd-core/workflows/update.md: corrected path from
  gsd-core/scripts/changeset/cli.cjs to scripts/changeset/cli.cjs;
  added an explicit [ ! -f ] guard so a missing CLI surfaces a clear
  message rather than silently swallowing the error; stderr captured
  via 2>&1 sentinel so node errors are visible in the preview output.
- release.yml's changeset-CLI invocations (node scripts/changeset/cli.cjs)
  remain at the repo-root path and are unaffected by this change.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit dcb0d8a28d)
2026-06-09 13:29:40 -04:00
Tom Boucher
74a121bb4f fix(#934): reapply verifier handles missing pristine baseline post-rename (#937)
* fix(#934): reapply verifier handles missing pristine baseline post-rename

Gap 1 (verify-reapply-patches.cjs): when backup-meta.json records a
pristine_hash for a file but gsd-pristine/ has no corresponding snapshot
on disk, the verifier fell to over-broad mode and produced false
FAIL_USER_LINES_MISSING. Fix: return advisory OK_NO_BASELINE (non-blocking,
exit 0) so the verifier does not block on files it cannot reason about.

Gap 2 (new migration 004): migration 003 removed legacy get-shit-done/
runtime files but left gsd-pristine/get-shit-done/ orphan snapshots in
place. Those stale snapshots referenced get-shit-done/... key paths that
no longer match the active gsd-core/... layout. Fix: add migration
004-prune-stale-pristine-get-shit-done (NOT editing 003, preserving its
checksum — ref #670 guard) to remove all files under
gsd-pristine/get-shit-done/ as GSD-managed pristine snapshots.

Includes tests: bug-934 OK_NO_BASELINE assertions in the verifier test,
new installer-migration-prune-stale-pristine.test.cjs, updated
installer-migrations baseline-lock checksum for 004.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#934): rename migration to satisfy legacy-name guard + mark intentional path refs

Rename src/installer-migrations/004-prune-stale-pristine-get-shit-done.cts
→ 004-prune-stale-pristine-snapshots.cts so the filename no longer contains the
forbidden token.  Update .gitignore and eslint.config.mjs to track the new built
path.  Add gsd-allow-legacy-name markers to the remaining intentional uses of the
legacy path string in the migration body (lines 3 and 100) and in tests
(installer-migration-prune-stale-pristine.test.cjs lines 202 and 226; and the
baseline-lock key in installer-migrations.test.cjs:1469).  Update the baseline
checksum for migration 2026-06-09-prune-stale-pristine-get-shit-done to reflect
the two new marker comments added to its body.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:51:15 -04:00
Tom Boucher
e4e8a9fcb8 fix(#936): run plan-phase inline in convergence; guard against nested spawner wraps (#939)
Both sites in plan-review-convergence.md that wrapped gsd-plan-phase in
Agent() (initial planning + replan loop) are now bare Skill() calls at depth 0.
On Claude Code, a depth-1 Agent has no Agent tool so wrapped plan-phase could
never spawn gsd-planner/gsd-plan-checker — the replan loop silently produced no
revised plan when HIGHs were found. Running plan-phase inline from the depth-0
orchestrator (which retains the Agent tool) restores the full sub-agent chain.

A full audit of all workflow files confirmed these two sites were the only
instances of the anti-pattern (no other workflow wraps a spawner orchestrator
in Agent() without a RUNTIME carve-out).

Added structural guard test bug-936-no-nested-spawner-wrap.test.cjs that
dynamically derives the spawner set (workflows containing subagent_type=) and
asserts no workflow wraps a spawner inside Agent() without a RUNTIME != claude
carve-out — prevents silent regression. Test passes on fixed code, would fail
on pre-fix code at the two de-wrapped sites.

Also applied two low-severity prose nits flagged in review:
- commands/gsd/plan-review-convergence.md: orchestrator role updated to
  describe inline plan-phase + Agent for review (was generic "spawn Agents")
- gsd-core/workflows/plan-review-convergence.md success_criteria: narrowed
  "Each Agent fully completes" to the review Agent (plan-phase is inline now)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:37:32 -04:00
Tom Boucher
dcb0d8a28d fix(#935): install changeset CLI so /gsd-update changelog preview works (#938)
- bin/install.js now copies scripts/changeset/ and scripts/lib/ into
  <configDir>/scripts/ so $GSD_DIR/scripts/changeset/cli.cjs resolves
  at runtime; aborts install with an explicit failure if the source
  directory is missing from the package.
- gsd-core/workflows/update.md: corrected path from
  gsd-core/scripts/changeset/cli.cjs to scripts/changeset/cli.cjs;
  added an explicit [ ! -f ] guard so a missing CLI surfaces a clear
  message rather than silently swallowing the error; stderr captured
  via 2>&1 sentinel so node errors are visible in the preview output.
- release.yml's changeset-CLI invocations (node scripts/changeset/cli.cjs)
  remain at the repo-root path and are unaffected by this change.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:37:26 -04:00
Colin Johnson
79254478fe Merge pull request #743 from viktorplus/kimi-runtime-support
feat(#744): add Kimi CLI runtime support
2026-06-09 11:45:41 -04:00
Colin Johnson
eadcf0fa12 Merge branch 'next' into kimi-runtime-support 2026-06-09 10:27:59 -04:00
Tom Boucher
8ee66ade77 fix(#929): cmdSkillManifest discovers nested concrete skills (#933)
Scans `gsd-ns-<router>/skills/<stem>/SKILL.md` in addition to the
existing flat `<stem>/SKILL.md` layout, so gsd-health and gsd-settings
report the correct concrete skill count on nested-layout runtimes
(cline, qwen, hermes, augment, trae, antigravity).

Guard: descent into a `skills/` subdir is restricted to `gsd-ns-*`
router directories — unrelated user dirs that happen to have a
`skills/` subdir are not traversed. Dual-routed concretes (same name
under two routers) are deduped within each root.

Adds a negative-case regression test: verifies that a non-`gsd-ns-*`
dir (e.g. `my-tool/`, `gsd-settings/`) with its own `skills/`
subdir does NOT contribute nested entries to the manifest.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 10:25:55 -04:00
Colin Johnson
27aca73b6e Merge branch 'next' into kimi-runtime-support 2026-06-09 10:15:41 -04:00
Tom Boucher
6b098aa05d Merge pull request #932 from open-gsd/hotfix/1.4.2
chore: merge release v1.4.2 to main
2026-06-09 09:27:17 -04:00
github-actions[bot]
6a8b4b0ecf chore: promote CHANGELOG for v1.4.2 2026-06-09 13:25:27 +00:00
Tom Boucher
86845340dc chore(#930): remove self-masking next dist-tag repoint from release finalize (#931)
* chore(#930): remove self-masking next dist-tag repoint from release finalize

The "Clean up next dist-tag" step silently failed under OIDC trusted
publishing (which can't write dist-tags) while unconditionally reporting
success via || true + an echo. It also violated the release model by
trying to repoint @next→stable; @next is managed exclusively by the rc
job's --tag next publish.

Closes #930

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update ADR-660 to reflect removal of next dist-tag repoint

The finalize job no longer runs `npm dist-tag add … next`; update the
ADR-660 description of step 4 to match the new behavior — @next is
managed exclusively by the rc job.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 09:17:00 -04:00
github-actions[bot]
732a32ea83 fix(changelog): rephrase Claude Code parenthetical in 1.4.1 notes for #1777 purity gate 2026-06-09 09:14:28 -04:00
github-actions[bot]
3884b37b09 chore: bump version to 1.4.2 for hotfix 2026-06-09 09:07:45 -04:00
Tom Boucher
a6229ec123 fix(#921,#922): orchestrators must not fork; plan-phase Agent gate is attempt-based (#926)
`context: fork` strips the `Agent` tool from a subagent's environment.
Spawning orchestrators (`/gsd-autonomous`, `/gsd-execute-phase`,
`/gsd-plan-phase`) depend on `Agent` to dispatch sub-agents; running
them forked silently disables the core capability they exist to provide
(#921). Remove `context: fork` from all three command frontmatter files.
`effort: xhigh` (introduced by #769) is preserved.

The `<runtime_compatibility>` Agent-availability guard added by #913 was
checking whether `Agent` was present *before* attempting the call. On
runtimes where the tool list is dynamically resolved this produced
false-negative aborts in sessions that have the tool (#922). Replace
the introspection-based pattern with an attempt-based gate: always
attempt the `Agent()` call; stop only if a real tool-unavailable error
is returned. This preserves #853's backgrounded-session close-off and
false negatives.

Tests updated: enh-769-context-fork-effort.install.test.cjs asserts the
three orchestrators lack `context: fork` and that the converter still
passes the field through for non-orchestrator commands; plan-phase-drift-
guard.test.cjs adds four assertions for the attempt-based gate language;
workflow-size-budget unchanged (budgets not exceeded).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit b866b95296)
2026-06-09 09:07:05 -04:00
Tom Boucher
27af818312 fix(#925): context monitor echoes the invoking hook event name (#927)
Read `data.hook_event_name` from the stdin payload and fall back to the
Gemini/non-Gemini heuristic only when the field is absent or blank.
Fixes Claude Code rejecting output with "expected Stop but got PostToolUse"
when the monitor is called by Stop, SubagentStop, or PreCompact hooks.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit cf6d3b3be5)
2026-06-09 09:06:18 -04:00
Tom Boucher
03d9fcdd65 fix(#924): revert Claude to flat skill layout so concrete skills are discoverable (#928)
PR #883 nested Claude skills 3 levels deep under gsd-ns-*/skills/<stem>/SKILL.md.
Claude Code's Skill tool scans only one level under ~/.claude/skills/ — nested
concrete skills were never listed and Skill(skill="gsd-plan-phase") calls failed.

Revert to flat layout: all ~61 concrete skills at ~/.claude/skills/gsd-<name>/SKILL.md.
The 6 other runtimes confirmed as non-recursive scanners (cline, qwen, hermes, augment,
trae, antigravity) retain their nested layout — only Claude changes.

Tradeoff: ~61 top-level skill dirs return to the flat install, but they are discoverable
and invokable. Nested concretes were invisible to the Skill tool entirely.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 08:59:20 -04:00
Tom Boucher
b866b95296 fix(#921,#922): orchestrators must not fork; plan-phase Agent gate is attempt-based (#926)
`context: fork` strips the `Agent` tool from a subagent's environment.
Spawning orchestrators (`/gsd-autonomous`, `/gsd-execute-phase`,
`/gsd-plan-phase`) depend on `Agent` to dispatch sub-agents; running
them forked silently disables the core capability they exist to provide
(#921). Remove `context: fork` from all three command frontmatter files.
`effort: xhigh` (introduced by #769) is preserved.

The `<runtime_compatibility>` Agent-availability guard added by #913 was
checking whether `Agent` was present *before* attempting the call. On
runtimes where the tool list is dynamically resolved this produced
false-negative aborts in sessions that have the tool (#922). Replace
the introspection-based pattern with an attempt-based gate: always
attempt the `Agent()` call; stop only if a real tool-unavailable error
is returned. This preserves #853's backgrounded-session close-off and
#913's intent of preventing inline role-collapse, while eliminating
false negatives.

Tests updated: enh-769-context-fork-effort.install.test.cjs asserts the
three orchestrators lack `context: fork` and that the converter still
passes the field through for non-orchestrator commands; plan-phase-drift-
guard.test.cjs adds four assertions for the attempt-based gate language;
workflow-size-budget unchanged (budgets not exceeded).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 08:42:25 -04:00
Tom Boucher
cf6d3b3be5 fix(#925): context monitor echoes the invoking hook event name (#927)
Read `data.hook_event_name` from the stdin payload and fall back to the
Gemini/non-Gemini heuristic only when the field is absent or blank.
Fixes Claude Code rejecting output with "expected Stop but got PostToolUse"
when the monitor is called by Stop, SubagentStop, or PreCompact hooks.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 08:42:16 -04:00
Viktorplus
c6c3a51277 Merge branch 'next' into kimi-runtime-support 2026-06-09 13:31:21 +02:00
Tom Boucher
5670feaef5 feat(#918): loop.render-hooks resolver — consume the Capability Registry (ADR-857 phase 3c) (#920)
Add the loop.render-hooks resolver: the first registry-consuming query.
`gsd-tools loop render-hooks <point>` validates the point against the
authoritative canonical 12, reads the registry's materialized byLoopPoint
hooks, filters them by activation, and emits a JSON envelope {point,
activeHooks, rendered} with ordered markdown.

Activation resolves each hook's `when` key by precedence: loadConfig value
(post-cutover federated) -> raw config.json workstream/root single-key lookup
(pre-cutover central override) -> registry configSchema default (so a
default:true capability hook is active out-of-the-box) -> inactive. Guarded
single-value reads only (no merged object built from untrusted keys).

Registry-only: no workflow calls the resolver yet (wiring is the phase-6
cutover). Completes the phase-3 trio.

Closes #918

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 00:24:12 -04:00
Tom Boucher
aa754188b9 Merge pull request #919 from open-gsd/hotfix/1.4.1
chore: merge release v1.4.1 to main
2026-06-09 00:19:23 -04:00
github-actions[bot]
6c1523fc60 chore: merge release v1.4.1 to main (#919)
main holds no content beyond v1.4.0 (git diff v1.4.0 origin/main is empty), so
main adopts the v1.4.1 tree verbatim. Uses the 'ours' merge strategy to record
main as a parent without resurrecting content the 1.4.1 fixes removed.
2026-06-09 00:18:28 -04:00
github-actions[bot]
7b6eae766a chore: promote CHANGELOG for v1.4.1 2026-06-09 04:09:07 +00:00
github-actions[bot]
06aa69506e fix(changelog): drop parenthetical product alias from 1.4.0 Augment note for #1777 purity gate 2026-06-08 23:59:21 -04:00
github-actions[bot]
6711c2062b chore: bump version to 1.4.1 for hotfix 2026-06-08 23:51:13 -04:00
Tom Boucher
bb27263def fix(#913): guard top-level Claude Code plan-phase against role collapse (#915)
Three-part fix for the top-level inline collapse bug:

1. plan-phase.md: add <runtime_compatibility> block after
   </available_agent_types> that makes the Agent-availability
   requirement explicit; workflow fails-closed (stops with a clear
   log) in genuinely Agent-less contexts.

2. plan-phase.md: rename 7 "ORCHESTRATOR RULE — CODEX RUNTIME"
   labels to "ALL RUNTIMES" so the spawn guard applies universally
   (not just when Codex is detected).

3. execute-phase.md: scope the existing "Other runtimes" inline-
   fallback prose to non-Claude contexts, preserving the #853
   backgrounded-agent behaviour for Claude Code background agents.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 5bf77a527f)
2026-06-08 23:44:20 -04:00
Tom Boucher
e0f67c75cd fix(#905): preserve STATE.md frontmatter scalars in syncStateFrontmatter (#907)
syncStateFrontmatter was silently dropping current_phase, current_phase_name,
current_plan, and progress when body annotations were absent (e.g. after an
agent or tool rewrote the body). These scalars can only be derived from body
annotations — when absent, buildStateFrontmatter returns nothing for those
keys. Added existingFm fallbacks mirroring the same pattern already applied in
cmdStateJson, so every writeStateMd call preserves the existing values instead
of stripping them. Also extended cmdStateJson with the same fallbacks for the
three non-progress scalars.

Adds regression test (7 cases) + lint-test-file-count allowlist entry.

Closes #905

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit d12809985e)
2026-06-08 23:44:20 -04:00
Tom Boucher
fe5c9c0527 fix(#892): parse checklist-style roadmap phases in validate/verify (#908)
buildRoadmapPhaseVariants() only matched heading-style phases (## Phase N:),
silently skipping the supported checklist format (- [x] **Phase N: name**).
This caused W007 false-positives for every on-disk phase dir when the project
uses a checklist ROADMAP. Fix adds a second regex pass (mirroring the existing
buildNotStartedPhaseVariants() approach). Also refactors the duplicate
inline heading-only regex in cmdValidateConsistency() to delegate to
buildRoadmapPhaseVariants() (DRY). Regression test in
tests/bug-892-validate-checklist-roadmap-phases.test.cjs covers both paths.

Closes #892

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 808df9110c)
2026-06-08 23:44:20 -04:00
Tom Boucher
de632addd4 fix(#891): probe non-Claude runtime homes in gsd-tools launcher shim detection (#911)
- Updated `gsd-core/workflows/_runtime-launcher.snippet.sh` with 15 new
  `elif` arms covering Hermes, Cursor, Codex, Gemini, Copilot, Windsurf,
  Augment, Trae, Qwen, CodeBuddy, Cline, Grok, Antigravity, OpenCode, and
  Kilo (respecting each runtime's env-var override with a `$HOME`-relative
  default).
- Re-ran `scripts/sync-runtime-launcher.cjs` to propagate the expanded
  snippet into all `gsd-core/workflows/*.md` files (~70 files).
- Manually applied the same snippet update to `commands/gsd/import.md`
  (1 occurrence) and `commands/gsd/graphify.md` (5 occurrences) — these
  are not covered by the sync script.
- Updated `tests/workflow-size-budget.test.cjs` budgets (XL/LARGE/DEFAULT
  + discuss-phase target) to account for the ~3 KB snippet expansion.
- Added regression test `tests/bug-891-non-claude-runtime-home-fallback.test.cjs`
  (6 tests: structural probe presence, ordering, behavioral HERMES_HOME
  env-var + default-path stubs, resolution order, and workflow propagation).
- Added `.changeset/891-launcher-non-claude-runtime-homes.md` (Fixed).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit a90c654745)
2026-06-08 23:44:20 -04:00
Tom Boucher
bf77c0b270 fix(#904): normalize phase number in init.execute-phase branch_name (#909)
Wrap the {phase} substitution in normalizePhaseName() at both fix sites:
- src/init.cts  — cmdInitExecutePhase branch_name output
- src/commands.cts — cmdCommit pre-execution branch derivation

When project_code is set (e.g. "CK"), extractPhaseToken returns the
full prefixed token "CK-01" as phase_number.  Without normalization the
generated branch was "gsd/phase-CK-01-foundation"; after this fix it is
"gsd/phase-01-foundation", matching the documented {phase} contract
(padded numeric only).

Adds a regression test in tests/init.test.cjs.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit cb2cda1865)
2026-06-08 23:44:20 -04:00
Tom Boucher
5209e36678 chore(#899): auto-close fork PRs without a pre-approved issue (#901)
(cherry picked from commit 46d7cdffae)
2026-06-08 23:44:20 -04:00
Tom Boucher
8151ea1329 fix(#866): route profile-pipeline temp output under reaped root + fixture teardown (#879)
Closes #866

(cherry picked from commit a7b288b9df)
2026-06-08 23:44:05 -04:00
Tom Boucher
bf954e4b44 fix(#916): deterministic phase-complete subprocess in regression tests (#917)
The bug #1998 subtest "checkbox updated when archived milestones exist in
<details>" flaked under the high-concurrency docker run (~672 test files in
parallel): the current-milestone checkbox was left unchecked.

Root cause: `gsd-tools phase complete` writes ROADMAP.md as its LAST step,
after a read-heavy parse/lock sequence. Under heavy parallel CPU/IO
contention the test's tight `timeout: 10000` fired mid-parse and SIGTERM'd
the subprocess before that write landed, leaving ROADMAP.md pristine (both
phases `- [ ]`). The bare `catch {}` silently swallowed the kill, so a
timeout masqueraded as a "checkbox not checked" assertion failure. All I/O
is scoped to each test's tmpDir, so there is no cross-process race — the
timeout was the sole cause.

Consolidate all 7 duplicated `phase complete` call sites (suites #1998,
#2005, #2526) into a shared runPhaseComplete() helper that:
  1. raises the timeout to 60s so the test's own timer never kills the
     subprocess under load;
  2. never silently swallows a signal/timeout kill (rethrows loudly with
     captured output) while still tolerating a clean non-zero exit for the
     ROADMAP-asserting tests via { tolerateExit: true }.

No retry loop. Verified with 3x `gsd-test --reset` full docker runs
(13207 tests / 2311 suites each, 0 failures, flaky subtest green every
round).

Closes #916

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 23:42:24 -04:00
Tom Boucher
6dbd895028 feat(#910): federated config merge in config-loader (ADR-857 phase 3b) (#914)
Build the federated config merge: each capability owns its config-key slice
(ADR-857 decision 3 / ADR-894), and loadConfig merges them defensively. The
registry now emits a full configSchema index ({key:{owner,type,default,
description}}, generator-validated); a new src/federated-config.cts resolves
federated keys defensively (skip central keys -> pending-migration warning,
skip malformed slices -> warning never throw, else type-checked user override
?? default, with nested dotted-path lookup and enum validation); and loadConfig
applies the overlay on every return path.

Wired as a provably-empty no-op channel: every UI-pilot key is still central,
so validKeys is empty and loadConfig returns byte-identical output on all paths
(identity return when the overlay is empty; shared CONFIG_DEFAULTS never
mutated). Registry-only; no key is cut over; nothing in the live loop changes.

Closes #910

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 23:37:41 -04:00
Tom Boucher
68d1035aa8 fix(#872): make roadmap-phase-fallback tests hermetic against ambient GSD env (#873)
extractCurrentMilestone reads STATE.md via planningDir(cwd), which is
workstream-aware (honours GSD_PROJECT/GSD_WORKSTREAM). The fixtures write
STATE.md to the plain <tmp>/.planning/STATE.md, so a developer shell inside a
GSD workstream (GSD_WORKSTREAM exported) redirected the read to a non-existent
workstream subdir -> version=null -> closed milestone sections leaked into the
slice and assertions failed. Clean CI/Docker env never hit it. Not a Node-26
regex bug; reproduces identically on any Node with GSD_WORKSTREAM set.

- scripts/run-tests.cjs: strip GSD_PROJECT/GSD_WORKSTREAM before spawning test
  children so the local runner env matches clean CI/Docker.
- tests/roadmap-phase-fallback.test.cjs: file-level beforeEach/afterEach
  save/delete/restore of both vars; new regression test pinning workstream-aware
  STATE.md resolution.
- tests/run-tests-harness.test.cjs: guard asserting the runner strips both vars
  (so removing the deletion fails clean CI).

Closes #872

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit a480510f54)
2026-06-09 03:34:32 +00:00
Tom Boucher
24c18d07ab chore(#57): add runtime install no-drift guard tests (#867)
Add tests/issue-57-runtime-install-no-drift.test.cjs protecting the Runtime
Install Policy Module boundary (ADR-58) and the explicit Runtime Config Adapter
Registry (#60), now that #58/#60/#56 have landed and the seam exists.

The guards fail when:
- (AC1) supported-runtime metadata is added to an installer/query call site
  (allRuntimes, the interactive runtimeMap menu) without a matching registry
  adapter entry — enforced by three-way set equality across allRuntimes,
  runtimeMap values, and ALLOWED_CONFIG_RUNTIMES.
- (AC2) config-mutation dispatch escapes the registry: every intent uses a
  registry-declared install surface, every permission writer is null or a
  registry-known runtime, unknown/prototype-key runtimes fail loudly, and a new
  inline 'runtime === "..."' branch against an unregistered runtime is rejected.

Assertions are behavioral (require + reflect on live exports) where behavior can
cover the contract (AC3); two annotated structural guards cover what it cannot.
Existing installer/runtime-policy/runtime-global-skills suites stay green (AC4).

Gates: eslint, lint-test-file-count, full Mac suite (12715 pass / 0 fail) and
Linux Docker (14709 pass / 0 fail) all green; Codex adversarial-review,
/code-review, and /security-review run with findings addressed.

Closes #57

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 35174ce9b0)
2026-06-09 03:34:32 +00:00
Tom Boucher
05db5b53e4 fix(#853): gate manager/autonomous background dispatch by runtime (#863)
* fix(#853): gate manager/autonomous bg dispatch by runtime

/gsd-manager and /gsd-autonomous --interactive dispatched Plan/Execute
via Agent(run_in_background=true). On Claude Code a backgrounded agent
has no Agent/Task tool, so it cannot spawn the nested subagents those
pipelines need — per-plan worktree-isolated executors, the plan-checker,
and the verifier. The phases reported complete but isolation and
independent verification silently never ran, even with use_worktrees /
plan_check / verifier enabled.

Both workflows now resolve the runtime (config-get runtime, default
claude) before dispatching: run plan/execute INLINE on Claude Code so
the nested pipeline runs, and background-dispatch only on runtimes where
a backgrounded agent can still nest. Mirrors execute-phase.md's existing
Codex fail-closed precedent. Reconciles the stale unconditional
background/overlap/lean-context claims elsewhere in both workflows and
in the docs. Adds a content regression test pinning the gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#853): add changeset for runtime-gated bg dispatch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 3697e6768f)
2026-06-09 03:34:32 +00:00
Tom Boucher
d02e29259a fix(#856): remove gsd-cmd-rewrites temp dirs after install (#862)
* fix(#856): remove gsd-cmd-rewrites temp dirs after install

applyRuntimeContentRewritesForCommandsInPlace() returns a fresh
mkdtemp dir under os.tmpdir() (gsd-cmd-rewrites-*) with rewritten
command markdown. installRuntimeArtifacts() copied from it but never
removed it, leaking one temp dir per commands kind per install — on
tmpfs /tmp hosts these accumulate and consume RAM-backed storage.

Wrap the per-kind copy in try/finally and rmSync the temp dir (only
when it differs from the staged source, i.e. the commands kind) once
the copy completes or fails. dest creation moved inside the try so a
mkdir failure still triggers cleanup.

Regression test isolates os.tmpdir() to a private TMPDIR root and
asserts no gsd-cmd-rewrites-* dir survives the install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#856): add changeset for installer temp-dir cleanup

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit ac56672dba)
2026-06-09 03:34:32 +00:00
Tom Boucher
47fad118a8 fix(#834): derive installer --help skill counts from PROFILES (#847)
* fix(#834): derive installer --help skill counts from PROFILES

The installer --help text undercounted profile skill counts: it claimed
"core — 7 main-loop skills" and "standard — ~13 skills" when the real
counts in PROFILES (bin/lib/install-profiles.cjs) are 8 and 14. The
`surface` skill was added to core after the help string was written, and
standard was never updated.

Derive the core and standard counts from PROFILES.core.length and
PROFILES.standard.length so the help text cannot drift again. Replace the
stale hardcoded "all 66 skills" (actually 67) on the full line with
drift-proof "all skills" — full is the '*' sentinel with no array length
and no cheap authoritative total in the help path.

Add regression tests that run `node bin/install.js --help` and assert the
printed core/standard counts equal the PROFILES lengths, plus a guard
that the full line carries no hardcoded numeric count.

Closes #834

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#834): add changeset for installer --help count fix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit e20985a15f)
2026-06-09 03:34:32 +00:00
Tom Boucher
ccf2b5624e chore(#846): remove unused PR-size labeler (size/S–XL) workflow (#848)
The PR Gate workflow's only job, size-check, labeled every PR with
size/S–size/XL based on lines changed. Those labels aren't used in any
review, triage, or automation flow, so the workflow was pure noise.

- Delete .github/workflows/pr-gate.yml
- Drop size-check from required status checks in both rulesets so PRs
  don't block forever on a check that never reports
- Remove pr-gate.yml from INERT_WORKFLOWS (ci-test-scope.cjs) and the
  knownInert list (ci-test-scope.test.cjs)
- Remove "PR Gate / size-check" from setup-branch-protection.sh

Closes #846

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 606363c416)
2026-06-09 03:34:32 +00:00
Tom Boucher
5bf77a527f fix(#913): guard top-level Claude Code plan-phase against role collapse (#915)
Three-part fix for the top-level inline collapse bug:

1. plan-phase.md: add <runtime_compatibility> block after
   </available_agent_types> that makes the Agent-availability
   requirement explicit; workflow fails-closed (stops with a clear
   log) in genuinely Agent-less contexts.

2. plan-phase.md: rename 7 "ORCHESTRATOR RULE — CODEX RUNTIME"
   labels to "ALL RUNTIMES" so the spawn guard applies universally
   (not just when Codex is detected).

3. execute-phase.md: scope the existing "Other runtimes" inline-
   fallback prose to non-Claude contexts, preserving the #853
   backgrounded-agent behaviour for Claude Code background agents.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 23:32:34 -04:00
Colin
560b59240f fix(#743): resolve Kimi branch lint failures 2026-06-08 23:07:48 -04:00
Colin Johnson
bf8813b7a0 Merge branch 'next' into kimi-runtime-support 2026-06-08 23:04:37 -04:00
Tom Boucher
d12809985e fix(#905): preserve STATE.md frontmatter scalars in syncStateFrontmatter (#907)
syncStateFrontmatter was silently dropping current_phase, current_phase_name,
current_plan, and progress when body annotations were absent (e.g. after an
agent or tool rewrote the body). These scalars can only be derived from body
annotations — when absent, buildStateFrontmatter returns nothing for those
keys. Added existingFm fallbacks mirroring the same pattern already applied in
cmdStateJson, so every writeStateMd call preserves the existing values instead
of stripping them. Also extended cmdStateJson with the same fallbacks for the
three non-progress scalars.

Adds regression test (7 cases) + lint-test-file-count allowlist entry.

Closes #905

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:52:43 -04:00
Tom Boucher
808df9110c fix(#892): parse checklist-style roadmap phases in validate/verify (#908)
buildRoadmapPhaseVariants() only matched heading-style phases (## Phase N:),
silently skipping the supported checklist format (- [x] **Phase N: name**).
This caused W007 false-positives for every on-disk phase dir when the project
uses a checklist ROADMAP. Fix adds a second regex pass (mirroring the existing
buildNotStartedPhaseVariants() approach). Also refactors the duplicate
inline heading-only regex in cmdValidateConsistency() to delegate to
buildRoadmapPhaseVariants() (DRY). Regression test in
tests/bug-892-validate-checklist-roadmap-phases.test.cjs covers both paths.

Closes #892

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:52:19 -04:00
Tom Boucher
6edc39c4eb chore(#893): remove dead loadConfig from configuration.cts (#912)
`loadConfig` in configuration.cts was superseded by config-loader.cts
(ADR-857 phase 2e, #885). Exhaustive grep confirms no caller imports
loadConfig from configuration.cjs — all live callers use config-loader.cjs
or the core.cjs back-compat re-export. configuration.cts now provides only
the pure normalization and defaults primitives (normalizeLegacyKeys,
mergeDefaults, migrateOnDisk, CONFIG_DEFAULTS) that config-loader.cts
depends on. Updated CONTEXT.md and docs/INVENTORY.md to reflect the
narrowed module surface.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:51:43 -04:00