Commit Graph

62 Commits

Author SHA1 Message Date
Tom Boucher
fbd62cd84f feat(#1035): phase 5a — author 16 role:runtime capability descriptors (registry-only) (#1039)
Author capabilities/<runtime>/capability.json for all 16 runtimes (role:runtime),
populating the registry's runtimes index ({} -> 16). Each carries the 6 ADR-1016
axes (configHome structured, configFormat, artifactLayout structured, commandStyle,
hooksSurface + hookEvents, sandboxTier, supportTier), extracted from the live
modules (runtime-homes/runtime-slash/runtime-artifact-layout/runtime-config-adapter
+ CODEX_AGENT_SANDBOX). validateRuntimeBody tightened to enforce the closed
vocabularies + structured configHome/artifactLayout (rejects old string configHome;
env required; skillsHome recursively validated).

Registry-only: bin/install.js + src/*.cts untouched, zero behavior change. The 5b-5f
drive steps consume these one axis at a time (ADR-1016 §8).

Codex accuracy review caught + fixed: opencode/kilo register ZERO lifecycle hooks
(install.js skips the whole block) -> hooksSurface:none (configFormat stays
settings-json); kimi probe selects on <root>/skills existence -> probeExists:"skills".
ArtifactKind required-field strictness + ConverterName closing deferred to 5d/5e.
ADR-1016 (Proposed) amended to match (Decisions 1 + 5).

Closes #1035

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-11 09:34:07 -04:00
Tom Boucher
8bd5e07c58 feat(#1031): autonomous §3a.5 plan:pre ui-phase cutover — last inlined ui-phase site (step-only) (#1033)
Cut over autonomous.md §3a.5 (autonomous plan:pre ui-phase step) to the
loop.render-hooks plan:pre dispatch — completing the ui-phase migration begun
in #1026 (plan-phase.md §5.6). Step-only, non-blocking: autonomous is always
pipeline, so it fires active kind==step hooks and never runs the manual-only
plan:pre blocking gate.

Skip condition keys on "no active step hooks" (not empty activeHooks), so the
gate-only {ui_phase:false, ui_safety_gate:true} case skips silently with no
spurious warning — matching OLD §3a.5. Fires gsd-ui-phase under the identical
precondition (frontend + no UI-SPEC + workflow.ui_phase active), bare
${PHASE_NUM} args. Replaces the inline ui-safety-gate.cjs probe + config-get
with render-hooks + the ui.plan-gate check verb.

Codex caught the gate-only spurious-warning divergence on the first pass; fixed
+ re-confirmed equivalence-preserving. gsd-ui-phase skill, §5.6, §3d.5 untouched.

Closes #1031

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-11 08:31:35 -04:00
Tom Boucher
9ed8c7d574 feat(#1026): §5.6/ui-phase cutover — first gate dispatch (plan:pre step + blocking gate) (#1028)
Replace plan-phase.md §5.6 (a 6-branch inline UI gate) with a capability-driven
loop.render-hooks plan:pre dispatch — the FIRST gate dispatch in any workflow.
A step (ui-phase, when:workflow.ui_phase) + a new blocking gate
(when:workflow.ui_safety_gate). New ui.plan-gate check verb returns
{frontend, hasUiSpec, block}; the dispatch runs it unconditionally then fires
the active step (pipeline) or halts on the active blocking gate (manual). The
gate-handling (run check.query; halt if blocking+block) is the reusable
phase-6 template for blocking-gate cutovers.

Config semantics fixed per #1022 + maintainer call: ui_phase gates plan-time
UI-SPEC generation, ui_safety_gate gates the planning block. Common case + all
ui_phase=false cases are equivalence-preserving; the one intended change is
{ui_phase:true, ui_safety_gate:false} now auto-generating in pipelines.

Review found it broken twice (non-generic dispatch, phase-lookup divergence,
then the step-only check nested in a gate loop) — fixed; final Codex pass
verified all 8 (ui_phase,ui_safety_gate)x{pipeline,manual} cases correct.
gsd-ui-phase skill + autonomous §3a.5 untouched (§3a.5 deferred).
getRoadmapPhaseWithFallback mirrors cmdRoadmapGetPhase for lookup parity.

Closes #1026

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-11 00:58:21 -04:00
Tom Boucher
2ac6592096 feat(#1023): first phase-6 cutover — ui-review (verify:post) inline → loop.render-hooks dispatch (#1024)
Replace the inlined ui-review invocation in autonomous.md §3d.5 with a
loop.render-hooks verify:post dispatch — the first workflow to consume
render-hooks and fire a skill from it (closes the #1018 live-execution residual
as real wiring). Capability-driven, equivalence-preserving for the current
registry (only ui-review at verify:post, default on): fires gsd-ui-review under
the same precondition (UI-SPEC exists via consumes-gate + workflow.ui_review).

Gate findings (real pattern issues, fixed so every future cutover inherits them):
- bug-2643 static "Skill() references a real skill" check vs templated
  Skill(skill="gsd-${ref.skill}") dispatch → skip ${...}-templated names.
- Coverage moved, not lost: gen-capability-registry now validates
  steps[].ref.skill in skills + ref.agent in agents + rejects gsd- double-prefix.
- Tightened §3d.5 tests; markdown clarity (consumes rule, LLM-native JSON read,
  UI-REVIEW.md score hint).

gsd-ui-review skill + §3a.5/ui-phase untouched. §5.6/ui-phase cutover deferred
(#1022 step-can-halt-vs-gate model question).

Closes #1023

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-10 22:40:58 -04:00
Colin Johnson
76f42ddb4b feat(#1014): add Claude Fable 5 model config (#1015) 2026-06-10 20:32:26 -04:00
Jeremy McSpadden
092340d18a fix(#711): wire autonomous convergence flag (#729)
* fix(#711): wire autonomous convergence flag

* Update wise-ibex-tumble.md

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-10 16:10:07 -04:00
Jeremy McSpadden
f61b97276e fix(#724): block convergence on actionable review findings (#728)
* fix(#724): block convergence on actionable review findings

* merge: integrate clean next (#936 inline) onto author tip + re-apply cursor fixes (Mode field, REVIEWS.md extraction) and review hardening (#724)

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-10 14:54:15 -04:00
Jeremy McSpadden
fb37fa7dd5 fix(#725): route Codex gsd-tools calls through shim (#731)
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-10 14:44:43 -04:00
Joe
5e8a723089 feat(templates): add optional Business Context section to PROJECT.md template (#756)
* feat(templates): add optional Business Context section to PROJECT.md template

Adds an optional `## Business Context` section (Customer, Revenue model,
Success metric, Strategy notes) between Core Value and Requirements, for
monetized or customer-facing projects. Optional by default — an HTML comment
tells non-business projects to delete it; capped at four one-line fields to
stay a constraint reference, not a business plan. The milestone evolution
review in complete-milestone.md checks it only when the section is present.

Refs #72

* chore(changeset): set pr number for #72 fragment

* test(#72): add source-text-is-the-product exemption marker

Addresses review Minor #1 on PR #756. The contract test reads the
PROJECT.md template and complete-milestone workflow .md files and
asserts on their content (the local/no-source-grep pattern). Those
.md files ARE the product surface, so this is a valid
source-text-is-the-product case. Add the explicit // allow-test-rule
marker per RULESET.TESTS.no-source-grep.exemption so intent is
audit-traceable before the rule promotes to error (#453).

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-10 14:13:01 -04:00
Tom Boucher
4c10eb2253 fix(#991): inject configured agent_skills into code-review family subagents (#1005)
* fix(#991): inject configured agent_skills into code-review family subagents

code-review.md, code-review-fix.md, and eval-review.md spawned their
subagents (gsd-code-reviewer / gsd-code-fixer / gsd-eval-auditor) without
querying or injecting the project-configured agent_skills, while ~20 sibling
workflows do. Subagents don't inherit the orchestrator's auto-loaded context,
so this injection is the only channel — reviewers/fixers/auditors silently ran
without the configured rule/skill context.

Mirror the established sibling idiom: add
`VAR=$(gsd_run query agent-skills <agent-type>)` in each workflow's initialize
step and interpolate `${VAR}` into every Agent() spawn of that type. This
covers all spawn sites, including code-review-fix.md's --auto loop which
re-spawns gsd-code-reviewer in addition to the two gsd-code-fixer spawns.

Regression test reads the workflow text (source-text-is-the-product) and
asserts each file queries agent-skills for every agent type it spawns and
interpolates the result at least once per spawn.

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

* chore(#991): add changeset for code-review agent_skills injection fix

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-10 14:07:23 -04:00
Tom Boucher
972a41a528 fix(#967): make verify key-links docs author-strict (from:/to: are file paths; symbols go in via:) (#990)
* fix(#967): make verify key-links docs author-strict (from:/to: are file paths; symbols go in via:)

Closes #967

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

* chore(#967): backfill changeset pr number (990)

---------

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-10 11:10:40 -04:00
Tom Boucher
626575cbc5 fix(#978): parse --force in milestone complete dispatcher so the guard's documented override works (#982)
* fix(#978): parse --force in milestone complete dispatcher so the guard's documented override works

The dispatcher built `{ name, archivePhases }` but never parsed `--force`, so
`options.force` was always `undefined` and the guard inside `cmdMilestoneComplete`
(which tells users to "Re-run with --force to override") could never be bypassed.

Add `const force = args.includes('--force')` and pass it into the options object.
The guard already honors `options.force` — no changes to milestone.cts needed.

Closes #978

* chore(#978): backfill changeset pr number (982)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-10 10:40:06 -04:00
Tom Boucher
caca4d255c feat(#985): intel command cutover — commands-only capability, last first-party family (ADR-857 phase 4d-impl-4) (#988)
* feat(#985): intel command cutover — commands-only capability, last first-party family (ADR-857 phase 4d-impl-4)

Migrate the intel CLI command family from a hardcoded gsd-tools.cjs case arm to
a registry-dispatched Capability (commandFamilies mechanism, #961), mirroring
the graphify (#972) and audit (#984) cutovers. New src/intel-command-router.cts
exports routeIntelCommand reproducing all 9 subcommands verbatim (incl. the
status timeAgo non-raw post-processing), lazily requiring intel.cjs inside the
route fn. capabilities/intel/capability.json declares the intel command family;
commands-only (skills:[]), declares the existing intel.enabled gate (default
false — behavior unchanged).

Behavior-CHANGING (dispatch path) but equivalence-proven: CLI output identical;
existing intel.test.cjs passes unchanged. Completes the first-party command-
family cutover sequence (graphify/audit/intel).

Closes #985

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

* test(#985): compute expected planningDir via path.join in intel cutover unit tests (Windows CI)

The intel-command-cutover unit-mock assertions hardcoded a POSIX `/.planning`
expectation while the router builds it with path.join(cwd, '.planning') →
backslashes on Windows, so the planningDir-arg assertions (query/status/diff/
snapshot/validate/update/api-surface) failed only on windows-latest CI. Compute
the expectation with path.join (cross-platform); production router unchanged.

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-10 10:00:30 -04:00
Tom Boucher
9a03539c2d feat(#981): audit-uat + audit-open command cutover — commands-only capability (ADR-857 phase 4d-impl-3) (#984)
Migrate the audit-uat + audit-open CLI commands from hardcoded gsd-tools.cjs
case arms to a registry-dispatched Capability (commandFamilies mechanism, #961),
mirroring the graphify cutover (#972). New src/audit-command-router.cts exports
routeAuditUat/routeAuditOpen, each lazily requiring only its backing module
(uat.cjs/audit.cjs) inside the route fn — matching the old per-case lazy loads.
capabilities/audit/capability.json declares the two command families;
commands-only (skills:[]), no config gate, audit_review cluster untouched.

Behavior-CHANGING (dispatch path) but equivalence-proven: CLI output identical;
existing audit regression tests pass unchanged.

Closes #981

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-10 08:34:49 -04:00
Tom Boucher
77bfd943dd feat(#972): graphify command cutover — first capability owning a command family (ADR-857 phase 4d-impl-2) (#975)
Migrate graphify into a Capability that owns its `graphify` command family,
dispatched via the registry (#961 mechanism) instead of a hardcoded case.
graphify is now an enable/disable plug-in.

- src/graphify-command-router.cts: routeGraphifyCommand (standard route*Command),
  reproduces the removed case EXACTLY (query +--budget, status, diff, build,
  hidden build snapshot, usage/unknown errors); injectable _graphify test seam.
- capabilities/graphify/capability.json: role feature, tier:full, skills:[graphify],
  config:{graphify.enabled default false}, commands:[{family:graphify, module,
  router:routeGraphifyCommand}].
- removed case 'graphify' from gsd-tools.cjs; graphify now flows
  default -> dispatchCapabilityCommand -> commandFamilies.graphify -> router.
- regenerated registry (commandFamilies/bySkill/configSchema/profileMembership/
  capabilityClusters for graphify); tier:full keeps 4c install/surface a no-op.

Equivalence-proven: 8 recording-mock unit tests assert the exact fn+args per
subcommand (budget, snapshot-vs-build); 9 subprocess tests assert distinguishing
output shapes; existing graphify tests pass unchanged through the new path.

Surfaced (not silently accepted): the pre-existing --budget-no-value NaN no-op
quirk, preserved for equivalence, filed separately.

Closes #972

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-10 07:12:57 -04:00
Tom Boucher
0c567c17e4 feat(#961): capability command mechanism (commandFamilies index + default-case dispatch) — ADR-857 phase 4d-impl-1 (#964)
* feat(#961): capability command mechanism — commandFamilies index + default-case dispatch (ADR-857 phase 4d-impl-1)

Build the capability command mechanism per ADR-959: the `commands` declaration
field on the feature role, the registry commandFamilies index, and a real
dispatchCapabilityCommand consulted in runCommand's default case (replacing the
dead _dispatchNonFamily shim's role).

The registry DISCOVERS a standard route*Command (no rebuilt handler table). On a
default-case command, dispatch enforces a bare-.cjs-basename, resolves the module
under gsd-core/bin/lib/, asserts confinement, requires the resolved path, and
own-property-guards the router export before calling it. A require.main===module
guard makes gsd-tools.cjs importable for tests; the CLI path is unchanged.

Additive: commandFamilies is empty today, so the default case is behavior-
preserving for every command; the 10 dead _dispatchNonFamily sites are untouched
(future migration markers). The graphify cutover is the separate next step.

Closes #961

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

* fix(#961): surface capability router failures structurally + enforce sync contract

Review follow-up: dispatchCapabilityCommand now wraps the router invocation so an
unexpected (non-ExitError) throw is converted to a structured, attributed
error(msg, SDK_FAIL_FAST) — honoring --json-errors — instead of escaping as a raw
stack trace; an intentional ExitError propagates unchanged. An async router
(returns a thenable) is rejected loudly with a structured error (the contract is
synchronous, like the 12 host routers). Not shipped as "consistent with existing
behavior": the host's pre-existing version of this gap is filed as #965.

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-10 01:03:46 -04:00
Tom Boucher
a313a7e304 fix(#950): emit status: complete in quick-task SUMMARY frontmatter (#951)
* fix(#950): emit status: complete in quick-task SUMMARY frontmatter

Add `status: complete` to all four SUMMARY templates (summary.md,
summary-minimal.md, summary-standard.md, summary-complex.md), to the
executor agent's documented frontmatter field list, and to the quick.md
executor constraints block. The audit-open milestone-close scanner
(scanQuickTasks) reads this field to decide whether a quick task is done;
without it the scanner falls back to `[unknown]` and false-flags finished
tasks as open. Writer-side fix; the scanner is correct and unchanged.

Blast-radius: no other scanner reads `status:` from phase-plan SUMMARY
files. Phase disk_status is derived from file-count heuristics only.
Adding the field to the shared template is therefore safe and the value
`complete` is semantically accurate for a finished plan.

Regression test: tests/bug-950-quick-summary-status-complete.test.cjs
- RED: 4 template-contract tests fail before fix, behavioral tests pass
- GREEN: all 8 tests pass after fix

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

* chore: add changeset for fix/950-quick-summary-status-complete (#951)

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

* test(#950): assert writer-path contract + scope template checks to YAML frontmatter (adversarial review)

- Add `// allow-test-rule: source-text-is-the-product` at file top (before block comment)
- Add `extractFrontmatter()` helper that handles both leading-frontmatter files
  (summary-minimal/standard/complex.md) and fenced-frontmatter files (summary.md,
  whose frontmatter is embedded inside a ```markdown fence) — assertions now
  target the actual YAML block, not the whole file
- Scope all four [TEMPLATE CONTRACT] tests through extractFrontmatter() so a stray
  `status: complete` in prose/examples cannot produce a false green; error messages
  now print the extracted block to aid diagnosis
- Add [WRITER-PATH] quick.md test: asserts the <constraints> block instructs the
  executor to write `status: complete` in SUMMARY frontmatter
- Add [WRITER-PATH] gsd-executor.md test: asserts the Frontmatter spec documents
  `status: complete` as a required field
- Sanity-checked: guards fail when `status: complete` is removed from a template
  or from quick.md, and pass once restored

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-10 00:22:33 -04:00
Tom Boucher
dc139b38e0 feat(#949): install/surface consume derived profiles/clusters (ADR-857 phase 4c) (#954)
Make install + surface read the registry's derived profileMembership/
capabilityClusters so a capability's tier drives what installs + surfaces.
resolveProfile (when given the registry) unions capability skills for the
profiles its tier implies before the requires: closure; resolveSurface merges
capabilityClusters into the cluster map. bin/install.js, /gsd:surface, and the
capability-state resolver all thread the registry.

Shipped as a proven no-op: the UI capability is reconciled to tier:full (its
skills were full-only in the hand-authored profiles), so it contributes only to
the full profile (already the '*' sentinel) and core/standard are unchanged.
Equivalence tests prove resolveProfile/resolveSurface/listSurface/staging/
capability-state are identical with vs without the registry; the core-alias
staging path is verified equivalent (empty manifest → raw PROFILES.core).

Closes #949

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 22:35:42 -04:00
Tom Boucher
85cfa5dc13 feat(#945): unified capability-state resolver (ADR-857 phase 4b) (#946)
Add a read-side query composing the three toggle systems into one
per-capability view. resolveCapabilityState({registry, installedSkills,
surfacedSkills, config, cwd}) reports installed (skills ⊆ resolved install
profile), surfaced (skills ⊆ resolved surface), and per-hook active (no when →
active; non-empty-string when → resolved via _resolveActivationValue; empty/
non-string → inactive), with no forced composite verdict. cmdCapabilityState
does the I/O (resolveProfile + resolveSurface + loadConfig), resolves the
runtime config dir via the canonical getGlobalConfigDir (--config-dir override),
and surfaces resolution failures as warnings rather than a false installed='*'.
Routed as `gsd-tools capability state`.

Additive: install/surface/workflows untouched; consumed by nothing.

Closes #945

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 16:18:23 -04:00
Tom Boucher
ed467cd8f2 feat(#942): tier→profile/cluster derivation + consistency gate (ADR-857 phase 4a) (#943)
Establish `tier` as the source of install-profile + cluster membership
(ADR-894 §4). The registry now derives two views: capabilityClusters
(capId → its skills) and profileMembership (capId → {tier, profiles}, where
profiles is the PROFILE_RANK suffix from the capability's tier). A consistency
gate cross-checks them against the hand-authored PROFILES/CLUSTERS: HARD (throws)
on a capId-matching-a-cluster-name with a different skill set; SOFT
(pending-reconciliation stderr warning, not serialized) when a capability skill
isn't yet in the closure-resolved hand-authored profile.

The SOFT gate loads the real skills manifest and resolves each profile's closure
once so transitively-included skills don't false-warn; warnings are de-duped to
one per (capability, skill); both derived views are scoped to skill-owning
capabilities; serialized with a global capId sort for determinism; reserved-name
guards at every write site; lazy requires of the built constants.

Behavior-preserving: install/surface untouched; derived views consumed by
nothing. Full generation rides along with the phase-6 migration.

Closes #942

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 15:11:42 -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
27aca73b6e Merge branch 'next' into kimi-runtime-support 2026-06-09 10:15:41 -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
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
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
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 Johnson
bf8813b7a0 Merge branch 'next' into kimi-runtime-support 2026-06-08 23:04:37 -04:00
Tom Boucher
a90c654745 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>
2026-06-08 22:51:40 -04:00
Tom Boucher
48cc27bd84 feat(#903): generate Loop Host Contract from workflow markers (ADR-857 phase 3a-impl-2) (#906)
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>
2026-06-08 22:16:58 -04:00
Tom Boucher
ad754ca6cd feat(#896): Capability Registry generator + UI pilot (ADR-857 phase 3a-impl) (#902)
* 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>
2026-06-08 21:15:41 -04:00
Viktorplus
4b0fcd83a0 Merge branch 'next' into kimi-runtime-support 2026-06-08 19:05:16 +02:00
Tom Boucher
3697e6768f 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>
2026-06-08 10:42:05 -04:00
Viktorplus
54191f6c6c Merge branch 'next' into kimi-runtime-support 2026-06-08 03:57:58 +02:00
Tom Boucher
40d48c0508 feat(#815): add /gsd-update --next to install the @next RC channel (#839)
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
2026-06-07 20:22:07 -04:00
Viktorplus
4d6f27ae32 Merge branch 'next' into kimi-runtime-support 2026-06-08 01:08:28 +02:00
Tom Boucher
2860e995b5 feat(#773): add --ephemeral and --dangerously-bypass-hook-trust to automated codex exec wrappers (#824)
* 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>
2026-06-07 18:54:05 -04:00
Viktorplus
7f868dcc6b fix: close Kimi runtime review gaps 2026-06-07 23:40:18 +02:00
Viktorplus
4e12967683 Merge remote-tracking branch 'upstream/next' into kimi-runtime-support 2026-06-07 11:56:18 +02:00
Tom Boucher
4056d830bc refactor(#651): consolidate verification-status routing into one queryable seam (#755)
* refactor(#651): consolidate verification-status routing into one queryable seam

The passed/gaps_found/human_needed verification status was re-encoded as
bare strings across three prose surfaces (gsd-verifier emits, execute-phase
routes, ship gates), each independently deciding the per-status next action
with no parity coupling — the DEFECT.GENERATIVE-FIX class.

Give the enum one home: src/verification.cts (-> bin/lib/verification.cjs)
exposing `gsd_run query verification.status <phaseDir>` returning a typed
{status, next_action, next_command}. ship.md and execute-phase.md now consume
the query instead of re-deriving the routing in prose; gsd-verifier.md points
at the shared vocabulary as the single emitter (values unchanged).

Also fixes the latent broad-grep status misread (DEFECT.FRONTMATTER-SCALAR-
BROAD-GREP): execute-phase.md read `grep "^status:"` over the whole report, so
a body `status:` line could misroute a valid phase. Extraction is now
frontmatter-scoped in one place. A parity test fails if a verifier status
gains no route. Lands the two CONTEXT.md DEFECT entries captured on the issue.

Closes #651

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

* chore(#651): set changeset pr to 755

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 01:26:56 -04:00
Tom Boucher
f7e902f1cf feat(#52): add agent_skills_security.trusted_global_roots allowlist for global skills (#754)
* feat(#52): add agent_skills_security.trusted_global_roots allowlist

Opt-in allowlist so a global: agent skill whose SKILL.md realpath resolves
outside the default global skills base (e.g. ~/.claude/skills) is accepted
when its real target lies under a user-declared trusted root. Default [] is
byte-identical to prior behavior; the symlink-escape guard is preserved and
simply re-applied against each declared root.

- src/security.cts: loadTrustedGlobalRoots — tilde-expand (~ and ~/), reject
  project-relative and dangerously broad roots (filesystem/UNC root, homedir),
  realpath-canonicalize each root every run and drop non-existent ones.
- src/init.cts: on base-check failure the guard consults the trusted roots
  (hoisted out of the loop); emits a stderr NOTE when a skill is accepted via
  a trusted root so the widened boundary is visible.
- src/core.cts: thread agent_skills_security through loadConfig.
- config-schema.manifest.json: allow the new key path.
- docs/CONFIGURATION.md: document the option and its security model.
- tests/agent-skills.test.cjs: unit + end-to-end CLI coverage (regression,
  feature, negative, broad-root hardening, stderr NOTE).

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

* docs(#52): add changeset fragment for trusted_global_roots (#754)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 01:06:41 -04:00
Tom Boucher
7e76f1a736 feat(#703): add --granularity override flag to /gsd:plan-phase (#750)
* feat(#703): add --granularity override flag to /gsd:plan-phase

Add a `--granularity <coarse|standard|fine>` flag to /gsd:plan-phase that
overrides the configured planning granularity for a single invocation.

The override is a new highest-priority tier above the existing precedence
chain (granularities[phaseType] -> granularity -> planning.granularity ->
'standard') in resolveGranularityInternal; when the flag is absent, resolution
is byte-for-byte unchanged. cmdInitPlanPhase now resolves with phaseType
'planning' so granularities.planning participates, and emits the resolved
value in the init JSON, which the plan-phase workflow forwards to the planner
prompt. Invalid values are rejected at the CLI boundary via a shared
assertValidGranularityOverride helper.

Closes #703

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

* chore(#703): set changeset pr to 750

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 23:56:53 -04:00
Tom Boucher
cf8bd3cd5e fix(#683): auto-degrade phase execution to sequential on worktree base mismatch (#749)
* fix(#683): auto-degrade phase execution to sequential on worktree base mismatch

Claude Code forks worktree-isolated executors off the repository default
branch (origin/HEAD), not the orchestrator's HEAD. Running /gsd-execute-phase
on a branch diverged from the default (unmerged milestone/feature branch) left
every executor without the phase's plan files and tripped the
worktree-branch-check guard with `exit 42` — 100% reproducible, all OSes.

- New module src/worktree-base-ref.cts: HEAD-vs-fork-base drift detection
  (origin/HEAD with symbolic-ref fallback) and no-clobber worktree.baseRef
  management, exposed as `worktree base-check` / `worktree set-baseref`.
- execute-phase.md: pre-dispatch, for Claude Code with worktrees enabled,
  auto-degrades the run to sequential on the main tree when a base mismatch
  is detected, recommending worktree.baseRef:"head". The exit-42 guard stays
  as a backstop.
- Installer: fresh local Claude installs set worktree.baseRef:"head" in
  .claude/settings.local.json (no-clobber, respecting an explicit shared
  settings.json value); upgrades print an opt-in notice pointing at
  `gsd-tools worktree set-baseref`.
- Docs: how-to guide, CLI/config reference, planning-config cross-ref.

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

* fix(#683): auto-apply worktree.baseRef on upgrade; gate fresh+upgrade on use_worktrees

Per maintainer direction: on a local Claude Code UPGRADE, set
worktree.baseRef:"head" automatically (no opt-in notice) when the project's
workflow.use_worktrees is enabled, instead of merely printing a remediation
notice. For consistency the FRESH path is now gated the same way: both paths
compute worktrees-enabled once (bounded walk-up read of .planning/config.json,
default enabled unless workflow.use_worktrees === false) and apply the
no-clobber baseRef only when enabled — never overwriting an explicit value in
settings.local.json or a shared settings.json. gsd-tools worktree set-baseref
remains for manual use. Docs + changeset updated; tests hardened (file-exists
assertions, fresh+disabled case, upgrade idempotency).

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

* fix(#683): measure workflow byte-budget on LF, fixing Windows-only CI failure

The workflow-size-budget test failed only on Windows: git checks out the .md
files as CRLF (no eol=lf in .gitattributes) and byteCount used
fs.statSync().size (raw on-disk bytes), counting an extra \r per line. That
inflated execute-phase.md — the XL high-water-mark file pinned near its ceiling
by the tighten-only ratchet — from 88492 LF bytes to ~90245 on Windows, over
the 90000 XL ceiling, while passing on the LF-checkout Mac/Linux runners.

The ceilings are explicitly "calibrated against raw `wc -c`" on an LF checkout,
so the measurement should be LF-based on every platform. byteCount now reads the
file and counts Buffer.byteLength after stripping CR, making the budget
platform-independent (a no-op on LF checkouts; verified statSync === normalized
for all 88 workflow files). No ceilings changed. Added a regression test
asserting CRLF and LF content of the same file count identically.

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

* fix(#683): make worktree-base-ref test path mocks Windows-safe (path.join)

tests/worktree-base-ref.test.cjs keyed its injected readFile/writeFile mocks
(and a few expected `file` values) with forward-slash template literals like
`${claudeDir}/settings.local.json`. The module composes those paths with
path.join(), which emits backslashes on Windows, so the mock keys never matched
the module's lookup → readFile returned null → resolveEffectiveBaseRef /
cmdWorktreeBaseCheck / cmdWorktreeSetBaseRef (and the JSONC variants) failed on
the Windows full-test runner only (they passed on Mac/Linux, and the install
tests passed because they use the real filesystem). The module is correct;
only the test fixtures hardcoded '/'.

All mock keys and path assertions now use path.join(base, ...) mirroring the
module, so they match on every platform (no-op on POSIX). 19 path references
across 16 lines.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 23:40:24 -04:00
Viktorplus
86836ebd2a Merge branch 'open-gsd:next' into kimi-runtime-support 2026-06-07 00:53:20 +02:00
Tom Boucher
1bea220d58 refactor(#720): lazy-load MVP-only reference bodies on non-MVP runs (#746)
* refactor(#720): lazy-load MVP-only reference bodies (eager @-import → gated Read)

Convert eager @-imports of MVP-only reference bodies into lazy "Read" instructions
gated on MVP_MODE / WALKING_SKELETON / MVP+TDD, so non-MVP planning/execution runs
no longer pull MVP guidance into context. Covers both the workflow files and the
planner/executor agent definitions (the dominant context-cost path):

- workflows/plan-phase.md: planner-mvp-mode.md + skeleton-template.md (L146/936/937/941)
- workflows/execute-phase.md: execute-mvp-tdd.md halt-report ref, now gated on gate-trip (L191)
- agents/gsd-planner.md: planner-mvp-mode.md, user-story-template.md, skeleton-template.md
- agents/gsd-executor.md: execute-mvp-tdd.md

The dedicated always-MVP mvp-phase workflow keeps its eager imports (intentional).
Behaviour is unchanged; non-MVP runs simply carry less loaded context. Adds a
regression guard mirroring the discuss-phase lazy-load test, and documents the
conformance in docs/ARCHITECTURE.md.

Refs #720

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

* docs(#720): add changeset fragment (pr #746)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 18:23:17 -04:00
Tom Boucher
b79b767629 refactor(bin): replace process.exit() in CLI entrypoints + ratchet rule to error (#738) (#741)
Part 2 of 2 of the n/no-process-exit cleanup (completes umbrella #738; part 1
was #739/scripts). Converts the 20 flagged process.exit() calls in the three
hand-written gsd-core/bin CLI entrypoints and flips n/no-process-exit to error.

- New src/cli-exit.cts -> gsd-core/bin/lib/cli-exit.cjs (ExitError + runMain),
  the gsd-core-side equivalent of scripts/lib/cli-exit.cjs; registered in
  .gitignore, eslint ignores, and the inventory manifest like its siblings.
- gsd-tools.cjs: 13 apply-prompt-budget exits -> throw ExitError; main()->runMain.
- verify-reapply-patches.cjs: 6 exits -> throw ExitError / return verdict; runMain.
- check-latest-version.cjs: 1 exit -> return verdict; runMain.
- eslint.config.mjs: n/no-process-exit warn -> error.

Scope note: the gsd-core/bin/lib/*.cjs modules (core, state, profile-pipeline,
roadmap-command-router, adr-parser, ui-safety-gate) are tsc-generated and
eslint-ignored (ADR-457), so their process.exit calls were never flagged and are
intentionally left untouched. Only the linted hand-written entrypoints are in scope.

Exit codes verified unchanged for all three entrypoints.

Closes #738

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 16:27:05 -04:00
Viktorplus
d008c83ec9 feat(01-02): add Kimi runtime name policy
- Register canonical kimi in runtime alias manifest and fallback policy
- Add focused canonicalization coverage without extra aliases
2026-06-06 22:16:19 +02:00
Tom Boucher
ba231ecbfc chore: clean up clear-cut ESLint warnings (#732) (#734)
Pay down pre-existing error→warn lint debt. Removes dead imports/vars, unused functions, redundant regex/string escapes, and stale eslint-disable directives; converts unused `catch (_e)` to optional catch binding (src/*.cts).

No behavior change. Lint 345→125 warnings (0 errors); deferred categories (n/no-process-exit, test-sleeps, control-regex) tracked in #732 for follow-up. Full test suite green (0 failures); code-review verified all removals unused and all escape fixes semantics-preserving.

Closes #732

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:24:48 -04:00