* 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)
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)
- 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)
`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)
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)
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)
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)
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)
- 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)
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)
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)
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)
* 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)
* 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)
* 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)
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)
* fix(#844): sync runtime manifest versions on npm version bump
The release workflow bumps package.json via `npm version` but never
stamped the runtime-integration manifests that must track it
(.claude-plugin/plugin.json #766, gemini-extension.json #775), so the
first RC/finalize whose version diverged from the -dev stream failed the
test suite before tagging/publishing.
Add scripts/sync-manifest-versions.cjs (single VERSIONED_MANIFESTS
registry) wired to a `version` npm lifecycle hook that stamps + stages
the manifests on every `npm version` — covering all four release bump
sites and local bumps with no workflow edits. A regression guard test
fails if any repo JSON whose version matches package.json is not
registered, forcing future version-bearing manifests into the sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#844): add changeset for manifest version sync fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#770): register Claude Code lifecycle hooks (SubagentStop/Stop/PreCompact/FileChanged)
Wire three new context-tracking events (SubagentStop, Stop, PreCompact) to
gsd-context-monitor so context-headroom warnings surface at model-stop and
subagent-finalisation moments — not just on PostToolUse. Add a new
FileChanged hook (gsd-config-reload.js) that hot-reloads .planning/config.json
context mid-session when the user edits it, injecting a config summary as
hookSpecificOutput.additionalContext. Updates plugin manifest hooks.json,
managed-hooks-registry, installer-migration-report allowlist, and
shell-command-projection cleanup tables. Tests: 21 new assertions in
enh-770-claude-hook-events.test.cjs; enh-788 and issue-766 test suites updated.
Closes#770
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#770): document newly-registered Claude Code lifecycle hooks
Add a Hook coverage table to the Claude Code npm installer section of
docs/how-to/install-on-your-runtime.md describing SubagentStop, Stop,
PreCompact, and the new FileChanged (gsd-config-reload.js) hook that
hot-reloads .planning/config.json mid-session. Also fixes the changeset
frontmatter (adds type: Added + pr: 821) so docs-lint can consume the
fragment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): add gsd-config-reload.js to INVENTORY.md and regenerate manifest
The feat commit added hooks/gsd-config-reload.js but did not bump the
Hooks count in docs/INVENTORY.md (14→15) or add the new row, and did not
regenerate docs/INVENTORY-MANIFEST.json. Both inventory-counts and
inventory-manifest-sync tests failed across the full CI matrix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): make lifecycle-hook tests deterministic on scoped runner
Replace the shared hooks/dist/ ensemble setup (ensureHooksDist /
teardownHooksDist) in the Claude hook tests with per-test isolation:
pre-populate each test's own tmpDir/.claude/hooks/ with stub files and
pass installerMigrations:[] to install() so the first-time-baseline
migration does not remove the stubs before the copy step can run.
Root cause: hooks/dist/ is gitignored and absent on a fresh npm ci.
ensureHooksDist() created it and teardownHooksDist() deleted it, but
with --test-concurrency=4 both test files ran concurrently as separate
Node.js worker processes sharing the same filesystem. One file's
afterEach teardown deleted hooks/dist/ while the other file's install()
was copying from it, producing an ENOENT (reproduced 2/10 runs locally).
The additional issue: even with pre-placed stubs surviving the copy race,
the 000-first-time-baseline migration classified hooks/gsd-*.js as
bundled-gsd-hook artifacts, auto-removed them, and the copy step never
re-ran (hooks/dist/ absent) — leaving contextMonitorFile missing and all
hook registrations silently skipped (the 'got: []' symptom).
Fix: pre-populate targetDir/hooks/ per-test (isolated temp dir) AND pass
installerMigrations:[] so the baseline scan is skipped. The Qwen suites
already used this pattern correctly; the Claude suites are aligned to it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): ship gsd-config-reload.js by adding it to build-hooks HOOKS_TO_COPY
The #770 feature added hooks/gsd-config-reload.js and registered it in
MANAGED_HOOKS, the installer, INVENTORY, and the test EXPECTED_ALL_HOOKS
list — but never added it to scripts/build-hooks.js HOOKS_TO_COPY. As a
result the hook was never copied into hooks/dist/ during the build, so:
- the hook would never ship to users (real production bug — the
FileChanged config-reload feature was dead-on-arrival), and
- install-minimal-hooks.test.cjs #1755 ("all expected hooks are copied
from hooks/dist/ to target", ".js hooks are executable after copy",
"manifest contains .js hook entries") failed on any environment with
a clean checkout (no pre-existing hooks/dist/): coverage, full test
macos-22/macos-24, test ubuntu-24.
The failures were masked locally only by a stale hooks/dist/ left from a
prior build (build-hooks copies into dist without clearing it). On CI's
fresh `npm ci` there is no dist, so the omission surfaced.
Fix: add 'gsd-config-reload.js' to HOOKS_TO_COPY so build-hooks stages it
into hooks/dist/ alongside the other JS hooks. Verified by removing
hooks/dist/ and rerunning the full suite green (0 fail).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): make config prototype-pollution beforeEach deterministic on scoped runner
Root cause: the #663 and alert-#26 prototype-pollution describe blocks
seeded .planning/config.json in beforeEach via a bare
runGsdTools('config-ensure-section') whose result was discarded. That
command runs in a spawned gsd-tools child; on the scoped CI lane
(--test-concurrency=4, config.test.cjs scheduled alongside the heavy
install/tarball suites that #770 pulled into the targeted set) the child
can be transiently killed under resource pressure (non-zero exit, empty
stderr — an OS-level kill, not an app error). The swallowed failure left
config.json absent, so the first subtest's readConfig() threw ENOENT
opening <tmp>/.planning/config.json. Only 1 of 4 subtests failed,
confirming a per-invocation transient, not a deterministic miss; the full
suite schedules files differently so config.test.cjs did not collide with
those heavy neighbors → passed there.
Fix: add ensureConfigReady(tmpDir) which retries config-ensure-section on
ANY failure or missing file and throws a clear diagnostic if it still
cannot create config.json, then use it in both prototype-pollution
beforeEach blocks. Setup is now deterministic under load; the #663/alert-#26
security assertions are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#836): no-LLM duplicate-issue detection + challenge + 1-day auto-close
Adds a deterministic (no-LLM) duplicate-issue governance lifecycle:
- scripts/issue-dedupe.cjs: pure, unit-tested module (tokenize, Sørensen–Dice
title similarity, scoreCandidates, renderChallengeComment, shouldClose) with
fail-safe destructive-action guards.
- duplicate-check.yml (issues:opened): scores new-issue title against open
issues, posts a challenge comment + applies the pending `possible-duplicate`
label on a clear match.
- duplicate-sweep.yml (daily cron): closes possible-duplicate issues whose
challenge comment is >24h old with no human reply and no 👎 veto; honors
exempt labels; re-checks the label immediately before close (TOCTOU guard);
strips the label on close to avoid reopen loops.
- remove-duplicate-label.yml (issue_comment:created): clears the label and
applies needs-maintainer-review when any human responds.
- bug_report.yml / docs_issue.yml: add the required "I searched existing
issues" preflight checkbox so all five forms force a pre-search attestation.
- docs/agents/triage-labels.md: document the label + lifecycle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#836): add changeset fragment for duplicate-issue detection
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
CI test-scope detection diffed changed files with a two-dot
`git diff --name-only base head`, where base is the moving tip of `next`.
A PR branch cut from a slightly older `next` surfaced every product file
`next` had gained since the merge-base, flipping product_changed/full_matrix
and running the full Windows/macOS matrix + coverage on docs-only PRs.
Switch to a three-dot `git diff --name-only base...head` (vs the merge-base),
matching GitHub's PR "Files changed" semantics. Add a regression test that
builds a stale-base topology, plus a guard test pinning `fetch-depth: 0` on
the `changes` job (required for the merge-base to be locally available).
Closes#837
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Adds an opt-in --next (alias --rc) flag to /gsd-update targeting the @next RC dist-tag (ADR #660), with a {latest,next} allowlist enforced at three layers, channel-aware version check + banner, and byte-for-byte unchanged default @latest behavior.
Closes#815
Add docs/how-to/install-minimal-and-add-skills.md covering the --minimal
/ --core-only / --profile=core install, the core/standard/full profiles,
and growing the surface live via /gsd:surface or on reinstall. Register
it in the docs/README.md How-to guides index.
Closes#832
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity
- Add gsd-cursor-session-start.js: injects STATE.md presence reminder (or
new-project nudge) into Cursor sessions via the sessionStart hook event
- Add gsd-cursor-post-tool.js: emits an additional_context nudge when
write-class tool calls touch .planning/ files (postToolUse hook event)
- Add 'cursor-hooks-json' installSurface to runtime-config-adapter-registry;
writeCursorHooksJson/reconcileCursorHooksJson write the canonical
{ version: 1, hooks: { sessionStart, postToolUse } } JSON shape with
idempotent reconciliation that preserves user-owned hook entries
- Hook scripts are copied with /gsd:→gsd- rewrite so installed files
contain no colon-form slash-command refs (bug-376 invariant)
- 20 new tests in tests/cursor-hooks.test.cjs cover all reconciler paths,
entry helpers, removal, runtime adapter surface, and hook script behavior
- Update CONTEXT.md, ARCHITECTURE.md, installer-migrations.md, and
000-first-time-baseline.cts to include Cursor hooks.json surface
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#777): build hooks/dist on demand in bug-376 test for scoped/windows CI
hooks/dist is gitignored and only produced by `npm run build:hooks`.
The CI scoped (ubuntu-latest/node-22) and windows (windows-latest/node-24)
test jobs do NOT run build:hooks before executing tests, so bug-376's
prerequisite suite was failing with "hooks/dist not found" on both legs.
Add ensureHooksDist() helper (mirrors bug-3357 pattern) that builds
hooks/dist on demand in the before() hooks of prerequisite and Suite 3.
Also add ensureHooksDist() call to Suite 3's before() so the snapshot
step is also hermetic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#776): Gemini hook events (BeforeAgent/AfterAgent/BeforeModel) + hooksConfig.enabled check
Register three new Gemini-CLI hook events on install:
- BeforeAgent: fires before agent planning; wired to gsd-context-monitor
- AfterAgent: fires after final response generation; wired to gsd-context-monitor
- BeforeModel: fires before each LLM call (per-turn); wired to gsd-context-monitor
All three reuse gsd-context-monitor.js (no new hook files). Uninstall cleanup
loop extended to remove the new events. Non-array guard added for robustness
against malformed settings.
Also detect hooksConfig.enabled:false in Gemini settings and emit a clear
warning — without this check, all registered hooks silently do nothing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: update changeset pr: 829
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#776): document Gemini hook events
Add hook coverage table to the Gemini CLI section of install-on-your-runtime.md,
covering the three new events (BeforeAgent/AfterAgent/BeforeModel wired to
gsd-context-monitor) plus a callout for the hooksConfig.enabled:false silent
failure mode detected by the installer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#789): elevate CodeBuddy — emit slash commands (+ document subagent/MCP scope)
Emit a CodeBuddy slash-command surface so GSD workflows appear in the
'/' menu, reaching parity with other elevated runtimes.
- Add convertClaudeCommandToCodebuddyCommand and register a commands/
artifact kind for the codebuddy runtime (commands/gsd-<name>.md),
consistent with the Cursor (#785) and Augment (#790) commands surfaces.
- Mark emitted skills user-invocable:false so the commands surface is the
sole '/' entry point (no duplicate /gsd-* entries); skills stay
model-invocable. CodeBuddy's SKILL.md supports this field.
- Normalize $HOME/.codebuddy (bare + slash) path forms in runtime
rewrites so --config-dir/local installs don't leak the default home.
- Report installed commands/ count on install; uninstall prunes gsd-*
commands while preserving user-owned commands.
Scope: subagents (~/.codebuddy/agents/) are already emitted by the
generic agents block (unchanged); no mcp.json is written (gsd ships no
MCP server, and CodeBuddy's mcp.json registers only external servers).
Closes#789
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#789): set changeset pr number to 830
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#778): cross-runtime command enrichment (Gemini {{args}}/!{}, Qwen priority)
Enrich the installer's per-runtime command/skill generators with native,
verified, additive fields:
- Gemini CLI: map Claude's $ARGUMENTS -> Gemini's {{args}} in generated TOML
commands so typed arguments interpolate; inject live .planning/STATE.md into
/gsd:progress via a fixed, injection-safe !{cat .planning/STATE.md 2>/dev/null}
shell block (no interpolated input).
- Qwen Code: emit the optional numeric `priority` field on main-loop skills so
the most-used workflows sort first in the /skills list (higher = earlier per
the Qwen skills spec; the issue's inverse numbering was corrected).
OpenCode per-command model/agent/subtask/variant enrichment was evaluated and
intentionally not implemented: `model` reintroduces the #1156
ProviderModelNotFoundError regression for non-Anthropic providers (the converter
deliberately strips model:), `subtask`/`agent` change execution semantics for
GSD's interactive commands, and `variant` is not in the OpenCode command schema.
Schemas verified against primary docs (Gemini custom-commands, Qwen skills,
OpenCode commands/skills). Adds tests/enh-778-* and how-to + USER-GUIDE docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#778): set changeset PR number to 825
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#772): adopt stable Codex hook events + commandWindows for Windows parity
Register three new stable Codex hook events (SubagentStart, Stop,
PostToolUse) wired to gsd-context-monitor.js so Codex installs get
the same context-headroom tracking at subagent and session boundaries
that Claude/Qwen already have.
Add commandWindows field to the SessionStart hook entry on Windows so
Codex uses the .cmd shim directly (Git Bash/MSYS cannot POSIX-exec
node.exe). commandWindows is only emitted on win32; POSIX is unchanged.
Refactor reconcileCodexHooksJsonSessionStart into a generic
reconcileCodexHooksJsonEvent so any event name can be reconciled with
the same dedup/preserve-user-entries logic.
Add gsd-context-monitor.js and .cmd to MANAGED_HOOK_COMMAND_BASENAMES
_BY_SURFACE so idempotent re-runs de-duplicate entries correctly.
30 new tests covering: export surface, event registration for each of
the three events, commandWindows parity (POSIX vs win32), idempotency,
uninstall, and user-entry preservation.
Closes#772
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#772): windows path normalization + docs-lint
- Normalize scriptPath backslashes to forward slashes in
ensureCodexHooksJsonEvent and ensureCodexHooksJsonSessionStart so that
isManagedHookCommand can match stored commands against configDir on
Windows CI runners. path.resolve returns backslash paths on Windows,
but when platform is not 'win32' (e.g. platform:'linux' in tests),
projectManagedHookCommand skips normalization — producing a mismatch
that breaks idempotency deduplication (the same hook entry appended
twice on re-register). Forward-slash paths are always valid in both
Node.js and Codex, so the normalization is safe for all platforms.
- Fix changeset pr: 0 → 827 to resolve fail_malformed_fragment.
- Add Codex hook coverage table to docs/how-to/install-on-your-runtime.md
documenting the SubagentStart/Stop/PostToolUse events + commandWindows
Windows-parity field added by this enhancement.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#769): emit context:fork + effort: frontmatter on heavy workflow skills
Add `context: fork` and `effort: xhigh` to the three heaviest workflow
commands (plan-phase, execute-phase, autonomous) and `effort: low` to the
two quick-status commands (progress, stats).
On Claude Code, `context: fork` runs the skill in an isolated subagent
context window so the main session's context budget is protected.
`effort: xhigh` / `effort: low` signal the appropriate token-budget tier to
the runtime. Both fields are silently ignored by runtimes that do not
recognise them (Gemini, Codex, Cursor, etc.) — no behaviour change outside
Claude Code.
Update convertClaudeCommandToClaudeSkill in bin/install.js to preserve
`context:` and `effort:` when rewriting source command files to SKILL.md for
a Claude global install. Add install-suite tests to assert the fields are
present in both source commands and the installed SKILL.md output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#769): tighten regex assertions + add execute/plan-phase effort coverage
Fix low-severity adversarial finding: tighten test regex patterns from
`\s*` to `[ \t]*` so they cannot match across newlines (CRLF parity).
Add missing effort: xhigh assertions for gsd-execute-phase and gsd-plan-phase
SKILL.md install output to complete the black-box coverage gap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#774): emit service_tier/model_verbosity in Codex agent TOML + agents/openai.yaml skill chip
- Add service_tier = "flex" and model_verbosity = "low" to the Codex
ConfigProfile TOML for light-tier agents (gsd-research-synthesizer,
gsd-codebase-mapper, gsd-plan-checker, and 8 others identified via
AGENT_DEFAULT_TIERS). Field names/values verified against Codex schema
(profile_toml.rs / config_types.rs Verbosity enum). Non-light agents
are unaffected.
- Add generateCodexSkillMetadataYaml() and writeCodexSkillMetadataFiles():
after installRuntimeArtifacts, iterate every gsd-* skill directory,
read the short-description already emitted in the SKILL.md frontmatter
by convertClaudeCommandToCodexSkill, and write agents/openai.yaml with
interface.display_name and interface.short_description for the Codex
TUI skill picker chip.
- yamlQuote (JSON.stringify) handles all YAML-unsafe chars.
- User-owned gsd-dev-preferences dir is never overwritten.
- Errors per-skill are swallowed so a bad SKILL.md can't abort install.
- agents/openai.yaml is covered by the snapshot/rollback system and
manifest hash (writeManifest hashes skill dirs recursively).
- Uninstall symmetry: _removeGsdEntries removes whole gsd-* dirs.
- 21 new tests in codex-config.test.cjs covering service_tier/verbosity
TOML emission, YAML generation (round-trip via js-yaml), and
writeCodexSkillMetadataFiles including an e2e integration test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#774): correct docs-lint coverage — proper changeset format + USER-GUIDE entry
Rewrite the changeset fragment from old @opengsd/gsd-core:patch format to the
required type:/pr: schema so the docs-lint parser can consume it. Add a new
"Codex skill picker and agent scheduling (#774)" section to docs/USER-GUIDE.md
describing the flex-tier scheduling and /skills TUI chip enrichments — both are
user-visible and belong in docs rather than behind a docs-exempt marker.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#768): pre-populate settings.json permissions.allow/deny for Claude Code
Adds mergeClaudePermissions() to bin/install.js which non-destructively
appends GSD's known-safe tool-call patterns to permissions.allow and
defense-in-depth credential-file patterns to permissions.deny during
Claude Code installs. Merge is idempotent (no duplicates on reinstall)
and additive (existing user entries preserved). Uninstall removes only
the exact GSD-owned entries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: update changeset pr number to 819
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add a Gemini CLI extension package so users can install, update, and
remove GSD through Gemini's own extension lifecycle and have it appear in
`gemini extensions list`:
gemini extensions install https://github.com/open-gsd/gsd-core
gemini extensions update gsd-core
gemini extensions uninstall gsd-core
gemini extensions link /path/to/gsd-core # dev
This mirrors the additive Claude Code plugin manifest (#766): a thin,
version-stamped manifest enforced by an in-repo drift test. The extension
ships the context-file payload (GEMINI.md), loaded into every Gemini
session; slash-command/agent/hook TOML projection into the extension is a
documented follow-up. The manual `npx gsd-core --gemini` installer (which
provides the /gsd:* commands) is unchanged — purely additive, no breaking
change.
- gemini-extension.json: name=binName, version tracks package.json,
description, contextFileName=GEMINI.md (minimal; no mcpServers — gsd
ships no MCP server)
- GEMINI.md: Gemini-session context payload
- package.json: add both artifacts to files[] so they publish
- CONTEXT.md: add "Gemini Extension Package" glossary entry
- docs: USER-GUIDE + install-on-your-runtime how-to
- tests/issue-775-gemini-extension.test.cjs: manifest validity, version
parity with package.json, contextFileName existence, files[] publication
Closes#775
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#773): add --ephemeral and --dangerously-bypass-hook-trust to automated codex exec invocations
Automated codex exec calls in the review workflow now carry --ephemeral
(no session-state accumulation across CI runs) and
--dangerously-bypass-hook-trust (skip hook-trust prompts for hooks
whose provenance gsd-core already controls). Both flags were verified
present in the installed codex CLI (codex exec --help).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#773): correct changeset pr: reference to #824
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#771): convert agent color: hex/magenta values to documented named colors
Claude Code's sub-agent `color:` field documents only 8 named colors
(red, blue, green, yellow, purple, orange, pink, cyan). Twelve agent
files used hex values and two used the undocumented `magenta`; convert
each to the nearest documented named color so the intended per-agent
TUI color differentiation is spec-compliant.
- agents/*.md: 14 color values hex/magenta -> nearest named color
- scripts/research-profiles.cjs: update the 3 generated research-agent
profiles (source of truth) so gen-research-agents stays in sync
- docs/AGENTS.md: update documented colors; add missing Color rows for
gsd-nyquist-auditor, gsd-project-researcher, gsd-phase-researcher
- tests/agent-frontmatter.test.cjs: add regression guard asserting every
agent color: is in the documented named-color set
Closes#771
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#771): add changeset
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>