Commit Graph

171 Commits

Author SHA1 Message Date
Tom Boucher
dd5a2211c9 enhance(#1964): semantic knowledge-base recall via MemPalace (keyword fallback) (#2416)
* test(#1964): add failing-first semantic-recall contract tests

Epic #1957 Phase 3C (final). Source-text-is-the-product contract tests:
semantic recall via MemPalace (top-k meaning-similar prior resolutions, catches
same-root-cause/different-wording cases), indexing resolved sessions at archive,
graceful degradation to keyword matching when MemPalace is absent,
knowledge-base.md stays the durable plain-text source of truth, agent Phase 0 /
Matching Logic is semantic-first (the stale 'keyword overlap, not semantic
similarity' claim must go), and no new embedding/vector infra (reuse MemPalace).

Failing-first: reference, the Matching Logic reframe, the Phase 0 consolidation,
and the archive indexing step do not yet exist.

* feat(#1964): semantic knowledge-base recall via MemPalace (keyword fallback)

Epic #1957 Phase 3C (FINAL). Replaces keyword-overlap matching with semantic
recall: at Phase 0 the debugger queries MemPalace with the current symptoms
and surfaces the top-k meaning-similar prior resolutions, catching the
same-root-cause/different-wording cases keyword overlap missed (the self-noted
'keyword overlap, not semantic similarity' limitation). Resolved sessions are
indexed into MemPalace at archive (symptoms + root_cause(s) + fix + recurrence
guard). knowledge-base.md remains the durable plain-text source of truth; when
MemPalace is absent the debugger falls back to keyword-overlap matching
(logged, never a silent skip). No new embedding/vector infrastructure —
MemPalace is reused.

Size-neutral agent edits: the Matching Logic section reframed (keyword-only ->
semantic-first + keyword-fallback + @-include); Phase 0's three keyword bullets
consolidated into one semantic-first bullet; one MemPalace-indexing step added
at archive. Agent at 57222 B (122 B headroom — final phase). Full rules in
gsd-core/references/debugger-semantic-recall.md. INVENTORY + manifest +
agent-size baseline + install-parity goldens + AGENTS.md updated.

* fix(#1964): address orthogonal review (invocation mechanism, index Resolution-not-symptoms + redaction, fallback detail)

- HIGH: the 'query MemPalace' instruction was WHAT-level only; the agent has
  no MCP tools. Added an Invocation section naming the Bash CLI
  (mempalace search --wing <wing>) + MCP-when-registered + wing resolution
  (config.mempalace.wing -> project_code -> project dir), matching every other
  MemPalace integration. Without this the feature silently degraded to keyword
  matching even when MemPalace was present.
- MEDIUM (security x2): index the agent-authored Resolution summary
  (root_cause + fix + recurrence_guard), NOT raw user-supplied Symptoms —
  excludes attacker-controlled prose from the cross-session index AND reduces
  secret/PII leakage. Redact secret-shaped values before indexing. Stated the
  write order (KB append + commit MUST succeed before indexing).
- LOW: restored 'identifiers' + 'case-insensitive' to the keyword fallback;
  added a test asserting the fallback mechanics survived the Phase 0
  consolidation (Error patterns field, 2+ token overlap, identifiers,
  case-insensitive).

* chore(#1964): ratchet agent-size baseline downward (leaner archive bullet shrank gsd-debugger.md 57222->57197)

* chore(#1964): backfill changeset pr number (PR #2416)
2026-07-18 19:01:04 -04:00
Tom Boucher
c67f301867 feat(#1963): emit blameless-postmortem Prevention block at resolution (#2410)
* test(#1963): add failing-first prevention/postmortem contract tests

Epic #1957 Phase 3B. Source-text-is-the-product contract tests: blameless
5-Whys that BRANCHES per Phase 2A RCA (not a single-cause chain; treats agent
error as 'why was that possible?'), the 'why wasn't this caught?' question,
the recurrence-guard taxonomy (regression test / assertion / lint rule / KB
pattern), the KB-entry why_not_caught + recurrence_guard fields with backward
compat, the session-manager prevention summary line, and the Zawinski
scope-boundary (a block, not a subsystem).

Failing-first: reference, archive_session edit, KB schema extension, and
session-manager summary do not yet exist.

* feat(#1963): emit blameless-postmortem Prevention block at resolution

Epic #1957 Phase 3B. At archive_session the debugger now produces a
Prevention block with three blame-free components: a branching 5-Whys causal
chain (branches per Phase 2A RCA, not a single chain; 'agent error' prompts
'why was that possible?', never blame), a 'why wasn't this caught?' answer
naming the missed gate (test/typecheck/lint/review/verify), and a concrete
recurrence guard (regression test / assertion / lint rule / KB pattern).

The knowledge-base entry gains two structured fields (why_not_caught +
recurrence_guard) so future Phase-0 recall surfaces the prior prevention, not
just the prior fix. Additive: old entries without the fields still load. The
session-manager compact summary surfaces a one-line prevention summary.

Full rules extracted to gsd-core/references/debugger-prevention.md (slim
archive_session step + 2 KB fields kept in the agent). INVENTORY + manifest +
agent-size baseline + install-parity goldens + AGENTS.md updated.

* fix(#1963): address orthogonal review (CRITICAL append-template drift + Phase-0 consumption + parity test)

- CRITICAL: the archive_session KB append template omitted Why not caught +
  Recurrence guard (only the Entry Format had them) — the feature's core
  deliverable silently did not happen. Added both fields to the append template
  the agent actually follows (nearest-instruction wins).
- HIGH: Phase 0 (KB read) only surfaced root_cause + fix; the new fields were
  dead data. Extended the Phase 0 Evidence line to consume why_not_caught +
  recurrence_guard when present (absent on old entries — backward compat holds).
- MEDIUM: added a cross-section parity test (every Entry-Format field must also
  appear in the append template — the guard that would have caught the
  Critical) + a Phase-0-consumption assertion.
- MEDIUM: the 'branches per Phase 2A' claim is now wired — reuses
  reasoning_checkpoint.candidate_causes across the four categories.
- MEDIUM: recurrence-guard taxonomy gains type refinement + config-default
  change; LOW: added 'build' gate to both surfaces for parity.
- NIT: compact-summary fallback shape ('no gate existed'); verify the guard
  artifact exists before recording it.

* test(#1963): anchor Phase-0 consumption test on the specific heading

The regex /Phase 0[\s\S]{0,1200}/ matched the first 'Phase 0' in the file
(in knowledge_base_protocol prose), not the Phase 0 block in investigation_loop.
Anchor on '**Phase 0: Check knowledge base**' and widen to 1500 chars.

* chore(#1963): backfill changeset pr number (PR #2410)
2026-07-18 17:38:54 -04:00
Tom Boucher
36a311c5bb enhance(#1962): harden regression tests (PBT shrinking + oracle classification + boundaries) (#2409)
* test(#1962): add failing-first repro-hardening contract tests

Epic #1957 Phase 3A. Source-text-is-the-product contract tests: PBT shrinking
(fast-check/Hypothesis, minimized seed, manual-minimization degradation), the
four oracle types (specified/derived/metamorphic/implicit with implicit flagged
weakest), boundary neighbors (off-by-one/min-max/empty-singleton tied to the
equivalence class), oracle_type in DEBUG Resolution, and the Phase 1A tie-in
(minimized seed + real oracle => the mutation guardrail bites).

Failing-first: reference, agent cross-refs, and template field do not yet exist.

* feat(#1962): harden regression tests (PBT shrinking + oracle classification + boundaries)

Epic #1957 Phase 3A. Extends Minimal Reproduction (shrinking) and Test-First
Debugging (oracle classification + boundary neighbors):
- Shrinking: wrap an input-space failing input in a property (fast-check JS/TS,
  Hypothesis Python) and store the MINIMIZED counterexample as the regression
  seed; degrade to manual minimization when no PBT framework is present.
- Oracle classification: state specified / derived (contract/model) /
  metamorphic / implicit (crash, weakest) before writing the assertion; record
  under Resolution.oracle_type; never default to implicit silently.
- Boundary neighbors: off-by-one, min/max, empty/singleton around the fixed
  defect's equivalence class.

Together they turn the regression test into a root-cause check — what the Phase
1A mutation guardrail needs to bite. Full rules extracted to gsd-core/references/
debugger-repro-hardening.md. INVENTORY + manifest + agent-size baseline +
install-parity goldens + AGENTS.md + DEBUG template updated.

* fix(#1962): address orthogonal review (bounding, provenance, oracle scope, sufficient-triple)

- HIGH: added a 'Bound the property/shrink run' section (60s timeout, degrade-
  to-manual on timeout, do-not-raise-default-run-limits, argv-not-shell) —
  the gauntlet violation the sibling references already honored.
- Medium: test-provenance caveat (the failing input often comes from the bug
  report — author the generator from a sanitized description, cross-ref
  debugger-fix-acceptance.md).
- Medium: oracle scope note — the 4 types cover deterministic bugs; non-
  deterministic failures re-route to stability-stress per bug-taxonomy.
- Medium: Phase 1A tie-in corrected — seed+oracle is necessary not sufficient;
  boundary neighbors close the adjacent-input escape; the sufficient triple is
  seed+oracle+neighbors.
- Low: preserve the original noisy repro as a secondary reference; operationalize
  'equivalence class' (the predicate the fix draws). Nit: degradation reworded.

* chore(#1962): backfill changeset pr number (PR #2409)

---------

Co-authored-by: sim <sim@local>
2026-07-18 15:46:42 -04:00
Tom Boucher
6baa2a8182 feat(#1961): add bug-taxonomy classification + strategy routing to gsd-debugger (#2407)
* test(#1961): add failing-first bug-taxonomy routing contract tests

Epic #1957 Phase 2B. Source-text-is-the-product contract tests (3 taxonomy
classes, explicit class->technique routing table, Bohrbug->repro+SBFL+bisect,
Heisenbug->record-replay/stability+SKIP-SBFL, Concurrency->atomicity/order/
deadlock checklist, bug_class in DEBUG Current Focus, supersede-not-append)
plus a routing-table specification object pinning the documented decisions
(SBFL forbidden on Heisenbug is the load-bearing 1B/2B seam).

Failing-first: reference, Phase 1.75, and routing-table reframe do not yet exist.

* feat(#1961): add bug-taxonomy classification + strategy routing to gsd-debugger

Epic #1957 Phase 2B (reliability-critical). Adds Phase 1.75: classify the
failure as Bohrbug / Heisenbug-Mandelbug / Concurrency, then route the
investigation technique via an explicit class->technique table (Kernighan: no
opaque heuristic). Bohrbug -> reproduction + SBFL (Phase 1.25) + git bisect;
Heisenbug/Mandelbug -> record-replay (rr) + stability-stress + statistical
sampling, with SBFL explicitly SKIPPED (a flaky spectrum poisons the Ochiai
ranking — the load-bearing 1B/2B seam); Concurrency -> the
atomicity/order/deadlock checklist first.

Reframes (supersedes, not appends — Zawinski) the flat 'Technique Selection by
situation' table into a class-routed table; the 11 techniques remain as routed
targets. bug_class recorded in Current Focus (DEBUG template); common-bug-
patterns catalog cross-referenced to the taxonomy.

Full rules extracted to gsd-core/references/debugger-bug-taxonomy.md. INVENTORY
+ manifest + agent-size baseline + install-parity goldens + AGENTS.md updated.

* fix(#1961): address orthogonal review (phase-name drift, General lane, revoke framing, row-scoped tests, bounding)

- HIGH: reference said 'Phase 1B' (epic shorthand); corrected to the deployed
  'Phase 1.25' (matches the agent + SBFL reference).
- HIGH: 6 of 11 techniques (Rubber duck, Delta, Working backwards,
  Differential, Comment-out, Follow-the-indirection) were orphaned by the
  situation-table reframe. Added a 'General (any class, situation-cued)'
  lane to BOTH the reference routing table and the agent's Technique
  Selection table that re-homes them — supersede-not-append now holds.
- MEDIUM: the SBFL-skip is structurally retroactive (Phase 1.25 runs before
  Phase 1.75 classification), so reframed the table column from 'Do NOT use'
  to 'Revoke if already run' + an explicit 'retroactive revocation, not
  proactive skip' note stating the ordering honestly.
- MEDIUM: contract tests are now row-scoped (parse the table by class, assert
  per-row) instead of presence-only; added a guard that the previously-
  orphaned techniques now have a General-lane route.
- LOW: pinned the canonical bug_class value form (lowercase-kebab:
  bohrbug|heisenbug-mandelbug|concurrency; prose may use title-case).
- NIT: added a 'Bound the Heisenbug-chase runs' note (rr/stability/sampling
  timeouts) per the unbounded-subprocess gauntlet.

* chore(#1961): backfill changeset pr number (PR #2407)
2026-07-18 14:42:29 -04:00
Tom Boucher
f8b16d1874 enhance(#1960): add RCA branching (fishbone + AND-gate) to gsd-debugger (#2405)
* test(#1960): add failing-first RCA-branching contract + schema-invariant tests

Epic #1957 Phase 2A. Source-text-is-the-product contract tests (fishbone
>=2 categories, AND-gate, multi-cause root_cause, backward compat, reasoning
checkpoint candidate_causes+and_gate fields, debugger-philosophy single-cause
note, DEBUG template) plus behavioral schema-invariant checks on two fixtures:
two contributing causes (AND-gate yes) -> both recorded; single-cause
(AND-gate no) -> one root_cause, identical to today.

Failing-first: reference, agent edits, and template note do not yet exist.

* feat(#1960): add RCA branching (fishbone + AND-gate) to gsd-debugger

Epic #1957 Phase 2A. Guards against 5-Whys single-cause bias: before committing
root_cause, the debugger enumerates candidate causes across >=2 Ishikawa
categories (code/config/environment/data) and explicitly answers an AND-gate
question. When the AND-gate fires, every contributing cause is recorded, so a
multi-cause fix no longer recurs via the unaddressed second cause.
Resolution.root_cause may hold one OR a small set (additive; single-cause
sessions are byte-identical to today). The Structured Reasoning Checkpoint gains
candidate_causes + and_gate fields; debugger-philosophy.md adds the
single-cause-bias trap.

Full rules extracted to gsd-core/references/debugger-rca-branching.md (slim
Phase 2 routing + 2 checkpoint fields kept in the agent). INVENTORY + manifest +
agent-size baseline + install-parity goldens + AGENTS.md + DEBUG template updated.

* fix(#1960): address orthogonal review (AND-gate self-consistency, parity guard, narrowed claim, ripples)

- Reference: the collapse rule now enforces AND-gate self-consistency —
  and_gate=yes with a single confirmed cause is flagged as incomplete
  (return to Phase 3); a race/timing note clarifies such bugs bridge
  categories; the 'byte-identical' backward-compat claim narrowed to
  'root_cause shape unchanged; reasoning_checkpoint gains 2 fields in every
  session'.
- DEBUG.md: stale 'five-field' mirror prose -> seven-field (parallel-surface
  drift the reviewer flagged); new debug-session-management parity test pins
  the field-count claim to the gsd-debugger.md YAML keys (CRLF-safe).
- Scalar-assuming consumers of set-valued root_cause updated: session-manager
  compact summaries (319/332), diagnose-only return (1062), archive entry
  (1216), ROOT CAUSE FOUND return (1322).
- Test: added the AND-gate-yes/single-cause invariant + fixture; rephrased the
  fixture describe block honestly as a schema-invariant specification.
- Phase 2 bullet phrasing clarified ('at hypothesis formation, before the
  Phase 4 commit').

* test(#1960): parity regex accepts word-form count ('seven-field' or '7-field')

* test(#1960): parity regex counts array-valued YAML keys (no inline value)

* chore(#1960): backfill changeset pr number (PR #2405)
2026-07-18 13:42:58 -04:00
Tom Boucher
56a5c6404c feat(#1959): add spectrum-based fault localization (Ochiai) pre-filter to gsd-debugger (#2403)
* test(#1959): add failing-first SBFL contract + Ochiai correctness tests

Epic #1957 Phase 1B. Source-text-is-the-product contract tests (Ochiai
formula documented, Tarantula fallback, top-N seeding, no-coverage skip
logged, ranking->Evidence, Bohrbug gating) plus a behavioral Ochiai
formula-correctness section: bound [0,1], max-score invariant, a known-fault
fixture proving the fault ranks #1 (criterion 2), clean degradation on
zero failing tests, and two fast-check properties.

Failing-first: reference file and agent routing do not yet exist.

* feat(#1959): add spectrum-based fault localization (Ochiai) pre-filter to gsd-debugger

Epic #1957 Phase 1B. When a runnable test suite with per-test coverage exists
(>=1 failing AND >=1 passing test), the debugger computes an Ochiai
suspiciousness ranking over the coverage spectrum and seeds the top-N
suspicious locations into Evidence as first-class hypothesis candidates,
narrowing the search space deterministically before LLM reasoning. Tarantula
documented as fallback. Degrades cleanly (logged, never silent) when there is
no test suite, no failing tests, or no per-test coverage, and is explicitly
not trusted on flaky/Heisenbug spectra (pairs with Phase 2B bug-taxonomy).

Full rules extracted to gsd-core/references/debugger-sbfl.md (slim Phase 1.25
routing kept in the agent to respect the size cap). No new coverage framework
— reuses the project's existing test/coverage runner. INVENTORY + manifest +
agent-size baseline + install-parity goldens + AGENTS.md updated.

* test(#1959): bound property generators to valid coverage counts

The [0,1] property generated failedExec independently of totalFailed, but
Ochiai's score is only bounded by 1 under the coverage invariant
failedExec <= totalFailed (a failing test that executed s is one of the
totalFailed failing tests). Out-of-domain inputs (failedExec=100, totalFailed=5)
make the formula correctly return >1. Bound failedExec by totalFailed via
fc.chain so the property tests the real domain. Also cleaned up the ranking
property (removed dead code).

* fix(#1959): address orthogonal review (monotonicity property, degradation row, coverage bounding)

- Replace vacuous ranking property (true-by-sort-construction) with a
  non-trivial monotonicity property: holding totalFailed + passedExec fixed,
  ochiai is non-decreasing in failedExec. An inverted formula would fail it.
- Add the missing 'no passing tests' degradation row (preconditions require
  >=1 passing test; Tarantula would divide by totalPassed=0).
- Bound the coverage subprocess (CLAUDE.md gauntlet): cap the coverage run,
  degrade-to-skip on timeout, never hang the debug session.
- Reword 'discard the ranking' -> 'mark the Evidence entry as revoked (do not
  delete)' per Kernighan auditability.

* test(#1959): bound monotonicity-property generator to valid coverage (failedExecA <= totalFailed)

* chore(#1959): backfill changeset pr number (PR #2403)
2026-07-18 07:55:28 -04:00
Tom Boucher
5e52350736 feat(#1958): add multi-signal fix-acceptance guardrail to gsd-debugger (#2396)
* test(#1958): add failing-first guardrail contract tests

Epic #1957 Phase 1A. Adds source-text-is-the-product tests asserting the
5-signal fix-acceptance guardrail contract (target test, mutation check,
no-op/deletion detector, adjacent tests, revert-and-reconfirm), graceful
degradation, FIX REJECTED BY GUARDRAIL return path, per-signal debug-file
recording, and subprocess bounding.

Failing-first: reference file and agent sections do not yet exist.

* feat(#1958): add multi-signal fix-acceptance guardrail to gsd-debugger

Epic #1957 Phase 1A. Prevents accepting a fix that merely greens the test
(Goodhart defense / APR overfitting). Adds a 5-signal gate run before fix
acceptance: target test, mutation check (Stryker), no-op/behavior-deleting
detector, adjacent/held-out tests, revert-and-reconfirm. Degrades gracefully
when Stryker or a test suite is absent (each skip logged, never a silent pass),
records per-signal results under Resolution.verification, and returns a
FIX REJECTED BY GUARDRAIL outcome the session-manager surfaces for
revise / accept-as-debt / abandon.

Full rules extracted to gsd-core/references/debugger-fix-acceptance.md (slim
routing kept in the agent to respect the agent-size cap). Debug template +
INVENTORY + manifest + agent-size baseline + AGENTS.md updated.

* test(#1958): correct newline-tolerant assertion + regen install-parity goldens

The revert-and-reconfirm assertion collapsed whitespace before matching so
markdown line-wrapping does not break it. Regenerated the golden-install-parity
and install-tree fixtures (npm run gen:golden) to absorb the intentional
gsd-debugger.md / gsd-debug-session-manager.md / DEBUG.md / new reference-file
changes to the installed artifact tree.

* fix(#1958): tighten guardrail per orthogonal review

Addresses the isolated reviewer's findings:
- signal 5 now states its recorded-repro dependency and routes the no-repro
  case to the degradation row; revert mechanism specified (git stash / git
  revert -n); minimality flag tied to diff structure, not revert-ability.
- bounded-subprocesses section now bounds the git subprocess (5-30s) too,
  requires argv-array argument passing, and scopes Stryker to the driving
  regression test (a mutant killed only by a non-driving test is a finding).
- new test-provenance (security) clause: the driving test must be
  agent-authored; bug-report repro scripts are DATA, never executed verbatim.
- tightened 3 contract assertions to bind to specific clauses
  (guardrail_verdict field, deletion-reject-unless-RCA, 60s+git bounding).
- Goodhart framing softened to 'partially-independent'; DEBUG.md template
  verification field notes the nested map shape.

* chore(#1958): backfill changeset pr number (PR #2396)

* fix(#1958): add issue ref to allow-test-rule annotation (ADR-456)

CI lint-allow-test-rule-refs requires every allow-test-rule exemption to
carry a 'see #NNN' issue ref per ADR-456. The new test file's annotation
lacked it; this adds (see #1958).
2026-07-18 00:39:28 -04:00
Tom Boucher
b302f53ee6 refactor(#2368): extract capability arm to capability-command-router (ADR-2346 P2) (#2370)
* refactor(#2368): extract capability arm to capability-command-router (ADR-2346 P2)

Behavior-preserving relocation of the 706-line case 'capability': arm from
gsd-tools.cjs into a new hand-authored bin/lib/capability-command-router.cjs
(sibling of ensure-runtime-build.cjs). The 15 bin/-relative require paths are
rewritten to sibling-relative (correct for bin/lib/). dispatchHostCommand is
now async (capability's install/upgrade/consent ops await the lifecycle); sync
routers (state/phase/…) pass through await unchanged. case 'capability':
removed; capability dispatches via HOST_COMMAND_ROUTERS.

Validated by the existing capability-lifecycle / -consent / -trust / -loader
test suites (no logic changed). Golden install-parity fixtures regenerated.

Closes #2368 (Slice 1 — relocation). Probe consolidation (capHostVersion→
readHostVersion, capReadStrict dedup) deferred to a follow-up slice.

* fix(#2368): add capabilityState/capabilityWriter requires + INVENTORY row

The relocated capability arm references capabilityState (cmdCapabilityState,
resolveCapabilityRuntimeState) and capabilityWriter (cmdCapabilitySet) — both
module-scope requires in gsd-tools.cjs (L288/289) that the initial closure-dep
scan missed. Added as sibling requires to capability-command-router.cjs. Also
adds the new cli module to docs/INVENTORY.md + regenerates the manifest.

* fix(#2368): correct capHostVersion __dirname depth for bin/lib/ relocation

capHostVersion's VERSION/package.json paths were bin/-relative ('..' and
'..','..'); on relocation to bin/lib/ they resolved one level too deep,
so capHostVersion returned 0.0.0 and capability install failed the
engines.gsd gate (#1920). Added one more '..' to each (now resolves
gsd-core/VERSION and repo-root package.json correctly).

* test(#2368): drop capability from the invocation loop (async/FS vs /fake/cwd)

capability is async and does FS/config reads, so invoking it against the
unit test's /fake/cwd is fragile. The 6 sync Tier-1 routers stay in the
invocation loop; capability is covered by the non-invoking registry-
ownership assertion + the dedicated capability-* test suites.

* chore: retrigger CI (no-changelog label now present)
2026-07-17 11:04:47 -04:00
Tom Boucher
2cbf186420 chore(#2143): fail-loud Result + per-surface write-set contract — Phase 3 (#2251)
* chore(#2143): fail-loud Result + per-surface write-set contract — Phase 3

Phase 3 of epic #2143 (ADR-2143 §5/§6). The three target bugs (#2140, #2112,
#2118) were already fixed tactically on next; this introduces the reusable
structural contracts and rewires the primary #2140 site onto them.

- src/write-set.cts (new): the parse `Result<T> = {ok,value|reason}` (§5) and the
  per-surface write-set (`WriteOutcome {surface, applied, requirement?}`,
  `WriteSet`, `writeSetComplete`) (§6). markdown-table.cts now imports + re-exports
  `Result` from here (single source; distinct from command-routing-hub's Result).
- requirements mark-complete (src/milestone.cts): returns a PER-REQUIREMENT,
  per-surface write-set; `write_set_complete` is true only if every surface of
  every requirement applied — structurally forbidding the #2140 OR-into-one-flag
  masking, including across a multi-ID batch (adversarial-review regression).
  Pre-existing output fields unchanged (behaviour-preserving; #2140 already fixed).
- deriveProgressFromRoadmap (src/phase-lifecycle.cts): removed the vestigial
  null-swallowing try/catch (findTableWithColumns never throws) — ADR §5 no-swallow;
  RoadmapProgress return contract unchanged.
- commit --files (#2112) and milestone complete --dry-run (#2118) left as-is
  (single-surface commit / pre-mutation preview — not genuine multi-surface writes).

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

* chore(#2244): backfill changeset PR number (#2251)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:39:58 -04:00
Tom Boucher
efd04716da chore(#2143): withSection bounded-mutation seam + phase.cts migration — Phase 2 (#2250)
* chore(#2143): bounded-mutation seam (withSection/withPhaseSection) + phase.cts migration — Phase 2

Phase 2 of epic #2143 (ADR-2143 §4): add a bounded-mutation primitive so a
per-phase ROADMAP edit is structurally confined to that phase's own section,
and migrate the phase-scoped mutation sites in `phase.cts` onto it.

- `src/markdown-sectionizer.cts`: `withSection(content, target, edit, opts?)` —
  resolves a section via `collectSection` and applies `edit` to ONLY that
  section's body, re-serialising via `replaceSection`. The edit callback sees
  only the section body, so any regex it runs is physically confined.
- `src/roadmap-parser.cts`: `withPhaseSection(content, phaseId, edit)` —
  resolves a phase's `### Phase N` detail-section heading via the #2121
  phase-id source and delegates to `withSection`. Heading match is anchored to
  the heading start (a sibling phase whose title mentions the number is not
  hijacked) and bounds at the next ATX heading of any level (`levelBounded:false`).
- `src/phase.cts`: `mutateMilestonePhase`'s plan-count and per-plan-checkbox
  writes now route through `withPhaseSection` — structurally retiring the
  #2130 / #2067 / #2080 boundary-crossing class for these sites. The phase-LIST
  checkbox is intentionally left milestone-slice-scoped (it lives outside any
  `### Phase N` detail section). Cross-phase renumbering is untouched.
- Property test (fast-check): editing phase k leaves every sibling section
  byte-identical; regression tests for title-collision + mixed heading depth.

Behaviour-preserving (verified by old-vs-new differential runs on real
fixtures). Extend-never-mutate (ADR-2143 §2). Registration: CONTEXT.md +
docs/INVENTORY.md export lists.

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

* chore(#2243): backfill changeset PR number (#2250)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:39:22 -04:00
Tom Boucher
d49ac81306 chore(#2143): markdown table model + schema registry + fail-loud pilot — Phase 1 (#2248)
* chore(#2143): markdown table model + schema registry + fail-loud pilot — Phase 1

Phase 1 of epic #2143 (ADR-2143): consolidate markdown table parsing onto a
canonical seam and migrate the pilot reader.

- Add src/markdown-table.cts: parseMarkdownTable (GFM tables -> typed
  {columns, rows} addressed by column NAME; ragged rows are typed parse
  errors, not silent), a single-source TABLE_SCHEMAS registry
  (RoadmapProgress / RequirementsTraceability / QuickTasks / Security, with
  variants under one id), matchTableSchema, and findTableBySchema. Result<T>
  is scoped to this seam (distinct from the dispatch Result).
- Migrate deriveProgressFromRoadmap (src/phase-lifecycle.cts) off the
  position-anchored regex to name-based resolution via the seam — fixes #2137
  (the 5-column milestone-grouped Progress table previously returned all-null).
- Add a schema-backed `gsd-tools quick-tasks-append` subcommand and route
  fast.md's log_to_state through it, retiring the inline `awk NF-2` column
  arithmetic — fixes #2133 (addresses #2012, #2119). Cell values are escaped
  (| and newlines) and the STATE.md read-modify-write is atomic under
  readModifyWriteStateMd (lost-update race, cf. #500/#905/#1230).
- Writer/reader/template parity test guards TABLE_SCHEMAS against drift
  (ADR-2143 §3 Generative-Fix-Divergence).

Registration: .gitignore, eslint.config.mjs, docs/INVENTORY.md +
INVENTORY-MANIFEST.json, CONTEXT.md glossary, docs/CLI-TOOLS.md.

Behaviour-preserving for the canonical 4-column Progress table; the named
bugs are driven fail-first. Extend-never-mutate (ADR-2143 §2).

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

* chore(#2242): backfill changeset PR number (#2248)

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

* fix(#2242): escape backslash before pipe in markdown-table cell escaping

CodeQL js/incomplete-sanitization (high): escapeCell escaped | -> \| but not
the backslash itself. Now escapes \ -> \\ before | -> \|, and splitTableRow
unescapes both \\ -> \ and \| -> | symmetrically so cell values (incl.
literal backslashes) round-trip exactly. Added backslash round-trip tests.

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

* fix(#2242): read ROADMAP Progress table by column name — supersede #2168 ad-hoc scan

Rebase reconciliation with #2168 (the tactical #2137 fix that marked itself
"pending #2143"). deriveProgressFromRoadmap now resolves the Progress table via
a new seam helper findTableWithColumns (first table whose header is a superset of
Phase/Plans Complete/Status/Completed, any order, extra columns ignored) and reads
cells by NAME — order/injection-invariant per ADR-2143 §3 — instead of the exact
TABLE_SCHEMAS match. This satisfies #2168's column-invariance property test while
staying seam-based and preserving its `## Progress` scoping (#2012/#1445).
Ragged Progress tables now resolve to null (ADR-2143 fail-loud); updated the stale
state.test.cjs assertion that predated the Phase-1 migration.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:36:14 -04:00
Tom Boucher
bd613566cb feat(#2100): drive Windsurf through the EoS descriptor + wire Cascade's blocking hook bus (ADR-1239)
Fold all 10 residual isWindsurf branches in bin/install.js onto descriptor-driven
hostBehaviors (byte-parity — no fold changes any install output):
- 2 dead destructures dropped (uninstall, finishInstall); the dead
  `else if (isWindsurf)` legacy agent-loop arm removed (windsurf ∈
  _DESCRIPTOR_AGENTS_RUNTIMES → unreachable).
- skipSharedHooksInstall:true folds the two `!isWindsurf` shared-hooks exclusions.
- legacyDevinSkillsCleanup:true folds the `.devin`→`.windsurf` one-time cleanup gate.
- installsCommandBodiesForWorkflowDelegation:true folds the #1629 command-body copy
  (workflow-delegation target — load-bearing; local-install verified intact).
- verificationStyle:"windsurf-workflows" folds the workflow-count report.
- Corrected stale _LEGACY_SCAN_SUBDIR_NAMES + hooks-json manifest comments (cursor + windsurf).
Zero live runtime==='windsurf'/isWindsurf branches remain across bin/install.js,
install-engine.cts, surface.cts, runtime-artifact-conversion.cts (AC2 guard scans all four).

UPGRADE (Cascade hook bus): wire GSD's write/command safety guards into Windsurf's
native hook bus. New hooksSurface 'windsurf-hooks-json' (VALID_HOOKS_SURFACES 7→8, GATE A
profile-marker-only allowlist, the HooksSurface union) + writeWindsurfHooksJson
(Cursor-templated, Cascade's flat {hooks:{<event>:[{command}]}} shape) writing
.windsurf/hooks.json with two BLOCKING pre-hooks:
- pre_write_code → gsd-windsurf-pre-write.js: blocks writes to a file outside the
  active git worktree / into .git internals.
- pre_run_command → gsd-windsurf-pre-command.js: conservative destructive-command
  deny-list (rm -rf of root/home incl. sudo/env/path-prefixed forms; fork bombs;
  force-push refspec forms — HEAD:main, +main, --force/-f — to main/master/next).
Both use Cascade's protocol (stdin JSON, exit 2 + stderr to block, exit 0 to allow,
fail-open on error/timeout). Tokenize-based classifier (no catastrophic-backtracking regex;
4096-char cap) with the fail-closed false-positives fixed post-review.

The 4 advisory GSD guards + pre_mcp_tool_use + 5 post_* logging events are deliberately
NOT wired: Cascade has no context-injection channel for advisory hooks and GSD has no MCP
guard — porting them would be non-functional padding (documented; codebuddy #2098 / copilot
#2099 faithful-subset precedent). extendedHookEvents stays [].

Golden: the 2 guard scripts ship in the shared hook bundle (HOOKS_TO_COPY + the shared
managed-hooks-registry), exactly like cursor's 6 gsd-cursor-*.js scripts — so the 8
shared-bundle runtimes' fixtures gain the 2 inert windsurf scripts + the registry hash
(functionally inert for non-windsurf; the established cursor pattern). No install-output
change beyond that (the folds are byte-parity; skip-bundle runtimes untouched). New scripts
registered in managed-hooks-registry + build-hooks + INVENTORY. Tests: declarative-reference-
windsurf (adapter/axes/fail-closed + AC2 guard) + windsurf-hooks-bridge (live exit-2 blocking
+ allow/fail-open + ReDoS-bound + writer/reconcile/remove idempotency); VALID_HOOKS_SURFACES
pin updated to 8. Matrix hookBus delta + changeset (Changed). capability-registry regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 16:04:24 -04:00
Dave
c1756d0cd5 chore(#1867): register ui-consideration probe + regen install cascade (SHIP-01)
Ship-safe registration + regenerated snapshots for the #1867 UI-consideration
probe (Phase 3, SHIP-01):

- CONTEXT.md: PROBE.ui.{verification,axis,seam} predicates + ui-consideration
  -probe added to PROBE.family (machine-canon for the 3rd adapter, MIXED axis).
- agents/gsd-ui-{researcher,checker}.md: one @-include of
  references/ui-consideration-probe.md each (both under the 24576 agent cap).
- docs/INVENTORY.md + INVENTORY-MANIFEST.json: register the reference doc and
  the compiled ui-consideration-probe.cjs (inventory-manifest-sync green).
- tests/fixtures/golden-install-parity/*.json (16 runtimes): recaptured against
  a clean full build — folds in the deferred Phase-1 (ref doc, plan-phase lift)
  and Phase-2 (ui-phase step, UI-SPEC section) install-surface changes.
- tests/agent-size-baseline.json: ratcheted the two grown UI agents.
- .changeset/vivid-orcas-chatter.md: type Added (pr updated at PR-open).

Inventory/golden/size gates green; lint:ci + lint:docs + lint:changeset green.
The plan-phase.md PRE_PHASE6 ceiling stays RED pending #1852 (unchanged).

Claude-Session: https://claude.ai/code/session_01BKt4hgNZwXSeJYJtYAQUSS
2026-07-10 14:40:03 -04:00
Tom Boucher
303a796579 docs(changeset): #2089 cursor host-integration migration + golden fixture 2026-07-09 00:21:51 -04:00
Tom Boucher
5aa7771dd3 Merge branch 'next' into fix/2009-load-failed-capability-injects-blocking- 2026-07-07 23:12:09 -04:00
Tom Boucher
015c3a7fda fix(#2071): extract install-time effort resolvers so effort sync stops requiring the un-shipped bin/install.js
`gsd-tools effort sync` crashed in every installed runtime (e.g. ~/.claude/gsd-core/)
with `Cannot find module '../../../bin/install.js'`: cmdEffortSync (src/commands.cts)
required the package-root bin/install.js for its install-time effort resolvers, but the
installer only copies the gsd-core/ subtree into a runtime home — bin/install.js is never
present there. So `effort` config changes silently never reached installed agents without
a full reinstall (exactly the gap #488 was meant to close). 4th instance of the recurring
"runtime code under gsd-core/ requires a file outside the shipped subtree via ../../../"
anti-pattern (#1223/#1920/#1383 were the prior three, all already mitigated).

Fix (ADR-457 direction — extract, single source): move readGsdEffectiveEffortConfig +
resolveInstallTimeEffort (with their _getGsdEffortCatalog + _readGsdConfigFile helpers)
out of the hand-authored bin/install.js into a new src/install-effort-resolver.cts that
compiles into the shipped gsd-core/bin/lib/install-effort-resolver.cjs. commands.cts now
requires it as a sibling (`./install-effort-resolver.cjs`) — always present in the
installed tree — instead of `../../../bin/install.js`. bin/install.js imports the same four
symbols back from the new module (it still calls them + re-exports them), so there is one
source of truth and no duplication/drift. The lazy manifest read is repointed from the
package-root layout (`.., gsd-core, bin, shared`) to the bin/lib layout (`.., shared`).

Scope note: this is one of four instances of the anti-pattern; the other three are already
shipped/guarded. A build-time guard rejecting new cross-boundary requires whose target isn't
in the installer copy manifest (to prevent instance #5) is recommended on the issue but kept
out of this fix.

Tests: tests/effort-sync-installed-runtime.test.cjs does a real minimal install into a temp
home (the golden-parity helper) and runs the issue's exact repro
(`gsd-tools effort sync --config-dir <temp>`), asserting no MODULE_NOT_FOUND for
bin/install.js. Fail-first verified: against pristine next the same test throws
`Cannot find module '../../../bin/install.js'` at cmdEffortSync; post-fix it syncs cleanly.

New module registered in .gitignore (ADR-457), eslint ignores, docs/INVENTORY.md +
INVENTORY-MANIFEST.json. bin/install.js is not shipped and the new module is under bin/lib
(excluded from golden parity), so no golden fixtures change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:21:45 -04:00
Tom Boucher
46f8d3814c fix(#2009): load-failed capability gates fail open with a loud warning
Previously a capability that failed to LOAD (e.g. incompatible engines.gsd) but
declared a gate-kind loop hook caused the loop resolver to inject a BLOCKING
synthetic gate (blocking:true, onError:halt) at every declared point, halting
every ship:pre / verify:post project-wide over an unrelated load error, with no
remediation surfaced.

Per maintainer decision (#2009) it now fails OPEN: no gate is injected (the loop
proceeds; --active-cap correctly reports the failed cap inactive) and a loud
warning is emitted — to stderr (the channel host workflows/agents actually see)
and in the envelope 'warnings' array — naming the load reason and the exact
'gsd capability remove <id>' remediation. The loader still records blockedGates;
only the consequence changes from block to warn.

Security (review): capId and reason originate from a third-party manifest /
directory name. capId is validated against the canonical kebab-case id shape
before it is placed in the runnable remediation command (withheld otherwise);
reason is stripped of control chars and backticks. This closes an argument/
prompt-injection vector in the surfaced message.

Docs updated to the fail-open-warning posture (ARCHITECTURE, INVENTORY,
CONFIGURATION, README, capability-overlay-model). Also removes a dead 'before'
import surfaced by lint in the issue-2045 test.
2026-07-07 19:38:01 -04:00
Tom Boucher
6addeccd19 feat(ai-integration): API-coverage verify:pre gate (#1562)
Full API Coverage by Default — Opt Out, Never Opt In. A phase that integrates
an external API/SDK/service can no longer seal without a decided coverage matrix.

- src/api-coverage.cts: deterministic detector (compound verb+noun signal +
  <Service> API/SDK surface; stopword-guarded; strips fenced code) + matrix
  parse/validate/render with field-length caps.
- check api-coverage.verify-pre: blocking seal-time gate; phase arg resolved as
  a token under .planning/phases/ only (traversal-neutralized); validates
  COVERAGE.md or blocks iff a strong integration signal is detected and no
  matrix exists; fail-closed when phases tree exists but phase unresolvable.
- capabilities/ai-integration: workflow.api_coverage_gate config key (default
  true), plan:pre contribution, blocking verify:pre gate. Data-driven.
- gsd-core/workflows/verify-work.md: generic verify:pre gate dispatch.
- Tests: detector FP/FN + matrix validation + fast-check bijection; gate e2e.
  Code+security review findings fixed (stopword FP, scope containment, pipe/cap
  rejection, prompt-injection message hygiene).
- Regenerated registry/matrix/loop-host-contract/goldens/baseline + docs.

Closes #1562
2026-07-07 15:11:12 -04:00
Tom Boucher
603593d41d fix(#1857): test gates normalize to one-shot + bounded timeout (no watch-mode hang)
A GSD verification gate resolves a project's test command and runs it. vitest
defaults to WATCH mode in an interactive TTY — exactly where a user runs
`gsd-execute-phase` — so a resolved `npm test`/`pnpm test` backed by vitest never
exited and the orchestrator waited indefinitely. Recovery needed the user to
manually prompt "something blocking?".

Fix — one shared helper + a bounded, surfacing timeout on the test-command gates:
- New pure module src/normalize-test-command.cts + `gsd-tools query
  normalize-test-command` verb: rewrites a resolved command to a best-effort
  one-shot form (direct vitest → `vitest run`; jest `--watch` → `--watchAll=false`;
  a package-manager `test` script whose package.json runner is watch-vitest →
  `CI=true` prefix; handles `--dir`; already-one-shot commands unchanged —
  never double-flagged). Named `normalize-test-command` (not `test-*`) so the
  file does not match node --test's default `test-*` discovery glob.
- The three gates that HUNG or silently-continued route through that ONE helper
  and bound execution with `timeout $(config-get workflow.test_gate_timeout)`
  (new config key, default 600s): the regression gate (extracted to
  execute-phase/steps/regression-gate.md since execute-phase.md is size-frozen —
  it shrank 93528→93132; ABORTS on exit 124), the post-merge gate, and the
  audit-fix gate (previously an UNBOUNDED `eval`). All name watch/dev mode on 124.
- verify-phase's gate was ALREADY bounded (a fixed `timeout 300`, not a hang), so
  it only gains the normalizer (so a watch runner exits fast) + a watch-mode hint
  on 124, staying under its frozen 40960-byte tier cap.

Security hardening (review): the normalizer only rewrites a runner named as a
standalone command TOKEN (so `run-vitest.js`/`make test-vitest`/paths are never
mangled), is length-capped and uses only linear-time split-based scanning (no
super-linear backtracking on an adversarial `workflow.test_command`), and reads
package.json only when it is a regular file (never blocks on a FIFO via `--dir`).

Config key `workflow.test_gate_timeout` (seconds, default 600) registered in the
schema manifest + templates/config.json + docs/CONFIGURATION.md (mirrors
workflow.cross_ai_timeout). New module registered in .gitignore, eslint ignores,
inventory manifest/index. All 16 golden-install-parity fixtures + workflow size
baseline regenerated for the changed shipped files; bin/lib is excluded from the
parity manifest.

Tests: tests/normalize-test-command.test.cjs (normalizer units incl. security
hardening) and tests/test-gate-watch-mode.test.cjs (the three core gates route
through the shared helper + configured timeout + exit-124 watch-mode hint;
verify-phase asserted as normalize-only/already-bounded).
tests/execute-phase-active-flags.test.cjs repointed at the extracted step;
tests/planner-language-regression.test.cjs allowlist comment updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:14:43 -04:00
Tom Boucher
ea4378063f Merge branch 'next' into feat/1820-specless-predicate-rail 2026-07-07 07:49:07 -04:00
Dave
7ef834cabc feat(#1820): spec-optional predicate rail — author probe predicates into must_haves when SPEC omits them 2026-07-06 14:40:38 -04:00
Jeremy McSpadden
896c2740d3 feat(#1990): add onboard command for brownfield setup 2026-07-05 19:16:06 +00:00
Jeremy McSpadden
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>
2026-07-03 12:18:25 -04:00
Tom Boucher
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
2026-07-01 20:09:01 -04:00
Rezolv
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>
2026-06-29 00:14:32 -04:00
Tom Boucher
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>
2026-06-26 23:35:04 -04:00
Tom Boucher
b307c4cfde refactor(#1734): extract install engine from bin/install.js (ADR-1239 Phase B deep move) (#1735)
* refactor(#1734): extract install engine from bin/install.js (ADR-1239 Phase B deep move)

Relocate the runtime-artifact install cluster out of the 12,490-line
bin/install.js into a dedicated src/install-engine.cts -> install-engine.cjs:
installRuntimeArtifacts, uninstallRuntimeArtifacts, installOpencodeFamilySkills,
and their cluster helpers (_copyStaged, snapshot/restore, legacy migration,
GSD-entry pruning, preserve/restoreUserArtifacts, OpenCode-family converters,
USER_OWNED_ARTIFACTS).

- bin/install.js imports the engine and re-exports the moved symbols for
  back-compat; getCommitAttribution STAYS in install.js (impure config I/O +
  argv explicitConfigDir global) and is injected via a resolveAttribution param.
- 17 test files migrated to import the moved symbols from the engine.
- Bookkeeping: eslint built-artifact ignore, .gitignore, INVENTORY manifest+row,
  CONTEXT.md Install Engine Module glossary seam.

Behaviour-preserving: install output is byte-identical for all 16 runtimes
(golden-parity harness #1730) — the only delta is the new install-engine.cjs
file shipping in the installed gsd-core/bin/lib/ tree.

Closes #1734

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

* chore(#1734): backfill changeset PR number (#1735)

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>
2026-06-25 21:16:47 -04:00
Tom Boucher
30d4b85de5 feat(#1684): negotiated host-integration interface (ADR-1239 Phase A) (#1690)
* feat(#1684): add negotiated host-integration interface module

ADR-1239 Phase A: a pure, additive, no-I/O module exposing PROTOCOL_VERSION, the 8-axis HOST_INTEGRATION_AXES closed vocabulary, the UNDOCUMENTED fail-closed sentinel, negotiateHostCapabilities (effective subset of host-declared and engine-known), a typed degradation ladder, and host-capability profiles.

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

* feat(#1684): validate and document host-integration axes (16 runtimes)

Extend validateRuntimeBody to validate the 8 hostIntegration axes (closed enums + undocumented sentinel + dispatch struct + reserved-key guards) and the widened runtime vocabulary; author a documentation-sourced hostIntegration block in all 16 runtime descriptors; regenerate the registry. Every per-CLI value is documented (cited) or the explicit undocumented sentinel.

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

* docs(#1684): add host-integration capability matrix and adr amendment

New per-CLI, per-axis citation reference (value/source/evidence for all 16 CLIs); ADR-1239 Phase-A-implemented amendment; CONTEXT.md glossary seam entry.

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

* fix(#1684): harden dispatch negotiation edge cases

Code-review hardening: treat NaN/Infinity maxDepth as missing (fail-closed, +warning); reset nested/background when namedDispatch collapses to false (struct consistency); SAFE_DEFAULTS dispatch floor to read-only; warn on non-finite protocolVersion; symmetric undocumented warnings for dispatch fields. Pure module — no consumers; behaviour fail-closed throughout.

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

* chore(#1684): register host-integration.cjs in lint-ignore and inventory

New tsc-generated bin/lib artifact: add to the eslint ignore list (ADR-457 — lint the .cts source), regenerate docs/INVENTORY-MANIFEST.json, and add the docs/INVENTORY.md CLI-modules row. Fixes the 3 gsd-test failures (551-eslint-bin-lib-coverage x2 + inventory-manifest-sync).

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

* docs(#1684): add changeset fragment for host-integration interface

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

* docs(#1684): add how-to for sourcing a host's integration axes

Diataxis how-to guide for adding/updating a host's runtime.hostIntegration axes from authoritative docs, the undocumented-sentinel rule, validation, and extending the closed vocabulary. Completes the Step-5 doc quadrants (reference + explanation + how-to). Indexed in docs/README.md.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:33:06 -04:00
Alex V.
1a46109b97 enhance(#1579): deterministic gsd-tools query eval.score verb (#1583)
* feat(#1579): deterministic gsd-tools query eval.score verb

Split C of #1573 (pure code, lowest risk). Adds an eval.score query verb
(coverage*0.6 + infra*0.4; bands 80/60/40) mirroring the verify.* chain;
gsd-eval-auditor consumes it instead of doing weighted arithmetic in-prompt.
Non-breaking — additive only.

arXiv: 2601.15130 (Plausibility Trap/DPDM), 2507.10281 (Table Agent), 2508.15754 (TIR).

* fix(#1579): address review — domain guard, property test, glossary, SKIP_ROOT, inventory/baseline

- C3 input-domain: reject out-of-domain eval.score (require 0<=covered<=total; was emitting overall_score>100 / negatives)
- C1 property test: add tests/eval.property.test.cjs (fast-check) — determinism, band monotonicity, [0,100] bounds, never-throws
- C2 glossary: CONTEXT.md "Eval Scoring Module" entry (source-of-truth path + interface)
- C4: add `eval` to SKIP_ROOT_RESOLUTION (pure arithmetic; no .planning/ access)
- inventory: register generated eval.cjs/eval-command-router.cjs (INVENTORY-MANIFEST.json + INVENTORY.md rows)
- size: regen agent-size baseline for gsd-eval-auditor (reused gsd_run shim + eval.score step)
- eslint: ignore generated eval*.cjs (ADR-457 bin/lib migration coverage)

* fix(#1579): register eval family in alias-drift gates

Add EVAL_COMMAND_ALIASES/EVAL_SUBCOMMANDS to scripts/check-alias-drift.cjs
families and to familyArrayKeys in the manifest-coverage test, so the eval
family lands under the same drift guard as every sibling family
(state/verify/init/phase/phases/validate/roadmap). Addresses trek-e review.

check:alias-drift ok; feat-3251 coverage 9/9; eval suites 10/10.

* docs(#1579): use half-open verdict band ranges in CLI-TOOLS

overall_score is fractional and thresholds are >=80/>=60/>=40, so a score
in [79,80) is correctly NEEDS WORK despite the old '60-79' label. Relabel
bands as 60-<80 / 40-<60 / 0-<40 to match the code. Addresses trek-e nit.

* fix(#1579): validate eval.score CLI inputs

Reject unknown infra tokens and fractional counts, and pin the 80-point verdict boundary including rounding-before-banding behavior.
2026-06-24 17:16:13 -04:00
Alex V.
a63684c222 enhance(#1577): WebFetch/WebSearch injection isolation + opt-in blocking (#1585)
* fix(#1577): isolate WebFetch/WebSearch ingress + opt-in injection blocking

Split A of #1573 (security-critical). Scans WebFetch/WebSearch output (the
largest untrusted channel) in gsd-read-injection-scanner; shared
untrusted-input-boundary reference @-included by the 8 ingest agents
(randomized per-wrap delimiters, in-prompt self-scan guard, task-anchoring);
opt-in security.injection_blocking (default advisory — non-breaking).

arXiv: 2506.05739 (PPA), 2507.15219 (PromptArmor), 2504.20472 (Referencing), 2503.00061 (defense-in-depth).

* fix(#1577): address review — honest blocking docs, config key, ADR, property test, revert localized

- A1: rewrote the opt-in-blocking doc + Security changeset honestly — the PostToolUse hook is a
  circuit-breaker (halts the agent's next step), NOT a redactor; it does not scrub content already
  in the transcript. The prompt-level data/instruction boundary is the primary control.
- A2: registered security.injection_blocking in the config schema + defaults manifests (default
  false) + an e2e config-roundtrip test; the dotted setter writes the nested shape the hook reads.
- A3: reverted the 4 hand-edited localized security-model.md (canonical EN only, per convention).
- A5: ADR-1577 (untrusted-input boundary + opt-in blocking; redaction-vs-circuit-breaker rationale).
- A6: property test — scanner never crashes / only emits valid JSON on unicode/large/malformed input.
- Also: inventory (untrusted-input-boundary.md) + agent-size baseline (8 ingest agents) +
  drift-guard matcher update (Read -> Read|WebFetch|WebSearch). A7 (content<20 early-exit) left as
  the noted pre-existing follow-up.

* fix(#1577): allowlist untrusted-input-boundary.md in injection-scan CI gate

The new reference quotes injection phrases ('ignore previous instructions',
'you are now…') as examples agents must NOT comply with, tripping the repo's
own prompt-injection-scan.sh diff gate (the standalone 'security' CI job, red
on HEAD). Allowlist it alongside the other security docs (security-model.md,
TEST-EXAMPLES.md) that legitimately demonstrate injection patterns. The JS
scanner test doesn't scan references/, so only the shell gate needed it.

Verified: scan --diff origin/next -> 0 findings; scanner JS test 15/15.

* fix(#1577): cover AC #2's gsd-ui-researcher + gsd-assumptions-analyzer

trek-e Major 1: the @-included set dropped two AC #2 agents. Restore them so
no named web-ingress agent is uncovered, keeping the two justified additions
(gsd-ai-researcher, gsd-domain-researcher). Final set = AC's 8 + 2 = 10.
 - gsd-ui-researcher carries the full WebSearch/WebFetch + MCP-fetch toolset.
 - gsd-assumptions-analyzer reads 5-15 codebase source files (external/source-
   document ingress per the boundary), though it has no web tools.
INGEST_AGENTS in the isolation test now asserts all 10; size baselines
regenerated (+60 bytes each, both well under the DEFAULT cap); changeset
reworded 8 -> 10.

Verified: untrusted-input-isolation 14/14; agent-size-budget 39/39.

* docs(#1577): document security.injection_blocking + boundary seam

trek-e Major 2 + Minor:
 - docs/CONFIGURATION.md: add the top-level security.injection_blocking key to
   the Full Schema and a Security Settings subsection, distinguishing it from
   the workflow.security_* namespace; honest circuit-breaker-not-redactor
   framing matching ADR-1577 / security-model.
 - CONTEXT.md: add the 'Untrusted-input boundary' seam glossary entry.

Verified: lint:docs ok; config-field-docs + contributor-standards green.

* test(#1577): make read-injection property test git-text, not binary

trek-e nit (and more): the file embedded a raw U+FFFF AND a raw NUL byte as
degenerate-edge inputs. The NUL is what actually made git classify it binary
(git binary = NUL in first 8K). Replace both with text-safe escapes that keep
the identical runtime values: '\\x00' and String.fromCodePoint(0xFFFF). File
now diffs/blames line-by-line.

Verified: property test 2/2; no NUL/raw-noncharacter bytes remain.

* docs(#1577): align untrusted boundary docs

Name all 10 ingress agents in INVENTORY/security-model and allowlist the intentional read-injection property corpus for the prompt-injection scanner.

* docs(#1577): align ADR ingest agent count

Update ADR-1577 from 8 to 10 ingest agents so it matches the actual boundary include set and the rest of the docs.

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-24 17:07:23 -04:00
Tom Boucher
f9d9dfb4bc fix(#1627): scale security rigor by ASVS level (planner disposition + auditor depth) (#1636)
workflow.security_asvs_level was display-only — the planner hardcoded
'mitigate if ASVS L1 requires it' and the auditor only echoed the level,
so L2/L3 behaved identically to L1.

- New reference gsd-core/references/security-asvs-levels.md defines L1
  (opportunistic), L2 (standard), L3 (comprehensive) for both planner
  threat disposition and auditor verification depth (higher = superset).
- planner: disposition now scales with the configured ASVS level (no
  hardcoded L1) + @-pointer to the reference.
- auditor: verification depth scales with asvs_level (L1 grep-presence,
  L2 boundary/vector check, L3 end-to-end trace + bypass check).
- planning-config.md + INVENTORY updated; planner kept under its 48K cap
  by extracting the goal-backward worked example to planner-guidance.md.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:48:58 -04:00
Tom Boucher
ba96c70b14 feat(#1602): deterministic coverage-metadata UAT routing for verify-work
Add an optional structured `coverage:` block to SUMMARY.md frontmatter and a
deterministic classifier that `verify-work` consumes to route deliverables to
auto-pass vs human-UAT — replacing the rejected #1598/#1599 post-hoc heuristic.

- New `src/coverage.cts` (→ bin/lib/coverage.cjs) parses the nested coverage
  block (extractFrontmatter can't — its `-` items are scalars-only; this is a
  focused parser, sibling of parseMustHavesBlock), validates each entry, and
  classifies into auto_passed vs present. Frozen MODE/PRESENT_REASON/ERROR_CODE
  typed-IR surface. Exposed via `uat classify-coverage --summary <f>`.
- Auto-pass is the narrow proven case only: strict-boolean human_judgment:false
  AND non-empty all-`pass` verification AND zero validation errors. Everything
  else — judgment, empty/failing verification, malformed entry — routes to the
  human (fail-safe). A malformed block falls back to legacy prose extraction and
  surfaces an error; an absent block is byte-identical to pre-#1602.
- execute-plan create_summary populates the block (fail-safe default
  human_judgment:true); verify-work extract_tests consumes it; create_uat_file
  marks auto-passed entries `source: automated`.
- Templates (summary + 3 variants), CONTEXT.md predicate + glossary, INVENTORY,
  eslint/gitignore registration, and Diataxis docs (COMMANDS reference +
  USER-GUIDE explanation) updated.
- Behavioral tests via the CLI (no source-grep); parser-robustness regressions
  for the null-entry/comment-header/mis-indent cases found in adversarial review.

Closes #1602

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:43:22 -04:00
Joe
e12a2abfd8 feat(#441): add /gsd-capture --list-seeds for seed listing and audit (#722)
* feat(#441): add /gsd-capture --list-seeds for seed listing and audit

Seeds (.planning/seeds/SEED-NNN-slug.md) could only be created (--seed),
enriched (--enrich), or auto-surfaced at /gsd-new-milestone. There was no way
to browse or audit parked seeds on demand. This adds a read-only listing,
following the established --list → workflow pattern (per the approved scope on

- gsd-tools `list-seeds [status]` (cmdListSeeds in src/commands.cts): scans the
  seeds dir, returns { count, seeds[], summary } JSON with each seed's id,
  slug, status, scope, trigger_when, planted, title. Optional case-insensitive
  status filter. User-controlled content is sanitized (sanitizeForDisplay) and
  every path validated (requireSafePath); read-only. Independent of
  audit.scanSeeds, which only returns unimplemented seeds for the milestone surface.
- /gsd-capture --list-seeds routes to a new read-only list-seeds workflow that
  renders the seed table.

Closes #441

* chore(#441): point changeset fragment at PR #722

* test(#441): allowlist list-seeds test in prompt-injection scan

The test asserts that list-seeds neutralizes injection payloads
(<system>, [INST]) embedded in seed content, so the fixtures legitimately
contain those patterns — same as the sibling security tests already on the
allowlist.

* fix(#441): use canonical /gsd:capture colon form in list-seeds workflow

Claude-facing source (commands/, agents/, gsd-core/workflows/, ...) must use
the /gsd:<cmd> colon form per ADR/CONTEXT.md; the hyphen /gsd-<cmd> form is
retired there (enforced by bug-2543-gsd-slash-namespace.test.cjs). The new
list-seeds workflow used the hyphen form.

* docs(#441): sync help full.md + INVENTORY for --list-seeds

Adds the --list-seeds entry to the help reference (help/modes/full.md, per
bug-2954 argument-hint↔help parity) and registers the new list-seeds workflow
in docs/INVENTORY.md (88→89) and the generated INVENTORY-MANIFEST.json.

* docs(#441): add --list-seeds how-to + drop phantom statuses

Addresses CHANGES_REQUESTED on PR #722 (two documentation blockers):

- USER-GUIDE.md Seeds section (how-to): extend the task to cover
  auditing parked seeds on demand via --list-seeds, including the
  status filter — kept task-oriented per Diataxis how-to mode.
- CLI-TOOLS.md (reference): drop phantom statuses implemented|rejected
  from the list-seeds filter vocabulary; the system only produces
  dormant|active|triggered (src/audit.cts scanSeeds). Reference must
  be factually accurate and complete.

* fix(#441): guard non-scalar status frontmatter in cmdListSeeds

A seed with a bare `status:` line (extractFrontmatter yields {}) or a
`status: [a, b]` value (yields an array) crashed the whole audit list:
`(fm.status || 'dormant').toLowerCase()` throws a TypeError on a non-string.
Coerce every frontmatter read through a `fmStr` helper (mirrors the existing
`typeof fm.id === 'string'` guard), so a non-scalar status falls back to
dormant and non-scalar scope/trigger_when/title can no longer leak a raw
array/object into the JSON contract. Title is now capped symmetrically.

Adds regression coverage for empty and array `status:` and non-scalar fields.

Refs #441

* docs(#441): align list-seeds workflow status vocabulary

The load_seeds step listed `implemented` as an example status filter, but the
real seed vocabulary is dormant|active|triggered (src/audit.cts scanSeeds);
`implemented` has no producer. Matches the earlier CLI-TOOLS.md correction.

Refs #441

* refactor(#441): extract pure deriveSeedIdentity; match raw status in list-seeds

Pull the seed_id/slug derivation out of cmdListSeeds into a pure, exported
deriveSeedIdentity(stem, rawFmId) so the parsing contract can be property-tested
in-process (review minor #1). No behavior change.

Filter comparison now matches the raw lowercased status (both sides already
normalized) instead of sanitizeForDisplay(status); sanitization is for output,
not matching (review nit #3).

* test(#441): add fast-check property coverage and count=1 boundary for list-seeds

Adds tests/list-seeds.property.test.cjs with four fast-check properties over
deriveSeedIdentity (never-throws, string-only contract, canonical id->seed_id/slug
invariant, filename-prefix fallback) per RULESET.TESTS.property-based-testing
(review minor #1).

Adds an N==1 status-filter boundary case to list-seeds.test.cjs (review minor #2).

* chore(#441): sync runtime launcher snippet into list-seeds workflow

Propagate the current _runtime-launcher.snippet.sh (with non-Claude
runtime home probes) into the new list-seeds.md workflow via
scripts/sync-runtime-launcher.cjs, satisfying bug-891 (E) propagation.

* test(#441): record list-seeds.md in workflow size baseline (#1074)

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-22 00:59:41 -04:00
Tom Boucher
405ae9b3b7 refactor(#1557): add runtime artifact install plan module (#1560) 2026-06-21 20:40:13 -04:00
Tom Boucher
b63a500fad fix(#1505): extract context_guard step to reference file; fix allow-test-rule see ref
- Extract execute-phase.md context_guard step prose to
  gsd-core/references/execute-phase-context-guard.md (@-ref lazy load),
  bringing execute-phase.md back under the ADR-857 phase-6 size ceiling
  (92914 < 93166 bytes)
- Fix allow-test-rule comment: add `see #1452` per ADR-456 lint rule
- Update feat-1452 tests to check the reference file for extracted content
- Register execute-phase-context-guard.md in INVENTORY-MANIFEST.json and
  INVENTORY.md Workflow References section
- Regenerate workflow-size-baseline.json after file shrinkage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 18:38:20 -04:00
Tom Boucher
3a3b2135c2 chore(#1073): purge phantom pre-migration issue refs from source, tests, docs (#1471)
#2551/#3182/#2361 are pre-migration get-shit-done-redux issue numbers with no
equivalent in open-gsd/gsd-core; they mislead triage and manufacture phantom
blockers. Repoint to real successors (#717 byte-budget rework, #720) or rewrite
as prose referencing the discuss-phase/modes progressive-disclosure split.
Correct co-located 'line budget'/'<500 lines' framing to the byte-based reality
(#717). Add a CI guard (tests/no-phantom-issue-refs.test.cjs) that fails if a
phantom ref is reintroduced. SSH-key patterns (id_ed25519) left untouched.
No user-facing runtime behavior change.

Closes #1073
2026-06-20 13:36:57 -04:00
Tom Boucher
e7855bc217 fix(#1459): user-owned consent store gates third-party capability activation; env/cwd in disclosure; loader validator parity (#1473) 2026-06-20 01:59:03 -04:00
Tom Boucher
0d56f544d2 feat(#1435): capability matrix (generated + drift-guarded) + trust-model doc consolidation (#1458)
* feat(#1435): capability matrix (generated + drift-guarded) + trust-model doc consolidation

ADR-1244 Phase 6. Adds the capability matrix reference, generated FROM the committed registry so it
can never drift from the actual capability set:
- scripts/gen-capability-matrix.cjs (--write / --check); --check is a CI drift guard.
- tests/capability-matrix-sync.test.cjs (4 tests): drift guard, buildMatrix==committed, every cap
  present, no placeholders.
- docs/reference/capability-matrix.md regenerated from the registry (release-stable: shows engines.gsd,
  omits the lockstep per-cap version that would churn the file every release).
- Consolidated the duplicate trust-model doc: deleted docs/explanation/the-capability-trust-model.md,
  merged its content into capability-trust-model.md, redirected ~10 references; no stale links remain.
- Diataxis verification (now that gsd capability is a real command): corrected the matrix's third-party
  section — the matrix is the first-party catalogue; the overlay-aware view of installed third-party
  capabilities is 'gsd capability list', not this generated file.

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

* docs(#1435): Added changeset for the capability matrix reference

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

* fix(#1435): address code-review — non-vacuous matrix test + generator polish

- capability-matrix-sync.test.cjs: assert the 'security registers a ship:pre gate' precondition
  unconditionally so the extension-point check can never degrade to a vacuous pass on registry drift.
- gen-capability-matrix.cjs: warn (stderr) on an unknown loop point at generation time; rename
  enginesOf -> fmtEngines for consistency with the other fmt* helpers (output unchanged).
- capability-trust-model.md: point the two how-to links at the real files
  (import-a-capability-from-a-url.md, version-a-capability.md) instead of the bare directory.

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

* chore(#1435): backfill changeset PR number → #1458

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:45:26 -04:00
Tom Boucher
9219af3360 feat(#1433): capability trust gate + upgrade/compat (ADR-1244 Phase 4) (#1449)
ADR-1244 Phase 4 (D5 trust + D6 upgrade/compat). capability-trust.cjs (disclosure/consent, strict_known_registries, engines+compatVersions, reserved namespace) + capability-lifecycle.cjs (install/upgrade/remove/reconcile; ledger-as-commit-point _pending intent; atomic stage-then-swap; surgical marker-isolated shared-edit strip; owner-token lock) + capability-source promote/skipEnginesGate seams + loader pending-skip + config keys. No sandbox re-derived (consent+integrity+reversibility). 6 Codex adversarial rounds + /security-review (no HIGH) + /code-review; gsd-test green both platforms; CI green. Phase 5 (#1434) wires the CLI dispatch.

Closes #1433.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:40:37 -04:00
Tom Boucher
1abb0d3427 feat(#1432): capability source resolver + ledger (ADR-1244 Phase 3) (#1443)
* feat(#1432): capability source resolver + ledger (ADR-1244 Phase 3)

ADR-1244 D3 + D4 — additive, testable-in-isolation modules (the /gsd:capability
install command + consent gate are Phase 4). NEW modules only; not yet wired into
bin/install.js.

- src/capability-source.cts → resolveCapabilitySource(spec, opts): one seam, one
  adapter per source kind — local (fs copy), git (execGit clone+checkout, https/ssh/
  git transports only), npm (execNpm pack --ignore-scripts + tar, NEVER npm install),
  tarball (https download + sha512 integrity-before-extraction + tar), registry (stub).
  SECURITY: install never executes capability code (copy/extract only, --ignore-scripts);
  integrity verified before staging; symlink members rejected at interior, source-root,
  AND tar-member (verbose-listing) layers; tar-slip member paths rejected pre-extraction;
  npm specs with shell metacharacters (incl %) rejected (execNpm uses a Windows shell);
  git ext::/file:// transports + leading-dash/metachar refs rejected; atomic staging
  (.staging→rename, restore-on-failure); full Phase 1/2 validator suite + engines.gsd
  pre-check on the fetched manifest. Test seam _setCapabilitySourceHttpGet.
- src/capability-ledger.cts → per-runtime .gsd-capabilities.json install manifest:
  readLedger/writeLedger (atomic via platformWriteSync)/recordInstall (idempotent,
  prototype-pollution-guarded)/removeEntry/reconcile (orphan report, hardened against
  hostile/non-string/'..' files[] members — never throws, never oracles outside runtimeDir).
- 48 new tests (34 source incl. the full security matrix, 14 ledger). Two Codex
  adversarial rounds; all 12 findings fixed + regression-tested.
- .gitignore + eslint.config.mjs: built capability-source/ledger.cjs git+eslint-ignored
  (ADR-457 #551 migration coverage); CONTEXT glossary + INVENTORY rows + manifest regen.

Closes #1432

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

* docs(#1432): add changeset for capability source resolver + ledger

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:23:20 -04:00
Tom Boucher
353f63d170 feat(#1431): runtime capability registry overlay (ADR-1244 Phase 2) (#1440)
* feat(#1431): runtime capability registry overlay (ADR-1244 Phase 2)

Promote the registry from a frozen data file to loadRegistry({includeInstalled}),
composing the first-party registry with a validated installed overlay (ADR-1244 D2):

- Extract the conformance validator to a shared runtime-callable module
  (gsd-core/bin/lib/capability-validator.cjs); the generator re-exports it
  verbatim, guarded by a generative-parity test (no build-time/runtime drift).
- capability-loader.cts: loadRegistry({includeInstalled}) composes first-party
  ∪ validated overlay from $GSD_HOME/.gsd/capabilities (global) and
  <root>/.gsd/capabilities (project) via the canonical buildRegistry. First-party
  always wins (id/skill/agent/config/command-family + reserved gsd-/anthropic-
  prefixes); full merged-set cross-capability validation; engines.gsd load-time
  re-gate (skip-with-warning); gate-kind capabilities FAIL CLOSED; fragment-path
  escapes rejected.
- semverSatisfies (hand-written, no dep) for the engines.gsd gate, fail-closed.
- Wire surface/state + loop to the overlay; loop injects a blocking gate for each
  skipped gate-kind overlay (fail-closed).
- cwd-aware overlay config-key federation: config-loader _federatedConfigSchema(cwd)
  + config-schema isValidConfigKey(key, cwd) compose the overlay per loadConfig/
  config-set call (never eager at module load, never wrong-cwd); first-party path
  unchanged with no cwd.
- run-tests.cjs sandboxes GSD_HOME (idempotent — nested spawns reuse it) for test
  hermeticity; capability-loader.cjs git+eslint-ignored (tsc artifact);
  capability-validator.cjs stays linted (#551 migration coverage).

Closes #1431

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

* docs(#1431): add changeset for runtime capability registry overlay

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

* test(#1431): kill config-schema cwd-aware federation mutants (Stryker ≥52)

The cwd-aware overlay config-key federation added to config-schema.cts
(_capabilityConfigSchema(cwd) + isCapabilityConfigKey/isValidConfigKey cwd
threading) introduced mutable surface uncovered by config-schema's mutation
test set, dropping its score to 39.58% (below the 52 break threshold). Add a
real-overlay-fixture describe block exercising every branch (cwd guard, overlay
loadRegistry, found-branch, first-party fallback, cwd threading); local Stryker
score 39.58% -> 77.08%.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:41:20 -04:00
Tom Boucher
7ca8011cd9 refactor(#1373): add canonical markdown-sectionizer seam (epic #1372 T0) (#1381)
* refactor(#1373): add markdown-sectionizer seam (ADR-1372 T0)

Establishes the canonical markdown-structure parsing seam per ADR-1372.
No existing parsers are modified; this is the foundational T0 tier only.

- docs/adr/1372-markdown-sectionizer-seam.md: Accepted ADR defining the
  seam interface, the tiered migration plan (T0-T7), and the prohibition
  enforcement approach (no-adhoc-markdown-parsing ESLint rule in T7).
- src/markdown-sectionizer.cts: Pure module, Node built-ins only.
  Exports: stripFencedCode (CommonMark-correct state machine ported from
  uat-predicate.cts _stripFencedBlocks, CRLF-safe, unterminatedFence
  signal), tokenizeHeadings (ATX headings outside fenced blocks),
  collectSections (line-by-line predicate-driven section collection),
  collectSection (single named section, levelBounded stop, optional
  stripFences), iterateBullets (dash/checkbox/numbered + continuation).
- tests/markdown-sectionizer.test.cjs: 54-test behavioral suite covering
  the parser QA matrix (LF/CRLF, Unicode headings, headings-inside-fences,
  unterminated fences, nested levels, all bullet markers, continuation
  lines, empty/non-string input) plus 4 fast-check property tests
  (idempotence, output shape, never-throws, length monotonicity).
- CONTEXT.md: Markdown Sectionizer glossary entry added (PR review gate).

Tests: 54 pass, 0 fail. Existing adr-parser + uat-passed tests: 22 pass.

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

* refactor(#1373): add extractTaggedBlocks + replaceSection to seam; register inventory

- src/markdown-sectionizer.cts: extend Section type with bodyStart/bodyEnd offsets;
  add extractTaggedBlocks(content, tagName) (inner text of <tag>…</tag> blocks,
  tagName regex-escaped, caller decides fence-stripping) and replaceSection(content,
  section, newBody) (pure character-offset splice for read-modify-write callers);
  update collectSections/collectSection to populate bodyStart/bodyEnd.
- tests/markdown-sectionizer.test.cjs: add 33 new behavioral tests for
  extractTaggedBlocks, replaceSection, and a DEFECT.GENERATIVE-FIX parity guard
  that asserts stripFencedCode and uat-predicate's _stripFencedBlocks agree on a
  shared 9-item corpus; documents the known 4-space-indent divergence.
- docs/adr/1372-markdown-sectionizer-seam.md: list extractTaggedBlocks and
  replaceSection in §"The seam".
- CONTEXT.md: update ### Markdown Sectionizer glossary entry with the two new exports.
- docs/INVENTORY.md: add markdown-sectionizer.cjs row (alphabetically between
  loop-resolver and milestone).
- docs/INVENTORY-MANIFEST.json: regenerated via gen-inventory-manifest --write.

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

* fix(#1373): clear no-unsafe-assignment + unused-var lint in markdown-sectionizer

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

* fix(#1373): correct section offset/round-trip + CommonMark heading/fence edges; register eslint coverage

FIX 1 (CRITICAL): Enforce content.slice(bodyStart,bodyEnd) === body invariant in both
collectSection and collectSections. bodyEnd is now bodyStart + body.length instead of
the raw stop-line offset, eliminating the trailing-newline overcounting that caused
replaceSection to drop separator newlines (## A\nbody## B gluing bug).

FIX 2 (MED): tokenizeHeadings now accepts ≤3-space indent (CommonMark §4.5) and empty
ATX headings (## / ##   ), text=''. 4-space indent correctly excluded.

FIX 3 (MED): collectSection gains stopAtLevel option — stops at the next heading whose
level ≤ stopAtLevel, independent of the opener's level. Enables state.cts ## sections
that also stop at ### without abusing levelBounded.

FIX 4 (MED): Backtick fence opener info string must not contain a backtick (CommonMark).
Applied in both stripFencedCode and tokenizeHeadings fence state machines. Tilde fences
unaffected.

FIX 5 (LOW): "byte offset" → "character (string-index) offset" in HeadingToken / Section
doc comments.

FIX 6 (LOW): extractTaggedBlocks doc comment documents nested-tag non-support; test locks
the non-greedy close-at-first-</tag> behavior.

FIX 7: Add gsd-core/bin/lib/markdown-sectionizer.cjs to eslint.config.mjs ignores so
tests/551-eslint-bin-lib-coverage.test.cjs passes (3/3).

Tests: 107 pass / 0 fail (was 87; +20 new tests for FIX 1–4, 6).

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

* fix(#1373): gitignore tsc-built markdown-sectionizer.cjs (ADR-457 build-at-publish)

The seam's compiled artifact must be a build-at-publish output like every other
src/*.cts->bin/lib/*.cjs module (decisions, core, state, ...), not a committed
file. Add it to the ADR-457 ignore list and untrack it; build:lib/CI regenerate it.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:14:17 -04:00
Dave
8f428bdd41 feat(1259-01): deterministic prohibition-enforcement producer + check route
- Author src/prohibition-enforcement.cts: the test-tier prohibition PRODUCER/gate (ADR-550 D5d heavy half)
  locate wired check (node-test|lint-rule) -> confirm fail-first -> run -> build enforcementEvidence -> dispositionForProhibition
  pure/deterministic with injectable runCheck; missing/failing/non-fail-first -> hard-gate (both modes); passing -> green
- Route check prohibition-enforcement in src/check-command-router.cts (same family as ui-plan-gate / tdd-review-checkpoint)
- Add tests/prohibition-enforcement.test.cjs (behavioral, typed-field, injected runner) — new module within <=2 budget
- Register the built bin/lib surface: docs/INVENTORY.md row + regenerated docs/INVENTORY-MANIFEST.json
- .gitignore: add the emitted gsd-core/bin/lib/prohibition-enforcement.cjs (build artifact, ADR-457)
- No src/probe-core.cts edit — the green/fail-closed policy seam already exists
2026-06-15 12:44:29 -04:00
Rezolv
395fb519e7 feat(spec-phase): prohibition probe — surface "must-NOT" constraints (#644) (#1149)
Adds the spec-time prohibition probe (spec-phase Step 5.6) — the second adapter of the probe-core resolution model. Surfaces unwritten must-NOT constraints as negative SPEC acceptance criteria with test/judgment verification tiers; fail-closed at verify time. Per ADR-550. Closes #644.
2026-06-14 21:29:11 -04:00
Tom Boucher
bf634b95c3 feat(#1213): Capability State Writer — write-side inverse of the resolver (#1225)
* feat(#1213): Capability State Writer — write-side inverse of the resolver

Adds src/capability-writer.cts (setCapabilityState + cmdCapabilitySet) and the
`gsd-tools capability set` subcommand: the write-side inverse of the capability
resolver (ADR-1213). One desired capability state projects onto the substrates —
`enabled` drives the runtime surface (canonical on/off), `gates` drive federated
config keys (hook granularity), install profile is a read-only floor — then
re-resolves and reports divergence (assert-and-report), so "off means off" holds
as a write-time invariant. Adds batched setConfigValues; routes gsd:settings
capability hook-gates through the writer. Docs: CLI-TOOLS reference, how-to,
ADR-1213, CONTEXT.md term.

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

* chore(#1213): add changeset for Capability State Writer (#1225)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 12:51:17 -04:00
Tom Boucher
9e5d4b266b fix(#997): ensure canonical ~/.claude/gsd-core path for plugin installs (#1207)
* fix(#997): ensure canonical ~/.claude/gsd-core path for plugin installs via SessionStart hook

Claude Code marketplace plugin installs unpack the package into the
version-pinned plugin cache and never run bin/install.js, so
~/.claude/gsd-core/ is never created. Agents, commands, and templates
markdown-@-include the canonical ~/.claude/gsd-core/... path (which
expands ~ but NOT ${CLAUDE_PLUGIN_ROOT}), so every include resolved to
nothing and agents (e.g. the executor) failed.

Add a SessionStart hook (hooks/gsd-ensure-canonical-path.js) that, on a
plugin install, symlinks the canonical path's immutable subdirs (bin,
contexts, references, templates, workflows) to the plugin's bundled
gsd-core/ tree. It changes zero @-references, is a no-op in classic
installs, preserves user-generated files (USER-PROFILE.md, STATE.md),
prunes stale links so it self-heals after `claude plugin update`, uses
Windows junctions, and rejects bundled/canonical paths that escape the
resolved plugin root (no traversal, no clobber).

Registered in HOOKS_TO_COPY (build-hooks), MANAGED_HOOKS, hooks.json
SessionStart (runs first, timeout 5), and BUNDLED_GSD_HOOK_FILES.
Behavioral regression tests folded into issue-766-plugin-manifest.test.cjs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(#997): backfill changeset PR number to #1207

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:37:41 -04:00
Tom Boucher
e9f9ae49c8 fix(#1146): single base-branch resolver across forking workflows (#1198)
* fix(#1146): single base-branch resolver across forking workflows

Replaces duplicated per-workflow bash detection that silently fell through
to :-main on repos where origin/HEAD is unset (git init+remote add+fetch
without set-head, most CI checkouts, many worktrees).

New CJS module git-base-branch.cjs exposes `gsd_run query git.base-branch`
with full precedence ladder: git.base_branch config override → origin/HEAD
symref → git remote show origin (authoritative) → local branch presence →
"main". All git subprocesses bounded with timeouts; degrades gracefully.

Wires execute-phase, quick, ship, complete-milestone, and pr-branch to the
single resolver. Removes 14 lines of duplicated detection bash across the
five workflows.

Includes 7 behavioral tests covering the full precedence ladder including the
key regression case (master repo, origin/HEAD unset → must return "master",
NOT "main") and an anti-regression guard that fails if any workflow
re-introduces the :-main/:-master fallback pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(changeset): backfill PR number #1198

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(#1146): drop stray PR-body file from branch

pr-1146-body.md was committed during changeset backfill but must not
be tracked in the repo. Content preserved externally for PR body use.

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

* test(#1146): add tests for flat base_branch config key and both-branch tie-break

Closes two mutation gaps identified in adversarial review:
- A2: flat {base_branch: ...} at config root (legacy key form) was covered
  by code but unguarded against mutation of lines 74-75 in resolver
- H: tier-4 tie-break when both main+master exist locally (main wins,
  per tryLocalBranch JSDoc) was documented but untested

9/9 tests pass.

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

* test(#1146): allowlist workflow-literal guard as runtime-contract exemption

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#1146): degrade gracefully when gsd_run unavailable in handle_branching bash blocks

handle_branching (execute-phase.md) and step 2.5 (quick.md) are extracted
and run verbatim by behavioral tests that lack the gsd_run preamble.
Adding a || fallback ladder (git symbolic-ref then echo main) keeps the
unified resolver as primary in real workflows while letting the test harness
succeed without gsd_run defined.

Also propagates updated runtime-launcher preamble to pr-branch.md (added in
origin/next MemPalace PR) and regenerates workflow-size-baseline.json.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:22:40 -04:00
Tom Boucher
a375c4b354 feat(#956): add MemPalace memory capability (ADR-857 feature plug-in) (#1201)
* feat(#956): add MemPalace memory capability (ADR-857 feature plug-in)

Adds an opt-in, default-resilient ADR-857 feature capability that wires
MemPalace (local-first memory: MCP server + CLI) into the GSD loop:
deliberate recall before discuss/plan and verbatim + temporal-KG capture
at phase boundaries. Three memory modes (augment default; kg_backend and
replace forward-declared). Master gate mempalace.enabled (default off);
every hook onError:skip, zero gates; absent/disabled MemPalace => loop
unchanged. Transport is rendered-markdown only — MemPalace runs
out-of-process, no third-party code in gsd-core (ADR-857 §7).

Capability: capabilities/mempalace/ (manifest + 2 fragments), skills
commands/gsd/mempalace-{recall,capture}.md, agent
agents/gsd-mempalace-curator.md. Registration: ns-context router,
utility cluster, KNOWN_SKILLS, help full.md, model-catalog, copilot
install list, size baselines; regenerated capability-registry +
inventory manifest. ship:post wired into ship.md (wire-on-demand).

HELD on #1196: this capability also declares hooks at discuss:pre and
discuss:post, which are structurally un-wireable until the host-loop
conformance model covers the discuss phase (discuss-phase.md is not in
HOST_LOOP_FILES). The phase6-capstone-conformance gate therefore fails
on exactly those two orphaned points by design — see #1196. Once #1196
lands, rebase onto next and the gate goes green with no further change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(#956): backfill changeset PR number (#1201)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 02:09:10 -04:00
Tom Boucher
b1e8a74708 fix(#1196): wire discuss loop step for capability hooks (#1199)
* fix(#1196): wire discuss loop step for capability hooks

discuss was contract-declared (gsd:loop-host marker, in POINT_ORDER and
LOOP_HOST_CONTRACT) but structurally unwireable: discuss-phase.md had no
`loop render-hooks` dispatch and was absent from the conformance gate's
HOST_LOOP_FILES, so capabilities could never wire discuss:pre/discuss:post.

- discuss-phase.md: add minimal discuss:pre (before analyze_phase) and
  discuss:post (after write_context) render-hooks dispatch steps that
  delegate consumption to a new shared reference (kept under the 32KB
  #2551 budget; no inline subagent dispatch token).
- references/loop-hook-dispatch.md: new canonical, point-agnostic contract
  for consuming `loop render-hooks --raw` activeHooks (contribution/step/
  gate) — single source for hook consumption across host loops.
- gen-loop-host-contract.cjs: derive HOST_LOOP_FILES from STEP_WORKFLOWS and
  export scanWiredPoints()/getWiredLoopPoints() (throws on a missing host
  file) — one source of truth for the host-loop file + wired-point set.
- phase6-capstone-conformance.test.cjs: consume the derived HOST_LOOP_FILES
  and shared scanWiredPoints (was a hand-maintained duplicate omitting
  discuss-phase.md + a duplicated regex).
- gen-capability-registry.cjs: add validateHooksWired() gen-time guard that
  rejects a capability hook declared at a valid-but-unwired loop point, with
  a clear remediation message — failure now surfaces at gen --check/--write
  time instead of deep in the full conformance suite.
- tests (capability-registry.test.cjs): regression + anti-pattern parity
  guards (every loop-host marker is in STEP_WORKFLOWS/HOST_LOOP_FILES;
  POINT_ORDER === flattened LOOP_HOST_CONTRACT) so no step can drift into
  the discuss-class gap again.
- docs/INVENTORY*: register the new reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(#1196): backfill changeset PR number (#1199)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 01:39:54 -04:00
Tom Boucher
ae8bb707bc refactor(#1170): remove hand-maintained INVENTORY count scalars (#1179)
* refactor(#1170): remove hand-maintained INVENTORY count scalars

The `(N shipped)` heading counts in docs/INVENTORY.md were absolute
scalars that collided silently on merge: two branches each bumping the
same integer to N+1 produced a clean git merge whose value the merged
filesystem (N+2) contradicted, hard-failing inventory-counts.test.cjs on
the CI merge commit across all platforms (DEFECT.INVENTORY-MERGE-UNDERCOUNT).

- Strip the six `(N shipped)` heading counts + the two prose footnote
  counts; repoint the intro to INVENTORY-MANIFEST.json as the registry.
- Drop the decorative `generated` date from the manifest + its
  strip-before-compare branch in gen-inventory-manifest.cjs (it conflicted
  on cross-day merges and is read by nothing).
- Delete inventory-counts.test.cjs (scalar-vs-disk gate, the collision
  source); its drift protection is subsumed by the merge-safe set-membership
  test inventory-manifest-sync.test.cjs, which stays as the sole gate.
- Add inventory-headings-countfree.test.cjs guard (fails if a count is
  re-added to a heading).
- Fix already-broken count-bearing cross-doc anchors to stable count-free
  slugs in ARCHITECTURE.md + multi-agent-orchestration.md.
- Retire the now-impossible DEFECT.INVENTORY-MERGE-UNDERCOUNT + obsolete
  RULESET.DOC-CONSISTENCY in CONTEXT.md; de-count DEFECT.INVENTORY-DRIFT;
  correct stale MANIFEST-CANONICAL-KEY (all six families canonical).

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

* chore(#1170): backfill changeset PR number (#1179)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 21:30:03 -04:00