8f2ebbe9bfbb98a1fc15cab36c3f6d5618eac341
629 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8f2ebbe9bf |
feat(#1928): remove sunset Gemini CLI runtime, redirect to Antigravity (#1996)
* feat(#1928): remove sunset gemini cli runtime, redirect to antigravity Google sunset Gemini CLI on 2026-06-18; Antigravity CLI is its official successor (already a first-class GSD runtime). Remove the gemini runtime from the enum (16->15), aliases, labels, config-home fragment, install path, converters (convertClaudeToGemini{Markdown,Toml,Agent}, convertSlashCommandsToGeminiMentions), capability descriptor, gemini-extension.json, RULESET.GEMINI.*, and the interactive menu (renumbered, no gap). --gemini now prints an explicit deprecation notice citing the 2026-06-18 sunset and redirects to --antigravity (no silent alias, per the issue's Hyrum's-Law rejection). Antigravity is preserved throughout: its GEMINI.md contextFileName, .gemini/antigravity config home, the shared convertGeminiToolName/claudeToGeminiTools tool vocabulary, and the 'gemini' hookEvents dialect it declares. GEMINI.md retargeted as Antigravity's context file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#1928): backfill changeset PR number (#1996) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#1928): drop Gemini CLI from issue templates (review nit) Removes the sunset Gemini CLI runtime from the two GitHub issue-template runtime lists that the removal PR missed, per @davesienkowski's review nit: - feature_request.yml: 'Applicable runtimes' checkbox (a user could otherwise request a feature for a runtime GSD no longer supports) - bug_report.yml: 'Runtime' dropdown + the stale ~/.gemini/settings.json retrieval-help line Leaves the post-removal templates fully consistent with the Antigravity redirect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
55604e9124 |
fix(#1906): require node-test clean-fixture causation control (#2001)
* fix(#1906): require node-test clean-fixture causation control The node-test fail-first proof accepted a deceptive content-independent negative test — one that reds merely because GSD_PROHIB_SUBJECT is set, ignoring the subject's content — whenever no cleanFixture was supplied, because #1346's causation control was opt-in. The proof's observed signal (RED) thus diverged from its target (RED caused by content) by default. Make the causation control mandatory for the node-test kind: a descriptor that omits cleanFixture is un-provable (fail-closed), never accepted under the weaker violation-only proof. When a clean fixture is present, fail-first is proven exactly as before (RED on violation AND non-vacuous GREEN on clean). The lint-rule kind is unchanged (its subject IS the linted file; no GSD_PROHIB_SUBJECT indirection). Breaking (Hyrum): a previously-green node-test prohibition with no clean fixture now hard-gates — blast radius is zero in-tree (no node-test prohibition ships today; only the lint-rule local/no-source-grep dogfood). Supersedes ADR-1606 Decision 4 / ADR-550 #1346 addendum's opt-in. Closes #1906 Claude-Session: https://claude.ai/code/session_017vYn26e3nkDNxcpty1ciPJ * docs(#1906): supersede the #1346 opt-in causation control (mandatory for node-test) Record the node-test mandatory-causation-control supersede across the governing surfaces: - ADR-1606 (the enforcement decision-of-record): addendum + Decision 4 annotated + the "Mandatory causation control — REJECTED" alternative flipped to accepted (premise no longer holds: zero in-tree node-test consumers). - ADR-550: the 2026-06-21 #1346 "Why opt-in, not required" paragraph marked SUPERSEDED, pointing at ADR-1606. - spec-phase.md: check_clean_fixture is now REQUIRED for node-test (was "optional"). - CONTEXT.md: PROHIB.enforce.causation predicate updated. Regenerated the shipped-artifact cascade from the spec-phase.md edit (+149 B, well under the 40960 cap): 16 golden-install-parity fixtures and the workflow size baseline. Refs #1906 Claude-Session: https://claude.ai/code/session_017vYn26e3nkDNxcpty1ciPJ |
||
|
|
5ae4ea4c84 |
feat(#1105): add external-job capability (SLURM scheduler-adapter producer half) (#1998)
* feat(#1105): add external-job capability (SLURM scheduler-adapter producer half) The async external-job consumer half (#1165) shipped long ago: the core loop reads .planning/async-jobs/<job>.json manifests and treats a non-terminal one as the legal external_job_waiting half-state. The PRODUCER half (#1164) was the remaining unimplemented piece of #1105. This adds the producer as a default-off capability: - capabilities/external-job/ — capability.json (execute:wave:post -> executor, plan:post -> planner contributions, external_job.* config keys, default-off) + fragments teaching runtime-budget classification and externalization. - src/external-job.cts -> gsd-core/bin/lib/external-job.cjs — pure producer module: SLURM state -> manifest-status map (no guessing), manifest build/validate (versioned stability contract), sbatch/squeue/sacct parsers, and a fail-closed manifest writer (refuses a second non-terminal job for a plan_id already in flight; refuses to clobber a malformed manifest). fs/clock seams for deterministic tests. - scripts/slurm-adapter.cjs — operator CLI (submit/poll/show) wrapping bounded sbatch/squeue/sacct subprocesses; surfaces manifest commands for confirmation and never auto-runs them (trust boundary). - tests/external-job.test.cjs — 23 behavioral + fast-check property tests. - docs/reference/long-running-operations.md + docs/how-to/async-external-jobs.md. - CONTEXT.md glossary entry for the External-job Capability. - Regenerated capability-registry.cjs; pruned the now-stale test-file-count allowlist entry (external-job is at the 2-file cap). * chore(#1105): backfill PR number in changeset * fix(#1105): sync capability artifacts + update registry shape-pin tests gsd-test caught that adding the external-job capability requires its dependent artifacts regenerated and its registry-shape drift absorbed: - sync-manifest-versions: stamp 1.7.0-rc.2 into capability.json (was 1.0.0). - gen-capability-matrix --write: regenerate docs/reference/capability-matrix.md. - gen-inventory-manifest --write: regenerate docs/INVENTORY-MANIFEST.json. - check-gap-analysis-plan-post-e2e: plan:post now has 1 contribution (external-job planner fragment) instead of 0. - execute-wave-post-gate-pipeline-e2e: execute:wave:post now has 2 contributions (mempalace + external-job) instead of 1. * fix(#1105): regenerate capability-registry after version stamp sync-manifest-versions re-stamped external-job/capability.json from 1.0.0 to 1.7.0-rc.2 after the last registry regeneration, leaving the committed capability-registry.cjs stale (CI gen-capability-registry --check failed). gsd-test masked this because its setup runs the full 'npm run build' (which regenerates the registry); CI's 'npm test' pretest only runs build:lib. |
||
|
|
e5ef323b15 |
feat(#1787): add /gsd:next smart entry workflow (#1798)
* docs: design spec for /gsd smart-entry command
Hybrid approach porting gsd-pi's smart-entry wizard to gsd-core:
deterministic classifier (gsd-tools smart-entry --json) + markdown
command/workflow with AskUserQuestion + --text fallback. Routing-first
('what now?' menu), 10 situations redesigned for gsd-core's phase loop.
* feat: add /gsd-start smart-entry command
State-aware front door adapted from gsd-pi's smart-entry wizard,
redesigned for gsd-core's markdown-first, multi-runtime architecture.
- src/smart-entry.cts: deterministic situation classifier (no-project,
paused, blocked, verify-failed, needs-first-phase, planning, executing,
verify-pending, idle-stranded, complete, unknown). Reads STATE.md,
ROADMAP.md, git, and verify signals; emits JSON the workflow consumes.
- gsd-tools.cjs: wire case + help listing.
- commands/gsd/start.md + gsd-core/workflows/gsd.md: thin markdown
dispatcher presenting an AskUserQuestion menu (with --text fallback for
non-Claude runtimes) and dispatching to existing commands. Falls back
to /gsd:progress if detection is unavailable.
- help.md: document /gsd:start (parity with bug-2954).
- tests: smart-entry.unit.test.cjs (classifier behavior across all
situations + priority + JSON shape) and gsd-workflow.structure.test.cjs
(markdown-layer invariants + every emitted command resolves to a real
slash command).
Spec: docs/superpowers/specs/2026-06-27-gsd-smart-entry-design.md
Note: command-contract (ADR-0002) requires a gsd:* prefix, so the bare
/gsd from the spec surfaces as /gsd-start.
* refactor: rename smart-entry command to /gsd:next
Rename the command from /gsd:start to /gsd:next per feedback. The
command file is now commands/gsd/next.md (name: gsd:next) and the
backing workflow is gsd-core/workflows/smart-entry.md (named for the
smart-entry classifier and gsd-tools smart-entry subcommand; does not
collide with the existing workflows/next.md, which is the progress
--next sub-workflow). help.md and the spec updated to match.
All affected tests (188) pass; lint:ci clean.
* fix: smart-entry reads real STATE.md schema (nested progress YAML + body Phase field)
Codex review found the classifier misread this repo's own STATE.md: it
looked only for scalar current_phase/total_phases frontmatter and body
fields named 'Current Phase'/'Total Phases', but real STATE.md stores
the phase as body 'Phase: N' and total_phases/percent under a nested
'progress:' YAML object. Both came back null, so active projects
(e.g. this repo at Phase 3 / verifying) wrongly classified as
needs-first-phase.
- detectSignals now reads total_phases + percent from nested progress{}
first, then scalar fm, then body; current_phase falls back to the
body 'Phase:' field (parseProsePhaseField lineage).
- Add regression tests against the real schema (nested progress YAML +
body Phase field) covering verify-pending + executing situations.
Verified against this repo: now classifies verify-pending (was
needs-first-phase). Coverage 93.25% lines / 86.99% branches.
* fix(workflow): tiered fallback when gsd-tools is broken (not just smart-entry)
Live test exposed a self-defeating fallback: when smart-entry --json
failed because gsd-tools itself was broken (missing
markdown-sectionizer.cjs), the workflow fell back to /gsd:progress —
which also depends on gsd-tools and would dead-end too.
Replace the single /gsd:progress fallback with a tiered recovery:
1. Probe gsd_run state-snapshot. If it ALSO errors, the whole tool
layer is down — read .planning/STATE.md directly with the Read tool
and synthesize a minimal situation + actions menu so /gsd:next stays
useful. Surface a rebuild hint.
2. Only if smart-entry alone is missing (older gsd-core), fall back to
/gsd:progress as before.
Matches the direct-read resilience the live agent already did by hand.
* docs: add gsd-next skill surface
* chore: trigger no-mistakes validation
* no-mistakes(review): Fix smart-entry phase ordering
* no-mistakes(review): Fix decimal smart-entry phase ordering
* no-mistakes(test): Fix smart-entry next test contracts
* no-mistakes(document): Docs synced for smart entry
* chore: add changeset fragment for #1798 (/gsd:next smart-entry workflow)
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* fix: shorten next.md description and update golden install parity fixtures
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* fix: update /gsd-next refs to /gsd:next in docs and add Smart Entry topic alias
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* chore: trigger no-mistakes validation
* fix: regenerate INVENTORY-MANIFEST.json for new /gsd-next files
Full CI caught that adding commands/gsd/next.md + gsd-core/workflows/smart-entry.md
left docs/INVENTORY-MANIFEST.json stale (not in the affected-test scope that
no-mistakes' test gate runs, so it surfaced in CI). Regenerated via
node scripts/gen-inventory-manifest.cjs --write; inventory-manifest-sync
test now passes.
* fix: add 'next' to core_loop cluster, update INVENTORY-MANIFEST, fix gates.md ref
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* fix: regenerate golden install parity fixtures for /gsd:next
Full CI (shard 3/3) caught that adding commands/gsd/next.md + the
smart-entry workflow/lib made the per-runtime golden install parity
fixtures stale across all 16 runtimes. Regenerated via
UPDATE_GOLDEN=1 node --test tests/golden-install-parity.test.cjs.
All 16 fixtures + inventory-manifest-sync now pass.
* Fix smart-entry verify-failed phase scoping and empty resolve shim step
Scope detectVerifyFailed to STATE.md's current phase so leftover higher
phase directories cannot force verify-failed routing. Move the gsd_run
shim resolver into the workflow resolve step so agents define gsd_run
before the detect step runs smart-entry.
* fix: recapture golden fixtures with updated gates.md hash (/gsd:next)
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* fix: recapture all 16 golden fixtures with updated smart-entry.md hash
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* chore: regenerate fixtures + inventory manifest after rebase onto next
Rebased onto next which adopted #1837 (package-version normalization to
<VERSION> in golden-install-parity hashes). Recaptured the golden fixture
that needed it (hermes), re-sorted INVENTORY-MANIFEST.json, and regenerated
the gsd-next / ns-workflow skill descriptions to match the command surface.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
* refactor(#1787): delegate /gsd:next in-project advancement to gated /gsd:progress --next
Reconciles the /gsd:next smart-entry front door with the existing
/gsd:progress --next engine (davesienkowski review on PR #1798). The
classifier previously recommended /gsd:execute-phase directly for the
`executing` situation, bypassing workflows/next.md Route 0
(resume-incomplete-phase invariant, #160) and Gates 1-3 — reproducing the
duplication that got the old flat /gsd-next removed (#3054), plus a
correctness hazard (executing the recorded current phase while an earlier
phase is silently incomplete).
Now planning/executing/verify-pending recommend `/gsd:progress --next`
(single gated engine); the specific command stays an explicit secondary.
Off-path states (no-project, paused, blocked, verify-failed,
idle-stranded, complete) keep direct recommendations — smart-entry's
distinct value over --next. Adds docs/adr/1787-gsd-next-smart-entry.md and
a regression test locking the delegation contract.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#1787): avoid literal /gsd-next token in ADR (bug-3054 guard)
The repo-invariants #3054 guard bans the removed /gsd-next slash form in
docs surfaces. Refer to the removed command as `gsd-next` (prose) — the
historical reference is unchanged, just the banned token is dropped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: gitignore compiled host-integration-sdk + handshake-serialized .cjs
Pre-existing gap from #1683: these two src/*.cts modules compile to
gsd-core/bin/lib/*.cjs but were omitted from the per-file ignore list, so
`npm run build`/`npm test` left them as untracked build artifacts (dirty
tree + accidental-commit footgun). Adds them alongside their siblings
(host-integration.cjs, mcp-server.cjs, …). Found while finishing #1798.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#1787): lock per-situation action invariants for all 11 situations + ADR typo
Adversarial-review follow-ups:
- Add a test asserting every situation's action set has exactly one
recommended action, 1-4 unique-id /gsd:* actions (previously the
one-recommended/1-4 invariant was only sampled for 6 of 11 situations).
- Fix ADR typo: /gsd-progress → /gsd:progress.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#1798): split oversized test chunks so a slow shard can't trip the per-chunk timeout
Root-cause of the intermittent `full test (windows-latest, 22, shard 1/3)`
failure. It was NOT a leaked handle (the runner's kill message guesses that,
but --test-force-exit already exits leaks cleanly). Diagnosis:
- Ran every shard-1/3 file WITHOUT --test-force-exit + a 45s kill-timer:
zero hangs, zero leaks — every file self-exits. So no leaked handle / hang.
- CI activity profile: output kept flowing (slowly) right up to the 600.0s
kill — a dead hang would go silent. => pure slowness.
- Per-file timing: install-minimal-hooks.test.cjs is a 4987-line / 250-case
consolidation file doing dozens of real installs — 41s even on a fast Mac
(much worse on the slow Windows I/O path), plus an install-heavy cluster.
Mechanism: MAX_FILES_PER_CHUNK=180 packed the whole ~171-file shard into ONE
`node --test` chunk, so the entire shard's wall-clock ran against a single
600s per-chunk backstop. On slow Windows runners that single chunk crossed
600s and was killed mid-run — an intermittent false-negative gate that also
hits `next` directly.
Fix: lower MAX_FILES_PER_CHUNK 180 -> 90 so each shard splits into ~2 chunks,
each with its own fresh 600s budget and a fresh node process (also relieves
per-process memory pressure). Verified locally: shard 1/3 now runs as
chunk 1/2 (90 files) + chunk 2/2 (81 files), 5323 tests, 0 fail. Also made the
timeout kill-message name slowness as a cause instead of asserting a leak, so
the next debugger isn't sent hunting a nonexistent handle leak.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
6d072435d0 |
test(#1975): consolidate 51 CLI + scripts-tooling regression tests into module suites
Fold 51 issue-named CLI black-box + scripts-tooling regression files into their canonical module suites (runtime-launcher-parity, worktree-safety, install-*, managed-hooks, read-guard, capability-registry, etc.), plus a NEW slash-command-namespace.test.cjs grouping the 4 slash/colon-namespace-leak invariant suites that had no canonical owner. Verbatim block-scoped describe wrappers; 427 subtests conserved 1:1. Host-env pre-check (per B2): no CLI-receiving host sets a redirecting GSD_WORKSTREAM/GSD_PROJECT value. One folded suite (bug-3668 runtime resolver) creates an extension-less PATH gsd-tools stub + bash -c; co-locating it with the host's chmodSync tripped local/no-unguarded-nonportable-exec, so it's now Windows-guarded (skip on win32) matching the host suite's own bash -c guard. Regenerates regression-name allowlist (222->182), ratchets file-count allowlist (graphify 7->6, docs entry removed), makes 26 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456; prunes stale ids). Repoints 13 tests/ references across CONTEXT.md, COMMANDS.md/FEATURES.md (EN + ja/ko/pt/zh) and ADR-0002. lint:ci green. Part of epic #1969. Closes #1975. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
697cbb1f05 |
test(#1977): consolidate 22 misc + repo-invariant regression tests
Final epic-#1969 batch. Fold 22 issue-named files: the 4 genuine repo-wide invariant scans (551-eslint-bin-lib-coverage, bug-3054 stale /gsd-next, bug-3810 no-gsd-sdk-runtime-refs, feat-3593 cli-negative-universal) into a NEW shared repo-invariants.test.cjs; the other 18 as singletons into their nearest module suite (model-resolver, codex-config, runtime-converters, security, state-transition, worktree-safety, roadmap-parser, etc.). Verbatim block-scoped describe wrappers; 334 subtests conserved 1:1. Host-env pre-check (B2+B6): the 6 CLI folds into GSD_TEST_MODE-setting hosts (model-resolver/ codex-config/runtime-converters) are benign — each origin independently sets GSD_TEST_MODE=1 itself (idempotent), unlike the B6 real-install case. Regenerates regression-name allowlist (222->213), ratchets file-count allowlist (state 17->16), makes 7 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456; prunes stale ids). Repoints 2 tests/ refs in docs/TESTING-SUITES.md. lint:ci green. Part of epic #1969. Closes #1977. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0cc7a1a426 |
test(#1974): consolidate 27 installer/hooks remainder tests into module suites
Fold 27 issue-named installer/hooks/statusline/migration/reapply regression files into their canonical module suites (installer-migrations, installer-migration-report, gsd-statusline, reapply-verify-hunks, install-*, gsd-check-update-worker-platform-gate, etc.). Verbatim block-scoped describe wrappers; 276 subtests conserved 1:1. No new files. The one subdir origin (tests/installer-migrations/001-legacy-orphan-files) moved up one level into installer-migrations.test.cjs; its single ../../ module require corrected to ../ so it resolves from tests/ root (verified). Host-env pre-check: no CLI-receiving host sets a redirecting GSD_WORKSTREAM/GSD_PROJECT value. Regenerates regression-name allowlist (222->205), ratchets file-count allowlist (verify 11->8, validate entry removed), makes 16 relocated allow-test-rule exemptions issue-ref- compliant (ADR-456; prunes stale ids). Repoints 15 tests/ references across state-md.md (EN + ja/ko/pt/zh). lint:ci green. Part of epic #1969. Closes #1974. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
85ed50cc4f |
test(#1972): consolidate 94 command/module regression tests into subject suites
Fold 94 issue-named command/module regression files into the canonical test file that owns each subject-under-test, across 52 existing suites (state, config, frontmatter, roadmap-parser, capability-registry, shell-command-projection-dispatch, plan-phase-drift-guard, health-validation, runtime-converters, commands, etc.). Verbatim block-scoped describe wrappers; 881 subtests conserved 1:1. No new test files. Host-env pre-check (per B2): the only GSD_WORKSTREAM/GSD_PROJECT-touching destinations (intel, planning-workspace) clear those vars hermetically, so folded CLI tests are safe. Regenerates regression-name allowlist (222->162), ratchets file-count allowlist across 8 buckets (validate entry removed after dropping <=2), makes 34 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456; prunes 34 stale ids). Repoints CONTEXT.md + ADR-0002/443/1235/3524 test-file references. lint:ci green. Part of epic #1969. Closes #1972. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
de3ba45d00 |
test(#1971): consolidate 48 gsd-tools CLI regression tests into subcommand suites
Fold 48 issue-named gsd-tools CLI regression files into the canonical test file that owns each subcommand subject (state, roadmap, phase, milestone, audit, config, router/dispatch, stats, verify, health, etc.), preserving every assertion and its origin issue number as provenance (block-scoped describe wrappers, 299 subtests conserved 1:1). No monolithic gsd-tools.test.cjs created — routes into 18 existing per-subject suites. Removes 48 tests/ files. Regenerates regression-name allowlist (271->231), ratchets the file-count allowlist across 6 buckets (audit/milestone/phase/roadmap/state/verify), and makes 10 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456; prunes 10 stale ids). Repoints one CONTEXT.md symptom ref and ADR-3524's parity-test ref. lint:ci green. Part of epic #1969. Closes #1971. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4f779eda43 |
test(#1970): consolidate 61 install-suite regression tests into function suites
Fold 61 issue-named install/codex/runtime regression files into the canonical test file that owns each subject-under-test, preserving every assertion and its origin issue number as provenance (block-scoped describe wrappers, zero assertion loss — 652 subtests conserved 1:1). Routes: - codex-config.test.cjs +19 (codex config/toml/hooks/adapter/skill surface) - install.test.cjs +18 (node-runner norm, manifest, arg parse, finishInstall) - install-runtime-artifacts +13 (per-runtime conversion + emission) - install-minimal-hooks +6 (hook-event dialects + guards) - path-replacement +2 (opencode absolute pathPrefix) - install-write-confinement +2 (pristine dir writes) - install-regressions +1 (user-artifact preservation) Removes 61 tests/ files → 61 fewer CI processes. Regenerates the regression-name allowlist (271→222), ratchets the file-count allowlist (config 10→9, install 12→9), and makes 13 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456; prunes 13 stale allowlist ids). Repoints ADR-0009's moved-test list. lint:ci green. Part of epic #1969. Closes #1970. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b51cbf96cf |
feat(#1943): extensionEvents vocabulary — separate from hookEvents (extension-system surface) (#1946)
* feat(#1943): extensionEvents vocabulary — separate from hookEvents (extension-system surface) * fix(#1943): re-export VALID_EXTENSION_EVENTS from gen-capability-registry (test import path) * fix(#1943): regenerate capability-registry.cjs + add changeset fragment * fix(#1943): import VALID_EXTENSION_EVENTS from validator, not gen-capability-registry (golden parity) |
||
|
|
69fef7c00e |
docs(#1683): Diátaxis host-integration docs + versioning policy + ADR-1239 Accepted — Slice 3 (#1940)
- how-to: author a host-plugin (external-author guide against the SDK surface) - tutorial: embed GSD in a new host (end-to-end programmatic-cli example) - reference: the Host-Integration Interface (axes, adapters, handshake, profiles) - explanation: interface versioning + deprecation policy (additive vs breaking, PROTOCOL_VERSION bumps, deprecation window) - ADR-1239 Status: Proposed → Accepted (Phases B–E shipped) |
||
|
|
1388e7a362 |
feat(#1683): published Host-Integration SDK surface + smoke test — Slice 2 (#1939)
The SDK entry (src/host-integration-sdk.cts) is the single PUBLIC surface a host-plugin author imports: the negotiated schema + classification, the five adapters (declarative/imperative/model/hook/state), and the serialized handshake. Frozen so the public shape cannot be mutated. Everything else in gsd-core stays internal. tests/sdk-smoke.test.cjs imports ONLY from the SDK entry and builds a third-party host-plugin end-to-end (compose adapters + handshake + classify) — proving an external author can wire a host without gsd-core internals (#1683 AC). ESLint-ignore + inventory manifest kept in sync for the new tsc-emitted module. |
||
|
|
47ecf997d6 |
feat(#1683): serialized capability-exchange handshake — Phase 6 Slice 1 (#1937)
* feat(#1683): serialized capability-exchange handshake — Phase 6 Slice 1 The out-of-process wire form of Phase 1's negotiateHostCapabilities: SDK hosts (pi, VS Code) that cannot share object refs exchange a JSON capability set over a wire boundary (MCP-style initialize). src/handshake-serialized.cts provides buildHandshakeRequest (host side) + handleHandshakeRequest (engine side, delegates to negotiateHostCapabilities), both JSON-round-trip-safe. CONSISTENCY is the contract: a serialized request yields the same NegotiationResult as the in-process call for the same axes (asserted in the test). Pure + additive; the companion MCP server or an SDK host binds it to a real transport. * fix(#1683): ignore tsc-emitted handshake-serialized.cjs (ADR-457) + refresh inventory manifest * fix(#1683): correct inventory manifest — drop junk smart-entry.cjs, add handshake-serialized.cjs The local gen-inventory-manifest scan captured a stale untracked smart-entry.cjs (not on next, no src/*.cts) and missed the freshly-built handshake-serialized.cjs. Aligned to the canonical clean-build report: + handshake-serialized.cjs, - smart-entry.cjs. * fix(#1683): type the JSON wire round-trips (no-unsafe-return) |
||
|
|
ff6b5cb024 |
feat(#1914): OpenCode native plugin integration (Option 1 file-copy) (#1923)
* feat(#1914): OpenCode native plugin integration (Option 1 file-copy) Ship a native OpenCode plugin (.opencode/plugins/gsd-core.js) plus the installer step that delivers it, so GSD's lifecycle hooks run on OpenCode. OpenCode declares hooksSurface:'none', so GSD's hook scripts already ship to <configDir>/hooks/ but nothing invokes them; the plugin bridges OpenCode's event bus onto those scripts as subprocesses (prompt/read/worktree/workflow guards, injection scanner, context monitor). Distribution is Option 1 (file copy) per the #1914 triage decision: no scripts.build rename, no prepare/prepack removal. package.json gains main + .opencode in files[] for discovery. Corrected against OpenCode's docs + loader source (not the reference branch): - Auto-discovery globs {plugin,plugins}/*.{ts,js} — .cjs is never matched, so the installed adapter must be .js (config dir carries {"type":"commonjs"}). - No opencode.json plugin-array patch — that array is npm-only; local files are auto-discovered. - REPO_ROOT is resolved by walking up to the dir holding hooks/ + gsd-core/, correct for package tree, global install, and local install. - Config-hook registration is gated (IS_PACKAGE_TREE) so it never double-registers commands/agents/skills already delivered by native copy. Also fixes an incidental .gitignore drift: 8 ADR-1239 .cts-generated .cjs artifacts were untracked-and-not-ignored (leak risk) — now ignored. Tests: tests/opencode-plugin-adapter.test.cjs (14, pure helpers + real subprocess bridge against stub hooks); golden-install-parity regenerated. Green on Mac + Linux (gsd-test): 0 failures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#1914): harden OpenCode plugin export shape + advisory accumulation (adversarial findings) Address Codex adversarial-review findings: - HIGH: export `{ id, server }` could trip OpenCode's loader (`for (entry of Object.values(mod)) getServerPlugin(entry)` throws on a non-extractable value). Make `id` NON-ENUMERABLE and assign module.exports from a variable (not a literal) so no string `id` is ever iterated — verified loader-safe under real import(pathToFileURL) (default + module.exports alias, both objects with .server; no bare id string). - MEDIUM: sequential advisory hooks clobbered output.metadata._gsdAdvisory; now accumulate into an array. - LOW: resolveRepoRoot fallback returned ".." while the comment said "../.." — aligned to "../.." (package-tree depth). Tests: added a faithful loader-loop emulation (raw CJS + ESM namespace views), advisory-accumulation, and a real bin/install.js copy→manifest→uninstall integration test. golden regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#1914): add changeset fragment for OpenCode plugin integration (PR #1923) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#1914): Windows — assert rewritten path with string include, not path-regex The Read content-rewrite test built a RegExp from `path.join(root,'gsd-core')`. On Windows the backslashes in the path are interpreted as regex escapes, so the assertion never matched and `test (windows-latest, 24)` failed — even though the adapter rewrote the path correctly. Replace the RegExp with a separator-agnostic `String.includes` check (the repo's no-path-literal-in-assert concern). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
92091d71f2 |
fix(#1871): wire phase archival end-to-end (phases archive cmd + default + atomic) (#1924)
Follow-up to #1919 (archive-then-remove core). Closes the remaining #1871 acceptance criteria so phase history is preserved across the full milestone lifecycle, not just at phases.clear: - #2 src/milestone.cts + src/phases-command-router.cts: extract shared archivePhaseDirectories() helper; add cmdPhasesArchive (the previously half-wired phases.archive alias now routes instead of erroring Unknown). - #4 gsd-tools.cjs + src/milestone.cts: milestone complete archives phase dirs by default (--no-archive-phases opts out; --archive-phases is now a harmless no-op). complete-milestone.md updated to drop the redundant manual Yes/Skip archive prompt. - #3 gsd-core/workflows/new-milestone.md: §6 stages the archive move + source removal (git add .planning/milestones/ .planning/phases/) in the same commit as the milestone start, so the archive lands atomically — no orphaned uncommitted deletions, no un-archived dirs inherited. - docs/CLI-TOOLS.md (+ ja/zh/ko/pt) + help/modes/full.md: flag accuracy. - tests: phases archive command (#2) + milestone complete default archive / --no-archive-phases opt-out (#4). Goldens + workflow size baseline refreshed. Closes #1871 |
||
|
|
3c13903dcd |
feat(#1866): agent-side self-load of configured agent_skills
Each of the 22 consumer agents now self-loads its configured agent_skills in its mandatory init step, so .planning/config.json agent_skills.<type> reaches the agent on every runtime — including Cursor and /gsd-autonomous, where Skill()-delegated workflow bash init did not reliably execute. - gsd-core/references/agent-skills-bootstrap.md: shared contract (query + Read + dedup guard that skips when <agent_skills> is already in the prompt, so Claude's orchestrator-side injection never doubles) - 22 agents/gsd-*.md: one self-load line naming the agent's own type - gsd-core/workflows/autonomous.md: note that delegated agents self-load - tests/agent-skills-bootstrap.test.cjs: regression + parity (CONSUMER_AGENTS bijection + fast-check property) — Generative-Fix-Divergence guard - docs: ADR-1866, CONFIGURATION dual-injection How It Works, INVENTORY row, Changed changeset Closes #1866 |
||
|
|
2391632973 |
feat(#1855): add Claude plugin marketplace manifest
Add .claude-plugin/marketplace.json so Claude-plugin-compatible runtimes
(ZCODE et al.) discover gsd-core from a custom marketplace source. The
canonical version lives at plugins[0].version and tracks package.json via
the release version-sync.
Refactor scripts/sync-manifest-versions.cjs so VERSIONED_MANIFESTS entries
are {path, versionKey} dot-path descriptors (default 'version'); register
marketplace.json with versionKey 'plugins.0.version'. getByPath/setByPath
reject __proto__/constructor/prototype (prototype-pollution guard).
plugin.json / gemini-extension.json behavior is unchanged.
- tests/issue-1855-marketplace-manifest.test.cjs: schema + version-sync guard
- tests/issue-844-manifest-version-sync.test.cjs: updated for descriptor shape
- VERSIONING.md + auto-backmerge VERSION_STAMP_MANIFESTS: include marketplace.json
- docs/how-to/install-on-your-runtime.md: marketplace discovery how-to
|
||
|
|
da37986cd0 |
fix(#1847): resolve standard tier to claude sonnet 5
Point the sonnet/standard tier at Claude Sonnet 5 (`claude-sonnet-5`,
GA 2026-06-30) across the Anthropic-backed runtimes and provider presets,
replacing the superseded `claude-sonnet-4-6`. Mirrors the change into the
CONFIGURATION.md and settings-advanced.md runtime-defaults tables (the
#3229 catalog↔docs parity gate) plus the pt-BR/zh-CN translations, and
updates the tests that pin the old ID. Regenerates the workflow size
baseline for the (smaller) settings-advanced.md.
Scope is Sonnet only — opus/haiku IDs are untouched. Prepared as a 1.6.1
hotfix off the v1.6.0 tag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit
|
||
|
|
93e5d2dd84 |
fix(#1525): skip deferred phases on autonomous reruns (#1846)
* fix(#1525): skip deferred phases on autonomous reruns * chore(#1525): add changeset fragment * chore(#1525): fix changeset body * test(#1525): refresh install parity fixtures * test(#1525): shrink autonomous workflow * test(#1525): refresh autonomous baselines * test(#1525): tolerate Windows temp cleanup flake |
||
|
|
8161fcc667 |
docs(#1609): design note — verifier reach = spec reach (#1715)
* docs(#1609): design note — verifier reach = spec reach Adds docs/design/verifier-reach.md (new docs/design/ dir): a design-rationale note recording the probe family's organizing principle — a goal-backward verifier only checks assertions that exist, so reliability comes from widening the spec (edge + prohibition probes), not sharpening the verifier. Expands the rationale already stated in ADR-857's verification-substrate section and the CONTEXT.md PROBE.principle predicate. Author's calibration numbers are hedged as internal research, not GSD claims. Closes #1609. * docs(#1609): split out plan→execute generalization to keep note within #1609 scope |
||
|
|
337eee59d5 |
docs(#1610): ADR for the workflow/agent size-budget ratchet (#1713)
* docs(#1610): ADR for the workflow/agent size-budget ratchet Gives the already-shipped size-governance decision (epic #1074; PRs #1089/#1096/#1097) its first ADR: per-file LF-normalized byte baseline (anti-creep across every workflow/agent .md) + loose tier hard caps (XL/LARGE/DEFAULT), measured in bytes not lines, with an explicit do-not-game-the-proxy clause (lazy extraction only). Distinct from the install-time skill-surface budget of ADR-0010/0011. Verified against tests/{workflow,agent}-size-budget.test.cjs + scripts/workflow-size.cjs: cap constants XL_CAP=98304/LARGE_CAP=61440/DEFAULT_CAP=40960/NEW_FILE_CAP=32768, and the largest XL orchestrator is plan-phase.md (~93,973B) ahead of execute-phase.md (~93,426B) — corrected from the draft. Closes #1610. * docs(#1610): de-rot tier-cap byte figures — cite baseline JSON not a drifting snapshot --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com> |
||
|
|
bad2c76c5e |
feat(#1826): cmdStateRebuild CLI + dry-run + verbose + integration tests + docs (#1830)
Phase 2 of approved feature #1817. Wires the pure `rebuildCore` transition (Phase 1, #1827) to the `gsd state rebuild` CLI subcommand per ADR-1817 §5 (heavy/manual counterpart to lightweight, auto-triggered `state sync`). Source changes: - src/state.cts: implement cmdStateRebuild. Locks via readModifyWriteStateMd (real path) or read-only (dry-run). Wires phaseInventoryProvider to a real .planning/phases/ disk scan (same canonical source buildStateFrontmatter uses). --dry-run emits a structured preview without writing. --verbose tees the audit-log entries to stderr (treated as data-only per ADR-1577). - src/state-command-router.cts: register cmdStateRebuild in the StateModule interface + add the rebuild handler with --dry-run and --verbose flag parsing. - src/command-aliases.cts: register the state.rebuild canonical + 'state rebuild' alias + mutation=true (so the manifest covers the new subcommand for SDK parity / dispatch hub tests). Tests (tests/state-rebuild-cli.test.cjs, 5 cases): - state rebuild with no flags reconciles drifted body + drops orphan table rows + appends audit log (end-to-end #1, #2, audit log). - state rebuild --dry-run computes the diff, writes nothing (criterion #5). - state rebuild --verbose tees the log; audit-log section still written. - Running rebuild twice on the just-rebuilt file is byte-identical (criterion #6 end-to-end). - Missing STATE.md produces a clean 'STATE.md not found' message, no stack trace (CONTRIBUTING QA matrix). Verified locally: - node --test tests/state-rebuild-cli.test.cjs → 5/5 pass - node --test tests/state-rebuild.test.cjs → 18/18 pass (Phase 1 regression) - node --test tests/state-transition.test.cjs → 85/85 pass (ADR-1769 regression) Docs (docs/COMMANDS.md): document `state rebuild [--dry-run] [--verbose]` with the canonical-command block format used by `state sync` / `state prune`. Changeset (.changeset/1817-state-rebuild.md): type=Added, user-facing description of the new subcommand (closes #1817 epic on merge). |
||
|
|
dfff25f1bd |
docs(#1817): add adr-1817 state.md rebuild derivability contract (#1828)
* chore(context): scrub nul byte from consent-store predicate
CONTEXT.md line 206 (Capability Consent Store predicate) contained a
literal NUL byte between ${realpath(projectRoot)} and <id> documenting
the disk-key join format. The byte was intentional but made the file
binary-detected, breaking grep/rg searches (hit while preparing ADR-1817).
Replace with the 4-char \x00 escape. Preserves the byte-level disk-key
documentation; surrounding prose 'prototype-pollution-safe NUL-joined
keys' carries the semantic context. file(1) now reports 'Unicode text'.
* docs(#1817): add adr-1817 state.md rebuild derivability contract
Phase 0 of approved feature #1817 (epic). Lands the design contract for
the new `rebuild` transition in the STATE.md Transition Module (ADR-1769):
- ADR-1817 (new): `rebuild` is the capstone 11th transition. Six design
decisions: (1) core substrate, non-toggleable, same tier as the other
10; (2) section taxonomy — re-derivable (`## Current Position` prose
from frontmatter, `## By-Phase Progress` table from disk) vs preserved
(`## Session`, `## Decisions`, unknown sections) vs de-duplicated
(`## Session Continuity Archive`); (3) orphaned data is logged + dropped
with a structured audit entry in `## Rebuild Log` (ADR-1411 provenance
principle); (4) idempotency is a hard guarantee — a no-mutation rebuild
appends no log entry; (5) non-overlapping scope with `sync` (3
frontmatter fields, auto-triggered); (6) orthogonal to
`auto_prune_state` (rebuild reconciles with current canonical sources,
prune removes by retention policy).
- CONTEXT.md (STATE.md Transition Module section): list `rebuild` as the
11th intent; add contract predicates mirroring the ADR.
- docs/adr/README.md: add ADR-1817 to the index (Accepted).
Targets the #1776/#1761/#1591 body-drift cluster that survived ADR-1769's
per-field transitions. Phased per ADR-1817: this PR (Phase 0) closes
#1817; Phase 1 (#1827) lands `rebuildCore` + intent dispatch + drift-class
unit tests; Phase 2 (#1826) lands `cmdStateRebuild` CLI + dry-run +
integration tests + docs + changeset.
* docs(#1817): reword state-doctor alternative to satisfy docs-parity lint
The docs-parity-live-registry test scans every docs/*.md (including
docs/adr/) for slash-command tokens and asserts each one resolves to a
live command in the registry. The rejected-alternative #4 in ADR-1817
mentioned a hypothetical `/gsd:state-doctor` workflow, which tripped
the lint (`unknown command token(s): [/gsd:state-doctor]`).
Reword to 'standalone state-doctor workflow' (no slash prefix). The
extractor is aggressive — backticks and space-preceding tokens are both
extracted per the test's own polarity-invariant cases — so the only
sound fix is to not form a slash token at all for hypothetical names.
Verified locally: `node --test tests/docs-parity-live-registry.test.cjs`
now passes 31/31 (was 30/1).
|
||
|
|
18995380ce |
feat(#1154): honest verifier — abstain (insufficient_spec) on non-inferable backstop truths (#1738)
* feat(verify-phase): honest verifier — abstain (insufficient_spec) on non-inferable backstop truths (#1154) Carry the edge-probe's existing `backstop` (non-inferable) tier through the plan-phase projection as a structured flat-scalar marker instead of a prose parenthetical, and make verify-phase abstain -> human_needed (never silent-pass) on a backstop truth it cannot confirm with explicit evidence. Truth-axis mirror of #644's prohibition judgment-tier (ADR-550 D4). Engine (deterministic, CI-tested per ADR-550 D5 — never the LLM verdict): - src/probe-core.cts: truthStatement/truthVerification normalizers, projectTruths (conservative serializer), dispositionForUnverifiableTruth (backstop+no-evidence -> unverified/flagged/insufficient_spec; backstop+evidence -> green; inferable -> green, the over-abstention guard). - src/roadmap.cts: coerceTruthToString now reads `statement` first so an object-form backstop truth is surfaced, not dropped (Hyrum backward-compat for truth-readers). Workflow/agent/docs: plan-phase emits the structured marker (flat scalar, ADR-550 #1278); verify-phase + gsd-verifier add the abstain arm; new references/honest-verifier.md; FEATURES/COMMANDS document insufficient_spec; ADR-550 amended (truth-axis D4 mirror). Decisions adopted (trek-e review): insufficient_spec feeds existing human_needed with a distinguishable reason (no new VERIFIER_STATUS); changeset Changed; round-trip parity test; abstain-on-unconfirmed-backstop regression test red-first. Implementation notes (deviations from the issue's proposed file list, verified live): - frontmatter.cts needs no change — its flat parser already round-trips object-form truths. - verify.cts needs no change — it grades artifacts/key_links structurally; truths are LLM-graded at the workflow layer, so consumption lives there + the deterministic helper. - No CJS<->SDK hand-sync — the SDK seam was retired (ADR-0174); src/*.cts is sole source. Regenerated artifacts: golden-install-parity fixtures, INVENTORY-MANIFEST, size baselines. * chore(#1154): add changeset (Changed) for honest verifier User-facing changelog fragment for #1738. Typed `Changed` (not `Added`) per trek-e review condition 3 — the verify behavior shifts for backstop-bearing specs (a confident silent `passed` becomes `human_needed`), which is user-visible even though the schema marker is additive. * docs(#1154): score-formula also excludes abstained insufficient_spec truths (review nit-1) trek-e review nit: the verify-phase score sentence said PRESENT_BEHAVIOR_UNVERIFIED truths were "the only ones excluded" from verified_truths. Post-#1154 an abstained `insufficient_spec` backstop truth is also excluded (it is not ✓ VERIFIED and routes to human_needed). Behavior was already correct; this tightens the wording. Regenerated golden-install-parity fixtures + workflow-size baseline for the touched verify-phase.md. (Nit-2 — a dedicated insufficient_spec_items frontmatter list — is intentionally not taken: the current design is ADR-550-D4-conformant, the abstain cause rides as a distinguishable report reason, and adding it would exceed the approved scope.) --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com> |
||
|
|
fd576528a7 |
fix(#1747): register four search-provider keys in the config schema (#1814)
* fix(#1747): register four search-provider keys in the config schema buildNewProjectConfig emits seven search-provider availability flags and research-provider.cts providerAvailability() consumes all seven, but only three were registered in VALID_CONFIG_KEYS (config-schema.manifest.json). config-loader.cts then printed an 'unknown config key(s)' warning for the four unregistered keys (tavily_search, ref_search, perplexity, jina) on every freshly generated .planning/config.json. Register the four missing keys in the schema manifest and document them alongside brave/exa/firecrawl in CONFIGURATION.md. Add a regression test plus a structural drift guard that requires every config-driven research-provider flag to be in VALID_CONFIG_KEYS, so a future provider addition cannot silently reintroduce the drift. * fix(#1747): move regression into owning test file + add changeset lint-regression-test-names rejects new bug-NNNN-*.test.cjs files; move the #1747 regression (four provider keys in VALID_CONFIG_KEYS + provider-flag drift guard) into tests/bug-2530-valid-config-keys.test.cjs, the canonical home for VALID_CONFIG_KEYS regressions, and delete the standalone file. Add the missing .changeset fragment — config-schema.manifest.json lives under gsd-core/ (user-facing), so changeset-lint requires a fragment. * test(#1747): regenerate golden-install-parity fixtures for schema change Adding four provider keys to config-schema.manifest.json shifts its shipped content hash (65dea848 -> 7d398e94); recapture all 16 runtime fixtures via UPDATE_GOLDEN=1. Each fixture changes exactly one line — the manifest hash. |
||
|
|
41193a44bd |
feat(#1681): ADR-1239 Phase C-2 — gsd-mcp-server bin entry + lifecycle test [slice 3b] (#1810)
* feat(#1681): ADR-1239 Phase C-2 — gsd-mcp-server bin entry + lifecycle test [slice 3b] Phase 4 slice 3b (closes #1681). The companion MCP server bin entry so any MCP-consuming host connects via 'npx gsd-mcp-server' (or its bin on PATH) and gets GSD command (point 1) + state IO (point 5) with no bespoke plugin. - gsd-core/bin/gsd-mcp-server.cjs: #!/usr/bin/env node shim requiring ./lib/mcp-server.cjs + runServer({stdin, stdout}); non-zero exit on fatal error (justified n/no-process-exit disable). Mirrors gsd-tools.cjs. - package.json: add 'gsd-mcp-server' bin entry. - tests/gsd-mcp-server-bin.test.cjs: 3 process-lifecycle tests — initialize + tools/list round-trip + clean exit, malformed-line -> parse error + server keeps running, empty stdin -> clean exit. Synchronous spawnSync (bounded; server exits on stdin EOF, no orphan). Phase 4 trust-gate (#1806) + loader wiring (#1808) + server module (#1809) + this bin/lifecycle slice = all of #1681's deliverables. Concrete host binding -> Phase 5 (#1682). npm-integrity + eslint + security + inventory all clean. * docs(#1681)+chore(changeset): how-to for the companion MCP server + Added fragment docs/how-to/connect-gsd-mcp-server.md — Diataxis how-to guide for connecting any MCP-capable host to gsd-mcp-server: goal-oriented flow (add config → restart → verify), real-world per-host conditionals, troubleshooting, and a trimmed reference table. Explanation/reference linked out (ADR-1239, capability-trust- model) per Diataxis boundary rules rather than mixed in. .changeset/humble-seals-rest.md — type: Added (first user-reachable surface of the epic: a new bin command). The how-to doc satisfies the docs-required gate. * fix(#1681): move gsd-mcp-server shim to top-level bin/ (out of the runtime-copied tree) The shim at gsd-core/bin/gsd-mcp-server.cjs was inside the tree the installer copies into every runtime config dir, so it leaked into all 16 runtimes and broke golden-install-parity. The MCP server is a PACKAGE bin the host spawns (npx gsd-mcp-server), not a per-runtime artifact — so it belongs at top-level bin/ alongside install.js (which is also never copied into a runtime config). - gsd-core/bin/gsd-mcp-server.cjs -> bin/gsd-mcp-server.js (require path now ../gsd-core/bin/lib/mcp-server.cjs). - package.json: bin entry -> bin/gsd-mcp-server.js. - tests/gsd-mcp-server-bin.test.cjs: SHIM path updated. - eslint.config.mjs: add bin/gsd-mcp-server.js to the bin/install.js block (drops the n/no-process-exit disable — the n plugin isn't loaded for that block, so the disable referenced an undefined rule). golden-install-parity 16/16 restored; lifecycle + unit tests green; eslint 0; lint:ci all ok. |
||
|
|
2b38356275 |
feat(#1681): ADR-1239 Phase C-2 — companion MCP server module (points 1 + 5) [slice 3a] (#1809)
* feat(#1681): ADR-1239 Phase C-2 — companion MCP server module (points 1 + 5) [slice 3a] Phase 4 slice 3a. A minimal, dependency-free stdio JSON-RPC 2.0 server exposing two of the six interface points so any MCP-consuming host (Claude/Codex/OpenCode/ VS Code/Gemini/Cursor/Cline/Hermes) can drive GSD with no bespoke plugin: - point 1 (command): tool gsd_invoke_command -> createHub/dispatch. - point 5 (state IO): tools gsd_read_state / gsd_write_state -> the Phase 3 stateIO seam (filesystem default). - src/mcp-server.cts: handleMessage(request, ctx) pure JSON-RPC handler (initialize / tools/list / tools/call) + runServer({input, output}) thin line-delimited-JSON loop over injectable streams. 3 tools wired to the existing engine surfaces. NO new dependency (hand-rolled JSON-RPC; the repo ships only claude-agent-sdk + ws — an MCP SDK is a separate packaging call). - tests/gsd-mcp-server.test.cjs: 9 tests (initialize, tools/list, state read/write round-trip, command dispatch, unknown tool / missing name / unknown method / notification / parse error, injectable-stream round-trip). Bin entry / packaging / manifest-version-sync / process-lifecycle docs -> slice 3b. This slice ships the importable, tested server surface a host (or the bin shim) drives. Proactive CI gates: ADR-457 ignores + INVENTORY-MANIFEST + injection-scan audit. All clean locally (9 tests + security 15/15 + inventory + eslint 0 problems). * chore(changeset): add Changed fragment for companion MCP server module (#1681) |
||
|
|
21b81ea068 |
feat(#1681): ADR-1239 Phase C-2 — external-descriptor trust gate (configHome confinement) [slice 1] (#1806)
* feat(#1681): ADR-1239 Phase C-2 — external-descriptor trust gate (configHome confinement) [slice 1] Phase 4 slice 1. Load-time, fail-closed configHome write-confinement for installed third-party host-plugin descriptors — defense-in-depth on top of the existing opt-in/schema/consent/first-party-wins loader gates + Phase 2's install-time assertDestWithinConfigHome (#1679 AC3). - src/external-descriptor-trust.cts: isPathConfined(target, root) pure cross-platform containment primitive + assertDescriptorConfined(descriptor, configHome) — walks runtime.artifactLayout global/local destSubpaths, throws fail-closed (naming descriptor + path) on the first escape. Rejects ../escape + absolute-outside-root. Missing layout / invalid entries skipped. - tests/external-descriptor-confinement.test.cjs: 7 tests (containment primitive, benign passes, global/local/absolute escapes rejected, missing layout, invalid entries). Load-time twin of Phase 2's install-time gate — rejects malformed/escaping descriptors BEFORE consent even matters. NOT wired into loadRegistry yet (slice 2, 4 callers, medium blast radius); this ships the reusable gate + tests. Not the ADR-1577 prompt-injection breaker (separate concern, shared word trust). Proactive CI gates: ADR-457 ignores + INVENTORY-MANIFEST + injection-scan audit. All clean locally (7 tests + security + inventory + eslint 0 problems). * chore(changeset): add Changed fragment for external-descriptor trust gate (#1681) |
||
|
|
abd21b2968 |
feat(#1680): ADR-1239 Phase C-1 — hook-bus + stateIO seams [AC4] (#1805)
* feat(#1680): ADR-1239 Phase C-1 — hook-bus + stateIO seams [AC4] Phase 3 slice 4 (AC4, final #1680 slice). The last two adapter seams behind the negotiated hookBus/stateIO axes: - src/hook-bus.cts: createHookBus({bus}, {hostEmit?}) -> host/engine/none. engine = in-process pub/sub (handler errors isolated); host = host-owned, fail-closed emit until a host emitter is bound; none = silent no-op (degrade to rule-text). PORTABLE_EVENT_FLOOR = SessionStart/PreToolUse/PostToolUse/ Stop/SessionEnd (the claude dialect all hook hosts share). - src/state-io.cts: createStateIO({io}, {backend?}) -> filesystem (today's behavior — straight fs) / sandboxed-storage / session-log-append (fail-closed seams until a host backend is bound). Proactive CI gates: ADR-457 ignores + INVENTORY-MANIFEST entries for both new .cjs; injection-scan 'act as' substring audit; unused-import check. All clean locally (11 tests + security 15/15 + inventory + eslint 0 problems). Phase 3 (#1680) seam layer now complete. Concrete host binding -> Phase 5 (#1682, D15/D18). * chore(changeset): add Changed fragment for hook-bus + stateIO seams (#1680) |
||
|
|
b152f7e64c |
feat(#1680): ADR-1239 Phase C-1 — model adapter seam (passive + active) [AC3] (#1804)
* feat(#1680): ADR-1239 Phase C-1 — model adapter seam (passive + active) [AC3] Phase 3 slice 3 (AC3). Two model-layer adapters selected by the negotiated modelMode axis (host-integration.cts): - passive: formalizes today's tier routing from src/model-resolver.cts — resolveModel delegates straight to resolveModelForTier (byte-for-behavior). This is the CLI runtimes (claude/gemini/codex/opencode/cursor/...): GSD injects prompts / a per-agent model field. - active: a host-supplied sendRequest seam (VS Code vscode.lm / pi providers) — GSD calls the model through the host. Ships as a fail-closed seam (throws until a provider is bound); Phase 5 wires a concrete provider. createModelAdapter({modelMode}, {sendRequest?}) — factory gating throws on invalid mode. Proactive CI gates applied: ADR-457 eslint ignores entry + INVENTORY-MANIFEST cli_modules entry + comment-wording audited for the injection-scan substring trap. * chore(changeset): add Changed fragment for model adapter seam (#1680) |
||
|
|
da368311ea |
feat(#1680): ADR-1239 Phase C-1 — imperative embedding adapter (composes loadRegistry) [AC2] (#1803)
* feat(#1680): ADR-1239 Phase C-1 — imperative embedding adapter (composes loadRegistry) [AC2] Phase 3 slice 2 (AC2). The engine-as-library path: createImperativeAdapter composes loadRegistry({includeInstalled:true}) — first-party-wins + consent + fail-closed gates, identical trust semantics to the CLI — and binds the engine surface behind the SAME HostIntegrationInterface the declarative adapter (AC1) satisfies, plus a registry accessor for the composed capability set. - src/adapter-imperative.cts: createImperativeAdapter({runtime}, {loadOptions}) → ImperativeAdapter (kind:'imperative' + .registry + install/uninstall delegating to install-engine). Thin: delegates the loop, does not reimplement. - tests/adapter-imperative.test.cjs: kind (16 runtimes), registry composition (loadRegistry called with includeInstalled:true), loadOptions pass-through, install/uninstall delegation, fail-closed construction. - eslint.config.mjs + docs/INVENTORY-MANIFEST.json: ADR-457 ignores entry + cli_modules entry for the new emitted .cjs (the two drift gates that bit AC1, applied proactively here). Concrete host binding (OpenCode/VS Code/pi) deferred to Phase 5 (#1682). * test: remove dead readStateMd helper from bug-1760 test readStateMd was defined but never called (writeStateMd is the only state-md helper this test uses). Clears the lone no-unused-vars warning so the repo lints fully clean (0 problems). No behavior change — test still passes 2/2. * chore(changeset): add Changed fragment for imperative embedding adapter (#1680) * fix(adapter-imperative): reword comment to avoid injection-scan substring match The prompt-injection scan regex 'act\s+as\s+(?:a|an|the)' was matching the 'act as the' substring inside 'contract as the declarative adapter' (contrACT AS THE). Reword 'contract as' -> 'shape as' — no 'act' substring, identical meaning. Clears the 'lib source files are clean' + 'codebase prompt injection scan' security-gate failures. |
||
|
|
c642ed0ec5 |
feat(#1680): ADR-1239 Phase C-1 — declarative embedding adapter + minimal HostIntegrationInterface [AC1] (#1802)
* feat(#1680): ADR-1239 Phase C-1 — declarative embedding adapter + minimal HostIntegrationInterface [AC1] Phase 3 slice 1 (AC1). Names + bounds today's projection path behind the common HostIntegrationInterface that both declarative + imperative adapters will satisfy. - src/embedding-adapter.cts: minimal HostIntegrationInterface (kind + runtime + install/uninstall) + ADAPTER_KINDS. The full 6-point binding surface (command/dispatch/model/hooks/state/artifact) is DEFERRED until the imperative adapter (AC2) fixes the shape — ADR-1239 lists the wire-shape as an open question; freezing it now risks rework across Phases 3-6. - src/adapter-declarative.cts: createDeclarativeAdapter({runtime}) factory. Delegates in-process to install-engine installRuntimeArtifacts / uninstallRuntimeArtifacts (the SAME engine functions bin/install.js uses), so output is byte-identical to today's install (gated by golden-install-parity). Module-ref call style = monkeypatch-friendly for tests. Lossy by design: projects files, does not drive the loop (that's the imperative adapter, AC2). - tests/adapter-declarative-equivalence.test.cjs: kind classification (all 16 runtimes), install/uninstall delegation with exact args (the byte-identity link), fail-closed construction (missing/invalid runtime throws). Purely additive — no install.js/install-engine changes. Unblocks AC2 (imperative adapter) + AC3/AC4 (model/hook/state seams) as follow-up slices. * chore(changeset): add Changed fragment for declarative embedding adapter (#1680) * fix(lint): ignore tsc-emitted embedding-adapter/adapter-declarative .cjs (ADR-457) The new src/embedding-adapter.cts + src/adapter-declarative.cts modules' emitted gsd-core/bin/lib/*.cjs artifacts must join the ADR-457 ignores list (lint the src/*.cts source, not the emitted .cjs). Without this, the .cjs is linted under js.recommended where @typescript-eslint/no-require-imports is undefined, so the verbatim-copied eslint-disable directive surfaces as 'Definition for rule not found' — failing the lint-tests CI job. Also restores the clean line-level disable in adapter-declarative.cts (valid in the .cts source context where the rule IS defined). * fix(docs): add adapter-declarative + embedding-adapter to INVENTORY-MANIFEST cli_modules The new ADR-1239 Phase C-1 modules' built .cjs artifacts must be registered in docs/INVENTORY-MANIFEST.json's cli_modules array or the 'docs/INVENTORY-MANIFEST.json matches the filesystem' drift test fails on CI. Mirrors the existing sorted entries. |
||
|
|
21f0b4316f |
refactor(#1796): ADR-1769 Path A — finish STATE.md preservation consolidation (#1799)
Extract readModifyWriteStateMd's post-sync preservation block into a pure,
field-classification-table-driven applyStatePreservation in the STATE.md
Transition Module. progress / status / stopped_at now join current_phase_name
as table-governed (getFieldClassification), so a preservation-policy change is
a one-row table edit instead of a per-call-site patch.
This realizes the consolidation ADR-1769 / CONTEXT.md already claimed shipped
('Absorbs readModifyWriteStateMd post-sync preservation block') and routes the
#1264 preservation policy through the single field-classification table — the
bug class is now structurally guarded by the table, not just the call-site
shouldResync flag.
Behavior is byte-identical to the pre-amendment inline block (Hyrum-safe — the
15 readModifyWriteStateMd callers' observable preservation is unchanged):
- state/frontmatter/transition + bug regression suite: 847 pass
- phase/milestone/verify (other RMW consumers): 582 pass
- codex (gpt-5.5/high) adversarial review: CLEAN (58,564-case equiv sweep)
ADR-1769 amendment appended documenting #1796.
Closes #1796
|
||
|
|
79c69d443b |
refactor(#1793): ADR-1769 Phase 7 — sync, prune, update migrations (#1760, #1761) (#1794)
Migrate cmdStateSync, cmdStatePrune, cmdStateUpdate (state.cts) onto the STATE.md Transition Module substrate and close the maintenance bug pair #1760/#1761 (ADR-1769, epic #1769). Completes the substrate: all 10 lifecycle/maintenance transitions now route through transitionCore. - Add {kind: 'sync'|'prune'|'update'} to StateTransitionIntent with syncCore, pruneCore, updateCore in src/state-transition.cts. - updateCore: body-only single-field update (strip/reassemble), mirroring the pre-migration cmdStateUpdate contract. - pruneCore: pure content→content section pruning (Decisions / Recently Completed / resolved Blockers / Performance Metrics rows at or below cutoff), byte-identical tokenizeHeadings splicing. Adapter owns currentPhase, dry-run, and STATE-ARCHIVE.md. - syncCore: body writes (Total Plans in Phase, Progress bar, Last Activity) given disk-derived numbers. Adapter owns the disk scan + roadmap scope. - #1760: cmdStatePrune now derives currentPhase from 'Current Phase' OR 'Phase' (the canonical template emits 'Phase: X of Y'), so prune engages on template-conformant STATE.md instead of bailing 'Only 0 phases'. - #1761: cmdStateSync skips the Progress write (percent=null) when a milestone version is set in frontmatter but the ROADMAP has no versioned heading for it (milestone cannot be bounded). Projects without a milestone version are unaffected. Leaves progress untouched rather than silently writing fallback- derived wrong values. - Regression: bug-1760 (prune engages on template field) + bug-1761 (sync leaves progress untouched when unbounded). ADR-1769 marked Accepted. All 82 transition + 515 regression tests pass. Closes #1793 |
||
|
|
064f63b299 |
refactor(#1791): ADR-1769 Phase 6 — patch migration + curated current_phase_name preserve (#1695) (#1792)
Migrate cmdStatePatch (state.cts) onto the STATE.md Transition Module substrate and close the curated-field clobber bug class #1743/#1695 (ADR-1769, epic #1769). - Add {kind: 'patch'} to StateTransitionIntent, with patchCore in src/state-transition.cts. Applies each caller-supplied {field:value} pair via stateReplaceField over the full content (body + frontmatter), tracking updated vs. failed. data.updated/data.failed mirror the CLI output shape. - Collapse cmdStatePatch to a transitionCore dispatch. Field-name validation (security) and the resync-progress decision stay in the adapter. - #1695/#1743 fix: extend the #1230 delta heuristic in readModifyWriteStateMd to the curated current_phase_name, table-driven via getFieldClassification('current_phase_name').preservation === 'preserve-always'. When a write does NOT change the body Phase: source line, the curated frontmatter value wins over syncStateFrontmatter's body re-derivation (which harvests a wrong parenthetical aside — #1695). begin/planned/complete-phase rewrite their body Phase line, so the delta does not fire for them and current_phase_name still advances. - Regression: bug-1695-state-patch-clobbers-phase-name.test.cjs — unrelated patch preserves curated current_phase_name; patching the Phase source advances. All 70 transition + 493 regression tests pass (state/phase/milestone + #905/#397/#3242 lineages). Closes #1791 |
||
|
|
3ceb83329d |
refactor(#1789): ADR-1769 Phase 5 — milestoneComplete migration (#1790)
Migrate the STATE.md write path inside cmdMilestoneComplete (milestone.cts) onto the STATE.md Transition Module substrate (ADR-1769, epic #1769). - Add {kind: 'milestoneComplete'} to StateTransitionIntent, with milestoneCompleteCore in src/state-transition.cts (consulting the field-classification table). Owns the closure write: Status ('<version> milestone complete'), Last Activity, Last Activity Description, a Current Position reset to 'Awaiting next milestone', and an Operator Next Steps reset pointing at the next-milestone command. - Collapse the inline STATE.md transform in cmdMilestoneComplete to a transitionCore dispatch. The adapter retains writeStateMd (lock + steady-state syncStateFrontmatter post-sync) and resolves the runtime-specific next-milestone slash command, injecting it via intent.nextMilestoneCommand. - The two section resets carry their pre-seam allow-adhoc-markdown waivers (regex semantics pinned by existing tests; pending collectSection #1372). - realClock.today() is byte-identical to milestone.cts's local today (both new Date().toISOString().split('T')[0]). - Characterization tests pin field updates, both section resets (replace + insert paths), and frontmatter #1255 parity. All 66 transition + milestone + state tests pass. Closes #1789 |
||
|
|
91704c9fcf |
refactor(#1786): ADR-1769 Phase 4 — plannedPhase + milestoneSwitch migration (#1788)
Migrate cmdStatePlannedPhase and cmdStateMilestoneSwitch (state.cts) onto the STATE.md Transition Module substrate (ADR-1769, epic #1769). - Add {kind: 'plannedPhase'} and {kind: 'milestoneSwitch'} to StateTransitionIntent, with plannedPhaseCore and milestoneSwitchCore in src/state-transition.cts (consulting the field-classification table). - plannedPhaseCore owns the template-aware Status/Last Activity updates, Total Plans in Phase, Last Activity Description, and the Current Position section (via the inlined mutateCurrentPositionForAdvance twin). The adapter keeps the resync:false readModifyWriteStateMd wrapper (#500 RC1). - milestoneSwitchCore owns the new-milestone reset: rebuilt frontmatter (milestone/name/status='planning'/zeroed progress) + Current Position body reset. The adapter keeps acquireStateLock + platformWriteSync (NOT readModifyWriteStateMd — milestoneSwitch rebuilds frontmatter directly). - Collapse both callbacks to transitionCore dispatches. The now-dead updateCurrentPositionFields helper and KNOWN_STATUS_PATTERNS import are removed (their behavior lives in the transition module's mutateCurrentPositionForAdvance). - Characterization tests pin the template-aware preserve-authored invariant, Total Plans, Last Activity narrative, Current Position update, and the full milestone reset (frontmatter + position body + gsd_state_version preserve + Accumulated Context preserve). All 58 transition + 177 state + phase + bug-2630/bug-905 regression tests pass. Closes #1786 |
||
|
|
6f80524be1 |
refactor(#1784): ADR-1769 Phase 3 — completePhase migration onto Transition Module (#1785)
Migrate the STATE.md write path inside cmdPhaseComplete (phase.cts) onto the STATE.md Transition Module substrate (ADR-1769, epic #1769). - Add {kind: 'completePhase'} to StateTransitionIntent + completePhaseCore in src/state-transition.cts. Pure body field mutations (Current Phase shape/name, Status, Current Plan, Last Activity + Description, Completed/Total Phases + Progress percent), consulting the field-classification table for touched keys. Roadmap progress injected via a new optional deps.roadmapProvider. - Collapse the ~90-line inline STATE.md transform in cmdPhaseComplete to a transitionCore dispatch. The adapter retains updatePerformanceMetricsSection (section table) + syncStateFrontmatter (disk-scan post-sync) and the multi-file atomic transaction (STATE is committed with ROADMAP/REQUIREMENTS, so readModifyWriteStateMd is not used here). - deriveProgressFromRoadmap/clampPercent/stateReplaceFieldWithFallback move from the phase.cts call site into the pure core (no circular dep: phase-lifecycle and state-document don't import state.cts). - Characterization tests in tests/state-transition.test.cjs pin the field updates, roadmap progress derivation, #1255 frontmatter parity, and the 'Phase:' fallback. All 44 transition + 193 phase tests pass. No user-visible behavior change. cmdPhaseComplete CLI output and 'phase complete' behavior unchanged. Closes #1784 |
||
|
|
744bb7aaee |
refactor(#1771): ADR-1769 Phase 1 — STATE.md Transition Module substrate + beginPhase (#1775)
* refactor(#1771): ADR-1769 Phase 1 — STATE.md Transition Module substrate + beginPhase Lands the Phase 1 substrate per ADR-1769: - src/state-transition.cts (new Module): - Field-classification table (FieldClass enum + FIELD_CLASSIFICATION rows) - STATE_MD_SECTIONS constants block - Pure transitionCore(content, intent, deps) dispatch - beginPhase intent implementation (first-time + #3127 resume paths) - src/state.cts:cmdStateBeginPhase — collapses ~190 lines to a thin dispatch onto transitionCore via readModifyWriteStateMd. The lock, no-op write guard, and #1230 post-sync delta heuristic stay in the RMW seam; the body-mutation policy moves to transitionCore. - tests/state-transition.test.cjs (24 tests): - Substrate invariants (table enum, section constants) - Characterization: 6 first-time body field updates - Characterization: 5 #3127 idempotency-guard resume behaviors - Characterization: 3 Current Position section mutations - Characterization: Current focus body text line (#1104) - Property (RULESET.TESTS.property-based-testing): beginPhase status propagation + FIELD_CLASSIFICATION own-property contract - Resume Current Position mutation (preserves Plan/Phase/Status) No external behavior change. Full state.test.cjs regression (177 tests) plus bug-3127/#3242/#905/#948 pass. Property tests surfaced two pre-existing quirks (state-document.cjs greedy \s* on whitespace-only field values; Object.prototype method leakage on FIELD_CLASSIFICATION lookups for strings like 'toString') — documented in test comments; fix-out-of-scope for Phase 1. Closes #1771 * refactor(#1771): ADR-1769 Phase 1 codex review corrections Addresses 3 blocking findings from codex gpt-5.5/high review: 1. FIELD_CLASSIFICATION shape (state-transition.cts): - Was flat FieldClass enum (collapsed source + preservation) - Now two-column {source, preservation} rows per ADR-1769 §4 - Added missing fields verified via Memtrace against buildStateFrontmatter (state.cts:1633-1653): gsd_state_version, last_updated, last_activity_desc, progress.{total_phases, completed_phases, total_plans, completed_plans, percent} - Field 2-7 preservation dispatch can now consult the table 2. Prototype-pollution hardening (state-transition.cts): - Table is now Object.freeze(Object.assign(Object.create(null), {...})) - getFieldClassification() helper uses Object.hasOwn; returns null for inherited prototype methods (toString/valueOf/__proto__) - Old code: FIELD_CLASSIFICATION['toString'] returned the function 3. STATE_MD_SECTIONS aligned to canonical template: - Verified against gsd-core/templates/state.md via Memtrace - Was: 8 entries including non-template sections (## Session, ## Decisions, ## Operator Next Steps, ## Session Log, ## Roadmap Evolution) - Now: 6 canonical top-level sections (## Project Reference, ## Current Position, ## Performance Metrics, ## Accumulated Context, ## Deferred Items, ## Session Continuity) Also: beginPhase now consults getFieldClassification() per touched field (codex finding: 'table not consulted by transitionCore'). Unknown fields raise immediately — adding a field without a table row is caught at runtime. Repo-hygiene catches from gsd-test (not node --test, which missed these): - gsd-core/bin/lib/state-transition.cjs added to eslint.config.mjs ignore list (ADR-457 tsc-generated) - docs/INVENTORY-MANIFEST.json regenerated via node scripts/gen-inventory-manifest.cjs --write Property test for Object.prototype leakage tightened to verify getFieldClassification() returns null for toString/valueOf/__proto__. Ref #1771 * fix(#1771): cast Object.create(null) to satisfy @typescript-eslint/no-unsafe-assignment ESLint CI failed on src/state-transition.cts:72:14 — Object.create(null) returns `any`, which leaked through Object.assign to the typed `FIELD_CLASSIFICATION` declaration. Adding an explicit cast to `Record<string, FieldClassification>` eliminates the unsafe-assignment while preserving the null-prototype protection codex review recommended. gsd-test: 21888/21888 PASS. * fix(#1771): add 'see #1771' to allow-test-rule exemption per ADR-456 CI lint-allow-test-rule-refs failed: 'New allow-test-rule exemption without an issue ref — add `see #NNN` per ADR-456'. Updated comment on tests/state-transition.test.cjs to reference the Phase 1 issue. * fix(#1771): remove unnecessary allow-test-rule exemption The exemption was added speculatively. The test file does not use readFileSync + .includes()/.match()/.startsWith() on source content — it calls transitionCore() with string literals and verifies results via stateExtractField() and array .includes() on the updated[] array. No exemption needed. |
||
|
|
4732c704f7 |
docs(#1769): ADR-1769 STATE.md Transition Module — Phase 0 (#1770)
Introduce ADR-1769 establishing the STATE.md Transition Module design: intent-based transitions over scattered RMW callbacks. Seven design decisions resolved via /improve-codebase-architecture + /grilling + /domain-modeling: 1. Module shape: Transition Module owns the full transaction 2. Method set: 10 transitions (lifecycle + maintenance + milestoneComplete) 3. I/O shape: pure core (content, intent, deps) -> newContent 4. Policy model: field-classification table 5. External writers: 2 migrate (milestone, phase), 1 stays (verify) 6. Core scope: writes only; body sections as constants 7. Migration: substrate first, transition-by-transition Updates CONTEXT.md with the new Module entry; updates docs/adr/README.md index. No behavior change. Implementation lands in Phases 1-7 per the ADR's migration sequence. Closes #1769 Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
4ced0a64cc |
feat(#1561): assumption-delta advisory checkpoint (#1767)
* feat(#1561): assumption-delta advisory checkpoint * chore(#1561): backfill changeset PR number (#1767) --------- Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
b0d5ca3379 |
feat(#1517): support custom reviewer instances for /gsd:review (#1766)
* feat(#1517): support custom reviewer instances for /gsd:review Add a bounded review.reviewer_instances config surface so one model-capable adapter (e.g. opencode) can run as several independent reviewer identities in a single /gsd:review pass. Instances participate only via review.default_reviewers, expand before built-in slugs, are available iff their cli is detected, and a non-matching entry is a hard error (typo must be loud). >=2 same-cli instances emit a shared-adapter caveat in REVIEWS.md. Default path with no instances is byte-for-byte unchanged. Single-source instance->cli resolution lives in resolveReviewerSelection / normalizeReviewerInstances (parity-locked in tests/review-reviewer-instances.test.cjs). cli validated against KNOWN_REVIEWER_SLUGS only (never arbitrary shell); model/agent opaque, never shell-interpolated. Closes #1517 * chore(#1517): backfill changeset pr:1766 --------- Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
a3d3c2a445 |
refactor(#1756): derive getDirName from a documented runtime.localConfigDir descriptor axis (#1757)
ADR-1239 Phase B (parent #1679). getDirName was a hand-maintained 15-branch if-chain mapping each runtime to its local content-rewrite dot-dir. Relocate those values into a documented runtime.localConfigDir descriptor field; derive getDirName from registry.runtimes[id].runtime.localConfigDir (fallback .claude). - 16 capability.json gain runtime.localConfigDir (byte-identical values) - capability-validator.cjs requires it (non-empty dot-dir); registry regenerated - docs/reference/capability-manifest.md documents the field + the three divergent values (copilot=.github, antigravity=.agents, kimi=.kimi-code) - drift-guard test: golden value map + key-set equality both ways Byte-identical install output for all 16 runtimes (golden-parity harness #1730). Closes #1756 Co-authored-by: review-bot <review-bot@gsd> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
e075a41c86 |
feat(#1754): CLI version-skew detection — warn when a global install shadows project-local GSD (#1755)
* feat(#1754): CLI version-skew detection — warn when a global install shadows project-local GSD Addresses #1754 (approved-enhancement). Detects when the running gsd-tools.cjs is outside the project root while a project-local install exists — the shadowing scenario from #1748 where a stale global canary CLI (retired @gsd-build/sdk) silently overrides project-local GSD. Implementation (Node CLI entry-point, not shell snippet — avoids bloating 93 workflow files past their size caps): - src/cli-skew-check.cts: pure function checkCliSkew({resolvedPath, projectRoot, projectLocalExists}) → string|null. Compares paths via path.relative; returns a warning when the resolved CLI is outside the project root AND a project-local install exists. Includes @gsd-build/sdk removal hint when the path matches. No I/O (pure), no gsd-sdk literal (avoids bug-2801 lint). - gsd-core/bin/gsd-tools.cjs: wired at startup via the existing findProjectRoot resolver. Non-blocking (try/catch; advisory stderr warning, never gates). - eslint.config.mjs: registers the new ADR-457 generated artifact in the ignores. - tests: 6-case suite (skew/no-skew/legacy/normalization); all green. - Golden fixtures regenerated (UPDATE_GOLDEN=1) for the new compiled artifact. - docs/how-to/update-gsd.md: Diátaxis reference note for the skew warning. Full suite: 3354 pass, 0 regressions (1 pre-existing local AGENTS.md failure). lint:ci green. Closes #1754 * chore(#1754): backfill changeset pr placeholder * chore(#1754): regenerate INVENTORY-MANIFEST for the new cli-skew-check source module --------- Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
ae4c198a13 |
docs(#956): MemPalace proposal — decision/phase ownership + corrected Phase-6 framing (#1753)
* docs(#956): address adr-phase-coverage findings on the MemPalace capability proposal Resolves the findings from the adr-phase-coverage audit (matrix + interpreted gaps posted on #956) by embedding durable decision→phase ownership and the cross-doc gating item into the proposal, so no deliverable sits ownerless between phases: - §15.1 Decision → Phase ownership: every §10 decision + user-facing capability is the explicit responsibility of one phase. Cross-cutting policies get a primary owner (D6 onError:skip → P1 manifest-encoded; D3 transport → P2 MCP-primary rendering, P5 CLI-fallback headless). - §15.2 Dependencies & gating items: Phase 6 is GATED on ADR-857's Migrate phase (workflows calling loop render-hooks) — recorded as a traced gating item, not prose. UX-auto + UX-curator have no other wiring surface; if ADR-857's Migrate is descoped, Phase 6 is formally blocked, not silently dropped. De-risk: Phases 1–5 ship the full manual-invocation value ahead. - Phase 3 gate: explicitly verifies the inherited UX-enable surface (gsd capability enable mempalace + config-set), not just the internal state resolver. - Phase 6 gate: names the user-observable automatic surface (a /gsd-execute-phase run auto-produces MEMORY-RECALL.md at plan:pre, curator spawns at ship:post) instead of the wiring mechanism. - §17 open questions: each is traced to the phase whose acceptance must resolve it (wing-identity→P0, replace-migration→P4, curator-tier→P2, headless-MCP→P5, phase-6-dep→§15.2 gate, diary-namespacing→P6). Docs-only (proposal refinement); no runtime change. Closes #956 * docs(#956): correct the Phase-6 framing — ADR-857 is released, auto-fire is wired and verified Retracts the 'Phase 6 GATED on ADR-857 Migrate' framing introduced in the prior commit. ADR-857 (capability system + loop render-hooks + workflow call sites) is released; the host-loop workflows call loop render-hooks at each canonical point, so mempalace auto-fires when mempalace.enabled. Verified end-to-end: 'gsd-tools loop render-hooks plan:pre --raw' with mempalace.enabled:true returns the mempalace-recall step (capId: mempalace, produces MEMORY-RECALL.md). - Phase 6 row: 'Loop wiring (shipped via ADR-857)' with the verified gate. - §15.1 Phase 6 row: wired via shipped ADR-857 infra (no gate). - §15.2: rewritten from 'Dependencies & gating items' (false premise) to 'Loop wiring status' — documents the released/shipped state + the retraction. - §17.5: 'Phase-6 dependency' → 'Loop wiring (resolved — shipped)'. The §15.1 decision→phase ownership matrix, Phase 3 UX-enable gate, and §17 open-question traceability from the prior commit stand (those were accurate). --------- Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
4525bc6f4d |
fix(#1749): close epic #1702 audit gaps — drift-guard bin/install.js, ci-test-scope wiring, ADR divergence (#1751)
Post-merge coverage-audit follow-ups to epic #1702 (found by an independent gpt-5.5/high audit + adr-phase-coverage cross-reference). None are CRITICAL — the 9-rule enforcement shipped and works; these close completeness/integrity gaps between ADR-1703's promises and the as-built reality. 1. drift-guard bin/install.js scope (ADR-1703 L114-119): the drift guard covered src/runtime-homes.cts only; the ADR named bin/install.js too. Phase 6's glob expansion made bin/install.js a covered surface. Extended tests/portability-vocab-drift.test.cjs with TWO sound checks: (a) any bin/install.js top-level function that directly returns path.*() must be in PATH_RETURNING_FNS (tight, 0 FP — the body-contains heuristic is unsound here, ~33 FPs); (b) a curated two-way existence lock on the installer path helpers (catches a rename making a vocab entry stale; keeps the curation in sync with PATH_RETURNING_FNS). The residual new-resolver boundary (temp-var shape) is documented. 2. ci-test-scope wiring (the Phase 6 portability selection rule was ineffective): eslint-rules/ was not in the product-code prefix list, so an eslint-rules-only change set code_changed=false and CLEARED the matched tests (reproduced: targeted_tests=[]). Added eslint-rules/ to the prefix list and the P1-P4 RuleTester suites to the selection rule (it previously listed only P5/P6). Verified: code_changed=true, 11 tests selected. 3. ADR-1703 acceptance note amended to record the two further as-built divergences: the disable-ban shipped as an out-of-band test (not the specified local/no-portability-disable meta-rule — the test runs outside ESLint so it cannot be self-disabled, at least as strong); and the drift-guard bin/install.js scope resolution above. Epic #1702 all eight phase boxes now checked. No runtime change; no-changelog (contributor tooling + docs). Closes #1749 Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
83685ea7a3 |
docs(#1744): portability architecture guide + ADR-1703 acceptance (Phase 7 closeout) (#1745)
ADR-1703 Phase 7 / epic #1702 closeout. The mechanical teardown is already complete across phases 1-6 (regex scanner retired P3, ratchet deleted P4, allowlist portability-usage gone, windows-portability-ok comments swept, every DEFECT.WINDOWS-* predicate rewritten per-phase). This phase delivers the two remaining ADR-mandated closeout items: - docs/contributing/adding-a-portability-rule.md: the forward architecture recipe (Diátaxis Explanation) — the five seams (rule / portability-vocab / platform-guard / disable-ban / ci-test-scope), the zero-escape-hatch contract, the shipped-rule catalog, and the step-by-step checklist for adding a new local/* portability rule. - docs/adr/1703: Status Proposed -> Accepted (all seven phases shipped); a Phase 7 as-built note recording the two deviations from the Phase 0 catalog — Phase 6's rename-only scope decision (#1740) and the codex-review precision tightening on require-fs-op-fallback. - docs/contributing/cross-platform-portability-rules.md: cross-link to the new guide from the rule reference. No code, no test, no runtime change. Epic #1702 Phase 6 box checked; Phase 7 box + epic closure to follow at PR merge. Closes #1744 Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
871621c3c8 |
feat(#1740): require-fs-op-fallback production AST rule + Windows transient-lock retry (Phase 6) (#1742)
* feat(#1740): require-fs-op-fallback production AST rule + Windows transient-lock retry (Phase 6) ADR-1703 Phase 6 of the cross-platform portability epic (#1702). Adds the second production-code portability AST rule + the ADR-mandated glob expansion to bin/install.js and scripts/build-hooks.js. - eslint-rules/require-fs-op-fallback.cjs: flags an unguarded fs.rename / fs.renameSync (the atomic-publish primitive named first in DEFECT.WINDOWS-FS-OPS.symptom) that is NOT inside a try/catch whose handler references a transient errno ('EPERM'/'EBUSY'/'EACCES' or a *RETRY_ERRNOS set) AND NOT behind a Windows platform guard. A catch that silently swallows or cleans-up-and-rethrows without an errno check does NOT satisfy the defect's 'never silently swallow' clause. copyFile/unlink are deliberately not flagged (they are the fallback primitives per the defect's own fix-forward). Scope narrowed to rename per Phase 5's precision discipline; documented on #1740. - src/shell-command-projection.cts: export retryRenameSync(from, to) — the drop-in bounded-retry helper over the existing atomicRenameWithRetry. - 27 bare fs.renameSync sites across 11 modules routed through retryRenameSync (capability-lifecycle/lock/source, installer-migrations, milestone, phase, planning-workspace, roadmap-upgrade, runtime-hooks-surface, state, workstream). Idempotent on POSIX; resilient to AV/indexer transient locks on Windows. - eslint.config.mjs: register rule at error on src/**/*.cts; new focused portability-rules block covering bin/install.js + scripts/build-hooks.js (ADR-1703 L124-126 glob expansion — both files are compliant: zero rename violations). - tests: 15-case RuleTester suite; portability-rule-disable-ban extended (PROTECTED_RULES + scans bin/install.js/build-hooks.js with shebang handling); ci-test-scope portability-lint selection rule. - CONTEXT.md DEFECT.WINDOWS-FS-OPS predicate rewritten to point at the rule; docs/contributing/cross-platform-portability-rules.md reference + how-to. Closes #1740 * chore(#1740): backfill changeset pr:1742 * fix(#1740): tighten require-fs-op-fallback precision (codex review HIGH-1/HIGH-2) Addresses two false-negative findings from the codex (gpt-5.5/high) adversarial review of PR #1742: HIGH-1 — a catch that REFERENCES a transient errno but only rethrows (no retry/fallback) was marked compliant. The DEFECT.WINDOWS-FS-OPS fix-forward requires retry, not just recognition. Fix: catchHandlerHasRetrySignal now requires a loop `continue` backedge OR a `return <call>` delegation; a bare rethrow is flagged. The misleading `/* retry logic */` valid test is replaced with a real retry loop, and the rethrow-only shape is added as invalid. HIGH-2 — the nested-try ancestor walk treated an OUTER errno-catch as protecting the rename even when an INNER catch intercepted/swallowed the error (the outer catch is unreachable). Fix: isInsideTransientErrnoTryCatch now stops at the NEAREST enclosing TryStatement WITH A CATCH HANDLER whose block contains the rename (try-finally is skipped — it doesn't catch); outer catches are no longer consulted. The unsound nested-try valid test is converted to invalid, and a try-finally-skipped valid case is added. Verified: 17 RuleTester cases pass; zero new production violations (the 27 fixed sites use retryRenameSync; the real retry loops — atomicRenameWithRetry, capability-ledger/consent, build-hooks — remain compliant via continue/errno); lint:ci green; disable-ban + vocab-drift green. --------- Co-authored-by: review-bot <review-bot@gsd> |
||
|
|
9d52043f50 |
feat(#1733): normalize-path-in-content production AST rule + fix Windows agent-skills content leak (Phase 5) (#1736)
* feat(#1733): normalize-path-in-content production AST rule (Phase 5) ADR-1703 Phase 5 — the first production-code rule. local/normalize-path-in-content (src/**/*.cts, @typescript-eslint/parser): flags a path-returning fn result (path.basename excluded — returns a separator-less filename) interpolated into an @-reference / config-dir markdown body without .replace(/\\/g,'/') normalization, per RULESET.CONTENT-PATH-NORMALIZATION / DEFECT.WINDOWS-PATH-LEAK-IN-MARKDOWN-CONTENT. Build-and-assess found the canonical defect site (computePathPrefix) already compliant and only 1 src/ hit — a false positive (path.basename in a status message) — eliminated by narrowing (exclude basename; require a real @-ref/ config-dir marker, not bare .md). 0 src/ violations: clean forward-prevention. The out-of-band disable-ban now scans src/**/*.cts too (typescript-estree) so the production rule also cannot be eslint-disabled. Registered (error) + PROTECTED_RULES; CONTEXT.md predicates + how-to doc updated. - RuleTester suite (26 cases) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#1733): add changeset for Windows agent-skills path-leak fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: harden mutation-matrix.cjs stdin read against EAGAIN on non-blocking pipe scripts/mutation-matrix.cjs read piped stdin via readFileSync(process.stdin.fd). On macOS libuv marks the stdin pipe fd non-blocking, so a synchronous read can throw EAGAIN before the writer fills the pipe — intermittently, under heavy CI shard load — aborting the script (status 2) and flaking mutation-matrix-ratchet. Replace with readStdinSync(): an fs.readSync loop that retries on EAGAIN (1ms synchronous Atomics.wait yield), stops on 0-byte/EOF, and rethrows other errors. Deterministic regression test injects EAGAIN via an fs.readSync monkeypatch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: re-run golden-install-parity on src/lib + installer changes (close drift guard) golden-install-parity hashes every installed bin/lib/*.cjs per runtime, so it must re-run whenever the built lib could change. ci-test-scope selected it for neither src/** nor installer changes, so a source-only edit (e.g. #1691's milestone.cts/roadmap.cts) recompiled bin/lib and silently drifted the golden fixtures past the scoped lane. Add golden-install-parity.test.cjs to both the 'TS runtime sources' and 'installer and package layout' selection rules, with behavioral regression tests for each. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: review-bot <review-bot@gsd> |