* 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>
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>
- 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>
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>
* 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>
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>
`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>
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>
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>
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>
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>
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>
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>
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>
`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>
- 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>
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>
Replace the inline LOOP_HOST_CONTRACT constant in the Capability Registry
generator with a generated-from-workflows contract (ADR-894 §3). The contract
is now derived from inert `<!-- gsd:loop-host ... -->` marker blocks in the five
step workflows, emitted as the committed gsd-core/bin/lib/loop-host-contract.cjs,
and required by gen-capability-registry.cjs — one source of truth, no drift.
Drift guards in gen-loop-host-contract.cjs: per-step point ownership (each step
must declare exactly its canonical loop points), multiple-block + duplicate-key
hard errors, and a word-boundary agent-role cross-check. Contract content is
byte-identical to the former constant; registry-only, nothing wired into the
live loop.
Closes#903
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#896): Capability Registry generator + UI pilot (ADR-857 phase 3a-impl)
First phase-3 code: the Capability Registry generation pipeline, built against
the ADR-894 contract and NOT wired into the live loop (registry-only, per the
staged-cutover design).
- capabilities/ui/capability.json — the UI pilot (ADR-894 worked example): 2
skills, 2 agents, 3 config keys, 2 steps + 1 gate, with `when` activation.
- scripts/gen-capability-registry.cjs — --write/--check generator. Hand-rolled
schema validation (envelope + role-typed feature/runtime bodies + typed
steps/contributions/gates + when + gate-check variants); cross-capability
invariants (single ownership; requires exist+acyclic+tier-monotone; config-key
ownership exclusive, collision-vs-central as a pending-migration warning);
hooks validated against an inline LOOP_HOST_CONTRACT (3a-impl-2 swaps its
source to the generated-from-workflows contract); GLOBAL point-ordered
consumes-satisfiability; materialized byLoopPoint ordering (produces/consumes
topo-sort); emits gsd-core/bin/lib/capability-registry.cjs (role-partitioned
indexes + requiresClosure). Prototype-pollution guards (Object.create(null) +
inline literal key checks) + fragment.path traversal guard.
- gsd-core/bin/lib/capability-registry.cjs — committed generated artifact
(mirrors package-identity.cjs: script-generated, tracked, linted, regenerated
on build, drift-tested), wired via the new `gen:capability-registry` build step.
- tests/capability-registry.test.cjs — 72 tests: schema + invariant + hook +
ordering + adversarial (path-traversal, proto-pollution, runtime body,
self-consume, cycles, collisions) + committed-file staleness guard.
New-CLI-module checklist (INVENTORY 97->98, MANIFEST, ARCHITECTURE), CONTEXT.md
"Capability Registry" un-[Planned]'d. Nothing wired into install/surface/loop.
Gates: lint, code-review (4 bugs fixed), security-review (path-traversal +
prototype-pollution fixed), codex adversarial-review ×3 (8+ findings fixed,
confirmed sound), clean-build docker 13190 pass / 0 fail.
Closes#896
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#896): CRLF-agnostic --check for capability-registry staleness (Windows)
The committed capability-registry.cjs staleness guard failed on Windows CI only:
git checks out the committed .cjs as CRLF (autocrlf, no .gitattributes) while the
generator emits LF, so the byte-for-byte --check comparison mismatched. Normalize
line endings on both sides of the --check comparison (no .gitattributes change,
no change to the LF the generator writes). Adds a regression test simulating the
Windows CRLF checkout.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#894): ADR-894 Capability declaration format + registry generation
ADR-857 rollout phase 3a (design-only). Resolve ADR-857's deferred open
question — the on-disk Capability declaration format — as a reviewable design
ADR before any generator code.
Specifies: the capabilities/<id>/capability.json folder layout (migration-staged
ownership — declarations reference existing stems until the phase-6 move); the
capability.json schema for role:feature (skills/agents/hooks/federated config/
loopHooks) and role:runtime (the six closed projection-primitive axes); the 12
named Loop Extension Points; the gen-capability-registry.cjs generator design
(validation + cross-capability invariants + --write/--check drift gate, mirroring
gen-inventory-manifest); the generated capability-registry.cjs shape (by-id /
by-skill / by-loop-point indexes + requires-closure); and a full worked example
(the UI capability: ui-phase + ui-review + agents + config + two loop hooks).
No code — design artifact only; the generator build, federated config loader
(3b), and loop seam (3c) implement against this contract.
Closes#894
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#894): amend ADR-894 with grilled capability declaration format
Stress-tested the declaration format before merge; the format changed
materially. Amendments:
- loopHooks[] -> three typed arrays (steps/contributions/gates), each with its
own shape (step: ref+produces/consumes; contribution: fragment+into agent-role;
gate: check+blocking).
- Add the Loop Host Contract (§3): each step publishes its points, agent roles,
and core artifacts so the generator validates hooks against reality, not
trusted strings.
- requires = capability ids only (host implicit); add tier-monotone invariant;
drop the requires:["plan"] error from the example.
- Config federation = atomic move: a migrated key leaves the central schema in
the same PR; presence in both is a collision (invariant stays).
- One registry, role-partitioned indexes (feature indexes vs runtimes index).
- Rework the UI worked example to the split-array shape (2 steps + 1 gate) +
a contribution illustration.
Adds a "Grilling amendments" section recording the six changes.
* docs(#894): amend ADR-894 with round-2 grilling (operational reality)
Second design-grill round, folded in before merge:
- Loop Host Contract is GENERATED from structured workflow markers
(<loop-point>/<agent-role>/<loop-artifact>) via gen-loop-host-contract.cjs —
it can't drift from the real workflows.
- Hook activation `when`: cheap deterministic config-level gating evaluated by
loop.render-hooks; deeper phase-context applicability self-gates inside the
dispatched skill (no phase-context vocabulary to keep honest).
- `tier` is the source of install-profile + cluster membership; profiles and
clusters are generated from tier + requires-closure (/gsd:surface operates on
capabilities) — collapses ADR-857's multiple toggle systems.
- Gate `check` = query | declarative-predicate | agentVerdict; agentVerdict is
forced advisory; only deterministic checks may block.
- byLoopPoint ordering is materialized in the registry; render-hooks filters the
active set + renders. Same-capability hooks degrade gracefully when an entry
step self-gates.
- Rollout: registry-only until atomic per-feature cutover (no double-execution
with still-inlined workflow features).
Updates the Grilling amendments / Consequences / Alternatives / Open questions
sections; reworks the UI example with `when`.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Three test files copy-pasted the concrete-skill->namespace-router CHILD_ROUTER
map verbatim, and two more duplicated an identical local parseRouterRequires
regex. Introduce tests/helpers/nested-layout.cjs that derives the child->router
map from the authoritative commands/gsd/ns-*.md requires: lists once, reusing
the production parseRequires (now exported from install-profiles) plus a
nestedSkillPath(skillsRoot, prefix, stem) helper. Refactor all five test files
to import from it.
Test-only + a single internal export addition; no production behavior change.
Closes#887
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ADR-857 rollout phase 2e — the largest core.cts extraction. Move the
configuration-loading subsystem (loadConfig + _getConfigDefault/
_getNestedConfigDefault/CONFIG_DEFAULTS/_deepMergeConfig, isGitIgnored +
_gitIgnoredCache, _warnUnknownProfileOverrides + RUNTIME_OVERRIDE_TIERS + the
dedup Sets, _resetRuntimeWarningCacheForTests) out of core.cts into a new leaf
module src/config-loader.cts. core.cts re-exports the public surface
(loadConfig, isGitIgnored, CONFIG_DEFAULTS, RUNTIME_OVERRIDE_TIERS,
_resetRuntimeWarningCacheForTests); 12+ callers unchanged.
Cycle-free: config-loader imports only leaves (configuration, config-schema,
planning-workspace, shell-command-projection, core-utils, model-catalog). All
core-internal helpers loadConfig touches moved with it to avoid a cycle. core
keeps CANONICAL_CONFIG_DEFAULTS for its model-resolver functions, which now
resolve loadConfig via the binding — this unblocks the final model-resolver
extraction (2f). core.cts: 1275 -> 792 lines.
Repointed tests/config-field-docs.test.cjs (a docs-parity source check) to read
the CONFIG_DEFAULTS literal from its new home (config-loader.cjs). New-CLI-module
checklist done (.gitignore, eslint, INVENTORY 95->96 + row, manifest,
ARCHITECTURE, CONTEXT.md "Config Loader Module"). Adds tests/config-loader.test.cjs
(27 tests: behavioral + shim-identity + adversarial config fixtures).
Gates: lint, code-review, security-review (prototype-pollution guard confirmed
intact), codex adversarial-review (0 findings; byte-identical move). Mac 4115
pass; clean-build docker: full-suite hit the local mirror's known incremental-tsc
non-determinism on an unrelated re-exported symbol (findPhaseInternal, from
already-merged 2d), but a clean targeted rebuild of the affected file passed
163/0 — CI's clean full matrix is the authoritative gate.
Closes#885
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Emit the 6 gsd-ns-* routers as the only top-level skill bundles and nest
the ~61 concrete skills under <router>/skills/<name>/SKILL.md on runtimes
with confirmed non-recursive skill loaders (claude global, cline, qwen,
hermes, augment, trae, antigravity). Router bodies rewrite their routing
tables from Skill-tool dispatch to a Read skills/<name>/SKILL.md pattern.
Recursive/unconfirmed loaders (cursor, codex, copilot, windsurf, codebuddy,
opencode, kilo) keep the flat layout. Completes the v1.40 namespace
architecture (#2792) so the eager skill listing drops to ~6 entries.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Address remaining review feedback on PR #743:
- Bump supported-runtime count 15 -> 16 across user-facing READMEs
(docs/README.md + ja-JP/zh-CN translations) now that Kimi is added.
- Add an explicit support-boundary note in the Kimi CLI install section:
the kimi --agent-file custom-agent contract targets legacy/Python
kimi-cli; newer npm Kimi Code (@moonshot-ai/kimi-code) rejects
--agent-file and consumes the same /skill:gsd-* skills via --skills-dir.
- Mirror the boundary in the changeset so release notes are unambiguous.
Historical ADR/changeset/INVENTORY references to '15 runtimes' are left
intact as point-in-time records.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ADR-857 rollout phase 2c. Move 11 shared low-level utilities out of core.cts
into a new leaf module src/core-utils.cts: POSIX path normalization (toPosixPath),
filesystem scanning (detectSubRepos, readSubdirectories, getPhaseFileStats,
pathExistsInternal), and small pure helpers (generateSlugInternal,
extractOneLinerFromBody, filterPlanFiles, filterSummaryFiles, timeAgo, and the
private extractCanonicalPlanId). core.cts re-exports the 10 public ones
(callers unchanged); extractCanonicalPlanId stays private (exported from the
leaf for core's fs-search functions).
Cycle-free: core-utils depends only on Node built-ins + already-leafed modules
(phase-id for comparePhaseNum, planning-workspace for findContextMdIn). This is
the shared leaf that unblocks the phase-locator fs-search extraction (2d) —
searchPhaseInDir/findPhaseInternal/getArchivedPhaseDirs can now take their
utilities from a leaf instead of from core.
New-CLI-module checklist done (.gitignore, eslint, INVENTORY 93->94 + row,
manifest, ARCHITECTURE, CONTEXT.md "Core Utilities Module"). Adds
tests/core-utils.test.cjs (88 tests: behavioral + shim-identity + adversarial).
Gates: lint, code-review, security-review, codex adversarial-review (0
findings). Mac 4041 pass; clean-build docker 12935 pass, 0 fail.
Closes#877
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ADR-857 rollout phase 2b. Move the 6 ROADMAP.md-parsing functions
(stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone,
getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter) + their
interfaces out of core.cts into a new leaf module src/roadmap-parser.cts.
core.cts re-exports them (behavior-preserving); ~9 external callers unchanged.
Resolves the parse/write straddle: roadmap.cts (ROADMAP.md mutation) now imports
its 3 parsing helpers from roadmap-parser.cjs directly instead of reaching
through core. roadmap-parser depends only on leaves (phase-id, planning-workspace,
shell-command-projection) — cycle-free, enabled by phase 2a.
New-CLI-module checklist done (.gitignore, eslint, INVENTORY 92->93 + row,
manifest, ARCHITECTURE, CONTEXT.md "Roadmap Parser Module"). Adds
tests/roadmap-parser.test.cjs (46 tests: behavioral + shim-identity +
adversarial ROADMAP.md fixtures).
Adversarial review surfaced a pre-existing fence-blindness bug in
getMilestonePhaseFilter (matches phase headings inside fenced code blocks);
filed as #875 and left for a separate fix (out of scope for this
behavior-preserving extraction). The two fenced-fixture tests characterize the
current behavior with a #875 reference and flip when it's fixed.
Gates: lint, code-review, security-review, codex adversarial-review (0
correctness findings). gsd-test: clean-build docker + Mac green; the full-suite
docker run's "X is not a function" errors on re-exported symbols were local
incremental-tsc staleness (verified: clean rebuild of the affected files = 195
pass, 0 fail; Mac = 4001 pass).
Closes#870
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
The 'full test (windows-latest, 22)' job runs all 656 unit files in one lane and
has crept to ~14m+ over recent PRs (12m31s -> 13m45s -> 14m18s), so the 15m cap
started cancelling jobs mid-run and red-blocking product PRs even when every test
passes (the job is killed on wall-clock, not a test failure). Raise the full-test
job timeout to 20m to restore headroom. Durable fix (shard the Windows full unit
lane) tracked as a separate follow-up.
Closes#869
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ADR-857 rollout phase 2a — the first cut of the core.cts decomposition.
Move the 9 pure phase-id parsing/matching helpers (escapeRegex,
normalizePhaseName, comparePhaseNum, extractPhaseToken, phaseTokenMatches,
phaseMarkdownRegexSource/Exact, getMilestoneFromPhaseId, getPhaseDirFromPhaseId)
out of core.cts into a new leaf module src/phase-id.cts. core.cts re-exports
them (behavior-preserving); its internal callers resolve the destructured
bindings.
Cycle-safe by design: phase-id depends on nothing in core, so re-export creates
no circular require (the property that made phase-1 io.cts clean). This is the
leaf-first ordering — it unblocks the roadmap-parser extraction (2b), which
imports phaseMarkdownRegexSource.
New-CLI-module checklist: .gitignore, eslint.config.mjs ignores, INVENTORY.md
count 91->92 + row, INVENTORY-MANIFEST.json, ARCHITECTURE.md row, CONTEXT.md
"Phase Id Module" glossary entry. Adds tests/phase-id.test.cjs (63 behavioral
tests incl. shim-identity + adversarial inputs).
Gates: lint, code-review, security-review, codex adversarial-review (all 0
findings), and gsd-test-both (14814 pass on Mac + Linux Docker, 0 fail).
Closes#865
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* 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>
* 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>
ADR-857 rollout phase 1. Move the CLI I/O primitives — output(), error(),
ERROR_REASON, setJsonErrorMode/getJsonErrorMode, and the output() large-payload
temp-file spillover helpers (GSD_TEMP_DIR, ensureGsdTempDir, reapStaleTempFiles)
— out of the 2271-line core.cts god-module into a new, small src/io.cts. core.cts
re-exports them so existing consumers are unaffected (behavior-preserving).
Repoint src/profile-pipeline.cts to import output/error/reapStaleTempFiles from
io directly; graphify/intel/audit were verified not to import these symbols. Net:
the leaf feature modules no longer depend on core just for I/O — the enabling
first cut toward Capability extraction.
New-CLI-module checklist: .gitignore (bin/lib/io.cjs), eslint.config.mjs ignores,
INVENTORY.md count 90→91 + io.cjs row, INVENTORY-MANIFEST.json, ARCHITECTURE.md
core.cjs/io.cjs rows, CONTEXT.md "I/O Module" glossary entry. Adds tests/io.test.cjs
(28 behavioral tests incl. shim-identity and the @file: spillover branch).
Gates: lint, code-review, security-review, codex adversarial-review, and
gsd-test-both (14742 pass on Mac + Linux Docker, 0 fail) all green.
Closes#859
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Record the final-state architecture: the five-step loop (Discuss → Plan →
Execute → Verify → Ship) plus shared-infrastructure skills are the privileged
host/core; every other feature is a Capability (plug-in) selectable at install
and toggleable after restart, attaching through ~12 Loop Extension Points.
Adds docs/adr/857-capability-system.md (Status: Proposed) capturing the eight
resolved design decisions, the resolved design details (extension points,
contribution merge, declaration shape, runtime descriptor, deferred trust gate),
alternatives, consequences, and a six-phase rollout. Records the new domain
terms in CONTEXT.md: Capability, Capability Registry, Loop Extension Point,
Runtime Capability.
Runtime/CLI support is itself a Capability (role: runtime) — Claude Code, Codex,
Antigravity tier-1; the seam is declarative-over-primitives so third-party CLI
support lands later as an additive loader, no rework.
Closes#857
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>