Commit Graph

125 Commits

Author SHA1 Message Date
Tom Boucher
4f779eda43 test(#1970): consolidate 61 install-suite regression tests into function suites
Fold 61 issue-named install/codex/runtime regression files into the canonical
test file that owns each subject-under-test, preserving every assertion and its
origin issue number as provenance (block-scoped describe wrappers, zero assertion
loss — 652 subtests conserved 1:1). Routes:

- codex-config.test.cjs        +19 (codex config/toml/hooks/adapter/skill surface)
- install.test.cjs             +18 (node-runner norm, manifest, arg parse, finishInstall)
- install-runtime-artifacts    +13 (per-runtime conversion + emission)
- install-minimal-hooks        +6  (hook-event dialects + guards)
- path-replacement             +2  (opencode absolute pathPrefix)
- install-write-confinement    +2  (pristine dir writes)
- install-regressions          +1  (user-artifact preservation)

Removes 61 tests/ files → 61 fewer CI processes. Regenerates the regression-name
allowlist (271→222), ratchets the file-count allowlist (config 10→9, install 12→9),
and makes 13 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456;
prunes 13 stale allowlist ids). Repoints ADR-0009's moved-test list. lint:ci green.

Part of epic #1969. Closes #1970.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 00:39:32 -04:00
Tom Boucher
b51cbf96cf feat(#1943): extensionEvents vocabulary — separate from hookEvents (extension-system surface) (#1946)
* feat(#1943): extensionEvents vocabulary — separate from hookEvents (extension-system surface)

* fix(#1943): re-export VALID_EXTENSION_EVENTS from gen-capability-registry (test import path)

* fix(#1943): regenerate capability-registry.cjs + add changeset fragment

* fix(#1943): import VALID_EXTENSION_EVENTS from validator, not gen-capability-registry (golden parity)
2026-07-02 21:45:35 -04:00
Tom Boucher
69fef7c00e docs(#1683): Diátaxis host-integration docs + versioning policy + ADR-1239 Accepted — Slice 3 (#1940)
- how-to: author a host-plugin (external-author guide against the SDK surface)
- tutorial: embed GSD in a new host (end-to-end programmatic-cli example)
- reference: the Host-Integration Interface (axes, adapters, handshake, profiles)
- explanation: interface versioning + deprecation policy (additive vs breaking,
  PROTOCOL_VERSION bumps, deprecation window)
- ADR-1239 Status: Proposed → Accepted (Phases B–E shipped)
2026-07-02 18:38:11 -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
337eee59d5 docs(#1610): ADR for the workflow/agent size-budget ratchet (#1713)
* docs(#1610): ADR for the workflow/agent size-budget ratchet

Gives the already-shipped size-governance decision (epic #1074; PRs #1089/#1096/#1097) its first ADR: per-file LF-normalized byte baseline (anti-creep across every workflow/agent .md) + loose tier hard caps (XL/LARGE/DEFAULT), measured in bytes not lines, with an explicit do-not-game-the-proxy clause (lazy extraction only). Distinct from the install-time skill-surface budget of ADR-0010/0011.

Verified against tests/{workflow,agent}-size-budget.test.cjs + scripts/workflow-size.cjs: cap constants XL_CAP=98304/LARGE_CAP=61440/DEFAULT_CAP=40960/NEW_FILE_CAP=32768, and the largest XL orchestrator is plan-phase.md (~93,973B) ahead of execute-phase.md (~93,426B) — corrected from the draft.

Closes #1610.

* docs(#1610): de-rot tier-cap byte figures — cite baseline JSON not a drifting snapshot

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-30 10:39:44 -04:00
Tom Boucher
dfff25f1bd docs(#1817): add adr-1817 state.md rebuild derivability contract (#1828)
* chore(context): scrub nul byte from consent-store predicate

CONTEXT.md line 206 (Capability Consent Store predicate) contained a
literal NUL byte between ${realpath(projectRoot)} and <id> documenting
the disk-key join format. The byte was intentional but made the file
binary-detected, breaking grep/rg searches (hit while preparing ADR-1817).

Replace with the 4-char \x00 escape. Preserves the byte-level disk-key
documentation; surrounding prose 'prototype-pollution-safe NUL-joined
keys' carries the semantic context. file(1) now reports 'Unicode text'.

* docs(#1817): add adr-1817 state.md rebuild derivability contract

Phase 0 of approved feature #1817 (epic). Lands the design contract for
the new `rebuild` transition in the STATE.md Transition Module (ADR-1769):

- ADR-1817 (new): `rebuild` is the capstone 11th transition. Six design
  decisions: (1) core substrate, non-toggleable, same tier as the other
  10; (2) section taxonomy — re-derivable (`## Current Position` prose
  from frontmatter, `## By-Phase Progress` table from disk) vs preserved
  (`## Session`, `## Decisions`, unknown sections) vs de-duplicated
  (`## Session Continuity Archive`); (3) orphaned data is logged + dropped
  with a structured audit entry in `## Rebuild Log` (ADR-1411 provenance
  principle); (4) idempotency is a hard guarantee — a no-mutation rebuild
  appends no log entry; (5) non-overlapping scope with `sync` (3
  frontmatter fields, auto-triggered); (6) orthogonal to
  `auto_prune_state` (rebuild reconciles with current canonical sources,
  prune removes by retention policy).

- CONTEXT.md (STATE.md Transition Module section): list `rebuild` as the
  11th intent; add contract predicates mirroring the ADR.

- docs/adr/README.md: add ADR-1817 to the index (Accepted).

Targets the #1776/#1761/#1591 body-drift cluster that survived ADR-1769's
per-field transitions. Phased per ADR-1817: this PR (Phase 0) closes
#1817; Phase 1 (#1827) lands `rebuildCore` + intent dispatch + drift-class
unit tests; Phase 2 (#1826) lands `cmdStateRebuild` CLI + dry-run +
integration tests + docs + changeset.

* docs(#1817): reword state-doctor alternative to satisfy docs-parity lint

The docs-parity-live-registry test scans every docs/*.md (including
docs/adr/) for slash-command tokens and asserts each one resolves to a
live command in the registry. The rejected-alternative #4 in ADR-1817
mentioned a hypothetical `/gsd:state-doctor` workflow, which tripped
the lint (`unknown command token(s): [/gsd:state-doctor]`).

Reword to 'standalone state-doctor workflow' (no slash prefix). The
extractor is aggressive — backticks and space-preceding tokens are both
extracted per the test's own polarity-invariant cases — so the only
sound fix is to not form a slash token at all for hypothetical names.

Verified locally: `node --test tests/docs-parity-live-registry.test.cjs`
now passes 31/31 (was 30/1).
2026-06-29 15:27:22 -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
21f0b4316f refactor(#1796): ADR-1769 Path A — finish STATE.md preservation consolidation (#1799)
Extract readModifyWriteStateMd's post-sync preservation block into a pure,
field-classification-table-driven applyStatePreservation in the STATE.md
Transition Module. progress / status / stopped_at now join current_phase_name
as table-governed (getFieldClassification), so a preservation-policy change is
a one-row table edit instead of a per-call-site patch.

This realizes the consolidation ADR-1769 / CONTEXT.md already claimed shipped
('Absorbs readModifyWriteStateMd post-sync preservation block') and routes the
#1264 preservation policy through the single field-classification table — the
bug class is now structurally guarded by the table, not just the call-site
shouldResync flag.

Behavior is byte-identical to the pre-amendment inline block (Hyrum-safe — the
15 readModifyWriteStateMd callers' observable preservation is unchanged):
- state/frontmatter/transition + bug regression suite: 847 pass
- phase/milestone/verify (other RMW consumers): 582 pass
- codex (gpt-5.5/high) adversarial review: CLEAN (58,564-case equiv sweep)

ADR-1769 amendment appended documenting #1796.

Closes #1796
2026-06-27 22:49:52 -04:00
Tom Boucher
79c69d443b refactor(#1793): ADR-1769 Phase 7 — sync, prune, update migrations (#1760, #1761) (#1794)
Migrate cmdStateSync, cmdStatePrune, cmdStateUpdate (state.cts) onto the
STATE.md Transition Module substrate and close the maintenance bug pair
#1760/#1761 (ADR-1769, epic #1769). Completes the substrate: all 10
lifecycle/maintenance transitions now route through transitionCore.

- Add {kind: 'sync'|'prune'|'update'} to StateTransitionIntent with syncCore,
  pruneCore, updateCore in src/state-transition.cts.
- updateCore: body-only single-field update (strip/reassemble), mirroring the
  pre-migration cmdStateUpdate contract.
- pruneCore: pure content→content section pruning (Decisions / Recently
  Completed / resolved Blockers / Performance Metrics rows at or below cutoff),
  byte-identical tokenizeHeadings splicing. Adapter owns currentPhase, dry-run,
  and STATE-ARCHIVE.md.
- syncCore: body writes (Total Plans in Phase, Progress bar, Last Activity)
  given disk-derived numbers. Adapter owns the disk scan + roadmap scope.
- #1760: cmdStatePrune now derives currentPhase from 'Current Phase' OR 'Phase'
  (the canonical template emits 'Phase: X of Y'), so prune engages on
  template-conformant STATE.md instead of bailing 'Only 0 phases'.
- #1761: cmdStateSync skips the Progress write (percent=null) when a milestone
  version is set in frontmatter but the ROADMAP has no versioned heading for it
  (milestone cannot be bounded). Projects without a milestone version are
  unaffected. Leaves progress untouched rather than silently writing fallback-
  derived wrong values.
- Regression: bug-1760 (prune engages on template field) + bug-1761 (sync
  leaves progress untouched when unbounded). ADR-1769 marked Accepted.

All 82 transition + 515 regression tests pass.

Closes #1793
2026-06-27 16:53:44 -04:00
Tom Boucher
064f63b299 refactor(#1791): ADR-1769 Phase 6 — patch migration + curated current_phase_name preserve (#1695) (#1792)
Migrate cmdStatePatch (state.cts) onto the STATE.md Transition Module substrate
and close the curated-field clobber bug class #1743/#1695 (ADR-1769, epic #1769).

- Add {kind: 'patch'} to StateTransitionIntent, with patchCore in
  src/state-transition.cts. Applies each caller-supplied {field:value} pair via
  stateReplaceField over the full content (body + frontmatter), tracking
  updated vs. failed. data.updated/data.failed mirror the CLI output shape.
- Collapse cmdStatePatch to a transitionCore dispatch. Field-name validation
  (security) and the resync-progress decision stay in the adapter.
- #1695/#1743 fix: extend the #1230 delta heuristic in readModifyWriteStateMd to
  the curated current_phase_name, table-driven via
  getFieldClassification('current_phase_name').preservation === 'preserve-always'.
  When a write does NOT change the body Phase: source line, the curated
  frontmatter value wins over syncStateFrontmatter's body re-derivation (which
  harvests a wrong parenthetical aside — #1695). begin/planned/complete-phase
  rewrite their body Phase line, so the delta does not fire for them and
  current_phase_name still advances.
- Regression: bug-1695-state-patch-clobbers-phase-name.test.cjs — unrelated
  patch preserves curated current_phase_name; patching the Phase source advances.

All 70 transition + 493 regression tests pass (state/phase/milestone + #905/#397/#3242 lineages).

Closes #1791
2026-06-27 16:21:28 -04:00
Tom Boucher
3ceb83329d refactor(#1789): ADR-1769 Phase 5 — milestoneComplete migration (#1790)
Migrate the STATE.md write path inside cmdMilestoneComplete (milestone.cts)
onto the STATE.md Transition Module substrate (ADR-1769, epic #1769).

- Add {kind: 'milestoneComplete'} to StateTransitionIntent, with
  milestoneCompleteCore in src/state-transition.cts (consulting the
  field-classification table). Owns the closure write: Status
  ('<version> milestone complete'), Last Activity, Last Activity Description,
  a Current Position reset to 'Awaiting next milestone', and an Operator Next
  Steps reset pointing at the next-milestone command.
- Collapse the inline STATE.md transform in cmdMilestoneComplete to a
  transitionCore dispatch. The adapter retains writeStateMd (lock + steady-state
  syncStateFrontmatter post-sync) and resolves the runtime-specific
  next-milestone slash command, injecting it via intent.nextMilestoneCommand.
- The two section resets carry their pre-seam allow-adhoc-markdown waivers
  (regex semantics pinned by existing tests; pending collectSection #1372).
- realClock.today() is byte-identical to milestone.cts's local today
  (both new Date().toISOString().split('T')[0]).
- Characterization tests pin field updates, both section resets (replace +
  insert paths), and frontmatter #1255 parity.

All 66 transition + milestone + state tests pass.

Closes #1789
2026-06-27 15:46:54 -04:00
Tom Boucher
91704c9fcf refactor(#1786): ADR-1769 Phase 4 — plannedPhase + milestoneSwitch migration (#1788)
Migrate cmdStatePlannedPhase and cmdStateMilestoneSwitch (state.cts) onto the
STATE.md Transition Module substrate (ADR-1769, epic #1769).

- Add {kind: 'plannedPhase'} and {kind: 'milestoneSwitch'} to
  StateTransitionIntent, with plannedPhaseCore and milestoneSwitchCore in
  src/state-transition.cts (consulting the field-classification table).
- plannedPhaseCore owns the template-aware Status/Last Activity updates, Total
  Plans in Phase, Last Activity Description, and the Current Position section
  (via the inlined mutateCurrentPositionForAdvance twin). The adapter keeps the
  resync:false readModifyWriteStateMd wrapper (#500 RC1).
- milestoneSwitchCore owns the new-milestone reset: rebuilt frontmatter
  (milestone/name/status='planning'/zeroed progress) + Current Position body
  reset. The adapter keeps acquireStateLock + platformWriteSync (NOT
  readModifyWriteStateMd — milestoneSwitch rebuilds frontmatter directly).
- Collapse both callbacks to transitionCore dispatches. The now-dead
  updateCurrentPositionFields helper and KNOWN_STATUS_PATTERNS import are
  removed (their behavior lives in the transition module's
  mutateCurrentPositionForAdvance).
- Characterization tests pin the template-aware preserve-authored invariant,
  Total Plans, Last Activity narrative, Current Position update, and the full
  milestone reset (frontmatter + position body + gsd_state_version preserve +
  Accumulated Context preserve).

All 58 transition + 177 state + phase + bug-2630/bug-905 regression tests pass.

Closes #1786
2026-06-27 15:24:18 -04:00
Tom Boucher
6f80524be1 refactor(#1784): ADR-1769 Phase 3 — completePhase migration onto Transition Module (#1785)
Migrate the STATE.md write path inside cmdPhaseComplete (phase.cts) onto the
STATE.md Transition Module substrate (ADR-1769, epic #1769).

- Add {kind: 'completePhase'} to StateTransitionIntent + completePhaseCore in
  src/state-transition.cts. Pure body field mutations (Current Phase shape/name,
  Status, Current Plan, Last Activity + Description, Completed/Total Phases +
  Progress percent), consulting the field-classification table for touched keys.
  Roadmap progress injected via a new optional deps.roadmapProvider.
- Collapse the ~90-line inline STATE.md transform in cmdPhaseComplete to a
  transitionCore dispatch. The adapter retains updatePerformanceMetricsSection
  (section table) + syncStateFrontmatter (disk-scan post-sync) and the
  multi-file atomic transaction (STATE is committed with ROADMAP/REQUIREMENTS,
  so readModifyWriteStateMd is not used here).
- deriveProgressFromRoadmap/clampPercent/stateReplaceFieldWithFallback move from
  the phase.cts call site into the pure core (no circular dep: phase-lifecycle
  and state-document don't import state.cts).
- Characterization tests in tests/state-transition.test.cjs pin the field
  updates, roadmap progress derivation, #1255 frontmatter parity, and the
  'Phase:' fallback. All 44 transition + 193 phase tests pass.

No user-visible behavior change. cmdPhaseComplete CLI output and 'phase complete'
behavior unchanged.

Closes #1784
2026-06-27 14:56:51 -04:00
Tom Boucher
4732c704f7 docs(#1769): ADR-1769 STATE.md Transition Module — Phase 0 (#1770)
Introduce ADR-1769 establishing the STATE.md Transition Module design:
intent-based transitions over scattered RMW callbacks. Seven design
decisions resolved via /improve-codebase-architecture + /grilling +
/domain-modeling:

  1. Module shape: Transition Module owns the full transaction
  2. Method set: 10 transitions (lifecycle + maintenance + milestoneComplete)
  3. I/O shape: pure core (content, intent, deps) -> newContent
  4. Policy model: field-classification table
  5. External writers: 2 migrate (milestone, phase), 1 stays (verify)
  6. Core scope: writes only; body sections as constants
  7. Migration: substrate first, transition-by-transition

Updates CONTEXT.md with the new Module entry; updates docs/adr/README.md
index. No behavior change. Implementation lands in Phases 1-7 per the
ADR's migration sequence.

Closes #1769

Co-authored-by: review-bot <review-bot@gsd>
2026-06-27 09:46:07 -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
4525bc6f4d fix(#1749): close epic #1702 audit gaps — drift-guard bin/install.js, ci-test-scope wiring, ADR divergence (#1751)
Post-merge coverage-audit follow-ups to epic #1702 (found by an independent
gpt-5.5/high audit + adr-phase-coverage cross-reference). None are CRITICAL —
the 9-rule enforcement shipped and works; these close completeness/integrity
gaps between ADR-1703's promises and the as-built reality.

1. drift-guard bin/install.js scope (ADR-1703 L114-119): the drift guard
   covered src/runtime-homes.cts only; the ADR named bin/install.js too. Phase
   6's glob expansion made bin/install.js a covered surface. Extended
   tests/portability-vocab-drift.test.cjs with TWO sound checks: (a) any
   bin/install.js top-level function that directly returns path.*() must be in
   PATH_RETURNING_FNS (tight, 0 FP — the body-contains heuristic is unsound
   here, ~33 FPs); (b) a curated two-way existence lock on the installer path
   helpers (catches a rename making a vocab entry stale; keeps the curation in
   sync with PATH_RETURNING_FNS). The residual new-resolver boundary (temp-var
   shape) is documented.

2. ci-test-scope wiring (the Phase 6 portability selection rule was
   ineffective): eslint-rules/ was not in the product-code prefix list, so an
   eslint-rules-only change set code_changed=false and CLEARED the matched
   tests (reproduced: targeted_tests=[]). Added eslint-rules/ to the prefix
   list and the P1-P4 RuleTester suites to the selection rule (it previously
   listed only P5/P6). Verified: code_changed=true, 11 tests selected.

3. ADR-1703 acceptance note amended to record the two further as-built
   divergences: the disable-ban shipped as an out-of-band test (not the
   specified local/no-portability-disable meta-rule — the test runs outside
   ESLint so it cannot be self-disabled, at least as strong); and the
   drift-guard bin/install.js scope resolution above.

Epic #1702 all eight phase boxes now checked. No runtime change; no-changelog
(contributor tooling + docs).

Closes #1749

Co-authored-by: review-bot <review-bot@gsd>
2026-06-26 08:12:40 -04:00
Tom Boucher
83685ea7a3 docs(#1744): portability architecture guide + ADR-1703 acceptance (Phase 7 closeout) (#1745)
ADR-1703 Phase 7 / epic #1702 closeout. The mechanical teardown is already
complete across phases 1-6 (regex scanner retired P3, ratchet deleted P4,
allowlist portability-usage gone, windows-portability-ok comments swept,
every DEFECT.WINDOWS-* predicate rewritten per-phase). This phase delivers the
two remaining ADR-mandated closeout items:

- docs/contributing/adding-a-portability-rule.md: the forward architecture
  recipe (Diátaxis Explanation) — the five seams (rule / portability-vocab /
  platform-guard / disable-ban / ci-test-scope), the zero-escape-hatch contract,
  the shipped-rule catalog, and the step-by-step checklist for adding a new
  local/* portability rule.

- docs/adr/1703: Status Proposed -> Accepted (all seven phases shipped); a
  Phase 7 as-built note recording the two deviations from the Phase 0 catalog —
  Phase 6's rename-only scope decision (#1740) and the codex-review precision
  tightening on require-fs-op-fallback.

- docs/contributing/cross-platform-portability-rules.md: cross-link to the new
  guide from the rule reference.

No code, no test, no runtime change. Epic #1702 Phase 6 box checked; Phase 7
box + epic closure to follow at PR merge.

Closes #1744

Co-authored-by: review-bot <review-bot@gsd>
2026-06-26 00:19:44 -04:00
Rezolv
b2a7a3980d docs(#1606): ADR for the prohibition-enforcement verify-time seam (#1712)
Promotes the verify-time enforcement decision that accreted as four chronological addenda on ADR-550 (#1259/#1279/#1346/#1278) into a single first-class architecture-of-record. ADR-550 keeps the spec-phase contract; this ADR owns the test-tier enforcement producer (locate -> machine-prove-fail-first -> run -> dispose) in src/prohibition-enforcement.cts.

Verified against live src/*.cts: the green AND-gate lives in runProhibitionEnforcement, the empty-file vacuity guard is isNonVacuousNodeTestPass (red-side is isNonVacuousNodeTestRed), and the ADR-857 pointer cites the real 'Verification substrate vs. plug-in tier' section / decision #6.

Closes #1606.
2026-06-25 20:13:00 -04:00
Rezolv
0bd4c82389 docs(#1607): record recall-side rejected alternatives in ADR-550 (#1714)
Appends an 'Alternatives considered (recall / representation / packaging side)' addendum to ADR-550: the two NEW spec-phase-side rejections (the withdrawn LLM requirement classifier #652, and a deterministic prohibition-recall engine) plus cross-references to the two already-decided ones (no polarity field on truths — D3; deferred single dispatcher CLI — D7e). Enforcement-side alternatives stay in ADR-1606.

Closes #1607.
2026-06-25 20:12:42 -04:00
Tom Boucher
c57e0d56c2 feat(#1720): no-unguarded-nonportable-exec AST rule; retire the regex script (Phase 3) (#1723)
Phase 3 of epic #1702. Closes #1720.
2026-06-25 16:25:48 -04:00
Tom Boucher
584dbd6a47 docs(#1703): ADR — cross-platform portability enforcement architecture (#1705)
Phase 0 of epic #1702. Closes #1703.
2026-06-25 13:12:34 -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
Tom Boucher
a0e45cd146 docs(#1507): mark ADR-1508 implementation-complete and close the conversion-module epic (#1677)
Append a dated amendment to ADR-1508 (append-only convention) recording that
the Runtime Artifact Conversion Module decision is implemented on next: the
content-rewrite engine + walkers + computePathPrefix live behind the deep seam
rewriteStagedSkillBodies/rewriteStagedCommandBodies, the getInstallExports relay
and GSD_TEST_MODE require are deleted, and CONTEXT.md marks the module SHIPPED.

Status stays Accepted (no Implemented status per docs/adr/README.md). Two
deferred follow-ups are tracked as sub-issues of the epic (neither a blocker):
- #1675 dedup convertClaudeToAugmentMarkdown family
- #1676 fast-check property test ($HOME-collapse + idempotency)

Delivery verified by a Codex (gpt-5.4, high) read-only review against the
epic's stated deliverables, cross-checked against the indexed code graph.

Closes #1507
2026-06-24 21:39:58 -04:00
Tom Boucher
cc2e33b098 docs(#1672): add ADR-1671 dynamic context management platform + reference example (#1674)
Phase 0 of the Dynamic Context Management epic (#1671): the ADR (documentation) plus a non-shipping reference example under examples/dynamic-context-management/ — the Option-E predicate fact-store parser/selector, a self-contained --check/--write/--select index generator, a sample index, and a runnable demo.

The example is intentionally excluded from the build (src/->bin/lib/), the npm package files[], the installer, and the CI test suite (tests/) — nothing here is compiled into or installed with GSD. Production implementation lands in a later phase.

Resolves #1672

Refs #1671

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:48:51 -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
bcc5a6d1ba fix(#1634): honor capability hook matcher and node-prefix command (#1638)
* fix(#1634): honor capability hook matcher and node-prefix command

Capability hook install (applyCapabilitySharedEdits) wrote each settings.json
hook entry with no `matcher`, so a tool-scoped hook fired on every tool (a
fail-closed guard could then block the whole session), and emitted a bare
single-quoted script path so a .js-family hook from a git/tarball source
without +x failed with Permission denied on every matching call.

- Pass through an optional declared `matcher` (entry-level sibling of `hooks`);
  absent => omitted (match-all), so existing shipped capabilities are unchanged.
- Validate `matcher` in the declaration (non-empty string, no control chars).
- Emit `node <quoted-path>` for .js/.cjs/.mjs hooks (mirrors first-party);
  .sh and others keep the bare quoted path (unchanged).

Root cause: the manifest hook schema (validator rule C4) was {event, script}
only with no matcher, and applyCapabilitySharedEdits never read or wrote one;
the command used shellSingleQuote(absScript) with no node prefix.

Regression tests fail-first on both defects (matcher dropped; bare path) and
pass after the fix; #1460 command assertions updated for the node prefix.

* chore(#1634): backfill changeset pr:1638

* fix(#1634): resolve lint and windows CI failures

- validator: replace the control-character range regex with a char-code loop.
  The literal /[\x00-\x1f\x7f]/ tripped ESLint's no-control-regex rule; char
  codes are equally precise and lint-clean. Behavior unchanged (still rejects
  matchers containing ASCII control characters incl. DEL).
- test: gate the executable-bit precondition on POSIX. Windows fs does not
  honor POSIX write modes (a 0o644 write reads back as 0o666), so the
  precondition is meaningless there and failed the windows-latest lane. The
  node-prefix assertion — the actual fix — is platform-independent and still
  runs everywhere.

* docs(#1634): amend ADR-894 for optional lifecycle hook matcher

The `role: "feature"` `hooks[]` entry now carries an optional `matcher`
(settings.json tool-scoping pattern: exact/pipe/wildcard/regex). Document
the field in the §2 schema table and record a Grilling-amendments entry:
the install path projects a declared matcher onto the emitted settings.json
hook entry (absent = match-all, so shipped capabilities are unchanged), and
per-runtime matcher projection (ADR-857 D8) stays a separate concern. This
amendment ships with the fix that introduced the field rather than as a
follow-up.

* docs(#1634): record WINDOWS-POSIX-MODE-BIT-ASSERT defect in CONTEXT.md

Capture the CI failure pattern from #1634/PR #1638 so it is not repeated: a
test that writes a file with a POSIX mode and then asserts statSync().mode
& 0o777 === <octal> passes on macOS/Linux but fails on windows-latest
(Windows fs does not honor POSIX write modes — reads back 0o666). Added as a
machine-greppable DEFECT predicate (symptom/examples/detect/fix-forward/
prevention) next to DEFECT.WINDOWS-TEST-PORTABILITY, with the fix-forward:
gate the mode-bit precondition on process.platform !== 'win32' and keep the
platform-independent behavioral assertion running everywhere.
2026-06-23 21:49:43 -04:00
Tom Boucher
61be100a02 docs(#1642): amend ADR-0174 §5 — reconcile Result type + add exitReason? (#1643)
Two changes to ADR-0174 §5 (Sync dispatch with tight-typed Result<T>):

1. Reconcile the documented Result type to the as-built code. The
   original §5 text planned 'Unknown' / 'BadArgs' / 'ValidationFailed'
   / 'NotImplemented' / 'HandlerFailed'; the SDK retirement migration
   kept the ADR-0012 names (UnknownCommand / InvalidArgs / HandlerFailure)
   and never added the planned ValidationFailed or NotImplemented
   variants. HandlerRefusal was added during implementation but never
   back-filled into this ADR. The ADR now documents what consumers
   actually depend on.

2. Add the optional exitReason?: string field on the InvalidArgs variant
   (and update the makeInvalidArgs factory signature). This carries an
   ERROR_REASON enum value separately from the existing reason
   explanation text, so capability routers migrating from direct
   error(msg, ERROR_REASON.USAGE) calls to makeInvalidArgs(...) Results
   preserve ERROR_REASON granularity through the Hub Result →
   error(msg, exitReason) translation. The field is additive and
   backward-compatible.

Also tightens the amendment requirement: 'Adding a new variant OR
adding a field to an existing variant requires amending this ADR.'

Phase 0 of parent #1641. No code changes; pure ADR amendment.
2026-06-23 21:42:16 -04:00
Tom Boucher
c28cccbf85 Merge pull request #1595 from davesienkowski/feat/1592-plan-drift-precheck
feat(#1592): add plan:pre codebase-drift pre-check before planner runs
2026-06-23 10:56:29 -04:00
Tom Boucher
1985d4a4de docs(#1603): add opencode host-plugin binding to adr-1239 2026-06-22 22:29:48 -04:00
Dave
d1f7ba82f2 feat(#1592): add plan:pre codebase-drift pre-check before planner runs
Add a non-blocking, warn-only codebase-drift gate at plan:pre so a stale
STRUCTURE.md is surfaced before /gsd:plan-phase spawns the planner, instead
of being discovered mid-execution by the existing execute:wave:post gate.
Gated on a dedicated workflow.plan_drift_precheck toggle (default true),
independent of schema_drift_gate. Never blocks planning, never spawns the
mapper agent at plan time.

Review feedback (#1595):
- Use a documented conventional-commit type (feat, not enhance) per
  CONTRIBUTING.md / gsd-validate-commit.sh.
- Normalize the plan_drift_precheck command references to the colon prose
  form (/gsd:plan-phase, /gsd:map-codebase) to match plan-phase.md §5.65;
  registry regenerated from capability.json.
- Make the test temp dirs hermetic: drain mkdtemp dirs in an after() hook
  via the helpers.cleanup() budget (local/no-raw-rmsync-in-tests-compliant).

Closes #1592

Claude-Session: https://claude.ai/code/session_016JBiXEAofvB3prJim29XMS
2026-06-22 20:30:59 -04:00
Tom Boucher
da4a86d8c1 feat(#1596): ship GSD skills via .claude-plugin/plugin.json
Phase B-provide of epic #1258. Adds a build-generated skills/ dir +
a skills manifest field so plugin-installed GSD exposes gsd-core:<skill>
the native Claude Code way. Closes the gap where plugin-only installs
lacked the skill surface because bin/install.js never ran.

- scripts/gen-plugin-skills.cjs: build step converting commands/gsd/*.md
  to skills/gsd-<stem>/SKILL.md via convertClaudeCommandToClaudeSkill
- .claude-plugin/plugin.json: add "skills": "./skills/"
- package.json: add skills to files, gen:plugin-skills to build chain
- tests/issue-766-plugin-manifest.test.cjs: Section H conformance
  (manifest field + dir + frontmatter + count parity) + C2 skills symlink
- docs/adr/766-*.md: dated amendment adding skills surface row
- .changeset/rapid-bears-hum.md: type Added
- skills/: 69 generated gsd-<stem>/SKILL.md files (build-committed)

Closes #1596
2026-06-22 18:07:35 -04:00
Tom Boucher
860179ee5d docs(#1593): ADR for cross-runtime skill mapping + converter methodology
Phase A of epic #1258. Adds the single authoritative ADR documenting
the per-runtime skill mapping + converter transform-contract catalog
that ADR-3660 (layout) and ADR-1508 (module ownership) each carry a
third of. Ships a companion reference matrix projecting all 16
runtimes from their capability descriptors.

- docs/adr/1593-skill-mapping-converter-methodology.md (new ADR)
- docs/reference/skill-mapping-matrix.md (new reference page)
- docs/adr/README.md (index row + status fixes: 3660, 1016 Proposed->Accepted)
- docs/adr/1016-runtime-capability-descriptor.md (header Proposed->Accepted;
  the ConverterName enum is already code-enforced per ADR-857 phase 5e)

Doc-only. No code, no behavior change. Closes #1593.
2026-06-22 16:27:41 -04:00
Tom Boucher
3a06b4888a Merge branch 'next' into feat/1173-wire-agent-converters-descriptor 2026-06-22 13:04:46 -04:00
Rezolv
195d356d7c Merge branch 'next' into feat/1346-enhance-verify-phase-project-a-check-vio 2026-06-21 17:41:37 -04:00
Tom Boucher
21fe9d3627 chore(#1544): test-quality + doc cleanups from the #1507 conversion-module review (#1547)
Follow-up hygiene from the #1507 epic review (release blocker fixed in #1537):

- path-replacement.test.cjs: delete the hand-reimplemented computePathPrefix copy
  (ADR-1508 said to); route all cases (incl. Windows + outside-home) through the
  real _computePathPrefix so the test can no longer drift from the function.
- enh-1511: add an isWindowsHost no-op tripwire characterization test.
- enh-1511: add a deterministic (fs-method monkeypatch, root/OS-independent)
  error-path test for applyRuntimeContentRewritesForCommandsInPlace — asserts the
  temp dir is rm'd on a read failure with no orphaned gsd-cmd-rewrites-* leak.
- runtime-artifact-conversion.cts: @internal note on rewriteStagedCommandBodies
  (deep-seam companion to rewriteStagedSkillBodies; no production caller today).
- ADR-1508 + CONTEXT.md: qualify the "single owner" claim with the deliberate
  opencode/kilo applyOpencodeFamilyPathPrefix pre-conversion carve-out (#784).

No user-facing behavior change (tests + internal docs + one code comment).
Refs #1507.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 15:20:46 -04:00
Behruz Nassre Esfahani
2b6107d46f refactor(#1173): defer agents-kind descriptor declarations (option a)
Adopt maintainer-recommended option (a): keep the convertedAgentsKind /
stageAgentsForRuntimeWithConverter scope-threading plumbing, but DEFER the
8 runtimes' capability.json `agents`-kind declarations to a follow-up that
first ships the ADR-1235 §0 byte-for-byte parity harness.

The declarations were a live regression: the second `layout.kinds` consumer,
applySurface / `/gsd:surface` / `--materialize` (src/surface.cts), does not
mirror the legacy agent pipeline (copilot `.agent.md` rename, path-prefix
rewrite + attribution, stale cleanup), so a `/gsd:surface` toggle deleted
installed copilot `gsd-*.agent.md` and path-unrewrote the other 7 runtimes.
trek-e + davesienkowski both flagged this.

- Revert the agents-kind entries from the 8 capability.json files and
  regenerate capability-registry.cjs (now matches next; 0 converted agents
  kinds declared).
- Revert the declaration-driven kind-count test bumps
  (runtime-artifact-layout, descriptor-drive, bug-782, enh-789, enh-790).
- Keep the synthetic-descriptor seam tests for convertedAgentsKind dispatch;
  add a synthetic scope-threading test so the kept isGlobal plumbing stays
  covered without depending on real declarations.
- Update the convertedAgentsKind doc comment to state declarations are
  deferred pending the ADR-1235 §0 parity harness.
- Move ADR-1235 to Accepted.
- Reword the changeset to plumbing-only (docs-exempt now honest: no runtime
  declares the kind, legacy loop authoritative, installed output unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:29:50 -07:00
Dave
c09c13f295 enhance(verify-phase): node-test causation control — prove the RED is content-caused (#1346)
The #1279 node-test machine-proof confirmed a known-bad subject drives the
negative test RED, but could not distinguish a genuine content-violation from a
deceptive test that reds merely because GSD_PROHIB_SUBJECT is set. Add an
optional fifth flat scalar `check_clean_fixture` (-> CheckDescriptor.cleanFixture)
threading a KNOWN-CLEAN control subject through projectProhibitions +
descriptorFromProjection. When present, the prover also runs the check against
the clean subject and requires GREEN, so fail-first is proven only when the check
is RED on the violation AND GREEN on the clean subject (content-dependent).

Opt-in and additive: absent a clean fixture the prover behaves exactly as
post-#1314 (no control, documented residual), preserving the zero-authoring
compose path; the lint-rule kind needs no analog (its subject IS the linted
file, no env indirection). Coverage: RED-first deceptive case, positive,
missing-clean fail-closed, round-trip read-back/emit, fast-check property
extended to the 5th scalar, and an end-to-end COMPOSE capstone (honest vs
deceptive). Docs: ADR-550 dated addendum, prohibition-probe reference,
spec-phase + verify-phase workflows.

Closes #1346

Claude-Session: https://claude.ai/code/session_01GsPRb8zvpcT7Eat6vZw8PX
2026-06-21 10:44:48 -04:00
Tom Boucher
b65892e6a2 docs(#1508): add ADR for Runtime Artifact Conversion Module content-rewrite ownership
Phase 0 of epic #1507. Records the decision to make the Runtime Artifact
Conversion Module the single owner of per-runtime content rewriting, flip the
dependency direction to installer/layout -> conversion, and close the
surface.cts -> bin/install.js getInstallExports relay. Doc-only.

Resolves ADR-3660 Initial-Scope deferral; distinct from epic #1258.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187qgypdy1wkWRpdaf2hRuD
2026-06-20 20:54:32 -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
c866ac1b24 fix(#1462): fail closed without data loss on a corrupt capability ledger; atomic ledger write (#1469) 2026-06-19 19:55:10 -04:00
Rezolv
dcceb1a004 fix(#1441): antigravity resolver prefers GSD-owned dir over first-existing (#1442)
* fix(#1441): antigravity resolver prefers GSD-owned dir over first-existing

resolveConfigHomeFromDescriptor's dot-home-nested probe returned the first
bare-existing candidate, so a CLI user (~/.gemini/antigravity-cli) who also
had the IDE's ~/.gemini/antigravity dir was silently shadowed to the legacy
dir (probed first). Regression from #217 — pre-#217 returned
~/.gemini/antigravity unconditionally.

Add an optional probeMarker (gsd-core/VERSION) to the dot-home-nested
descriptor: a two-pass probe prefers the candidate GSD installed into, then
bare existence, then probe[0]. Behavior is byte-identical when probeMarker is
absent (windsurf etc. unaffected). Adds detectAntigravityDirAmbiguity() for
installer/operator guidance on already-misinstalled users (auto-relocation
ruled out per ADR-0008's single-configDir migration bound).

Regression tests fail before / pass after: coexistence + marker-priority
cases, end-to-end through the registry descriptor.

Claude-Session: https://claude.ai/code/session_01JU2WB23JLE3QTysPXuFJjB

* chore(#1441): add changeset for antigravity resolver fix

Claude-Session: https://claude.ai/code/session_01JU2WB23JLE3QTysPXuFJjB
2026-06-18 21:47:18 -04:00
Tom Boucher
b0c774c2e3 feat(#1416): formalize Resolution convention + agent-skills value envelope (Resolution Provenance P3) (#1425)
Narrows P3 of ADR-1411 (Resolution Provenance, epic #1411) based on an
adversarial fit-analysis that showed a single Resolution<T> envelope adopted
by agent-skills, capability-state, and capability-writer fails the deletion
test: configured/reason are meaningless for capability verbs, and
capability-writer's errors[] (operation-not-applied) cannot fold into
warnings[]. The only genuinely shared seam is warnings: string[].

Changes:

- src/resolution.cts: new pure types+builder leaf — exports Resolution<T>
  {value, configured, reason, warnings}, makeResolution<T>() builder, and
  AgentSkillsValue {block, skills_count}. No other src/ imports.

- src/init.cts: cmdAgentSkills --json IR gains additive value:{block,
  skills_count} field (built via makeResolution). All existing flat fields
  (agent_type, block, skills_count, warnings, configured, reason, source,
  degraded) are retained unchanged for back-compat.

- src/capability-state.cts: doc comment on ResolveCapabilityRuntimeStateResult
  naming it the canonical read-verb envelope. No JSON change.

- src/capability-writer.cts: doc comment on SetCapabilityStateResult naming it
  the canonical mutation-verb result (warnings=advisory, errors=operation-
  not-applied). No JSON change.

- CONTEXT.md: new ### Resolution Convention glossary entry after
  ### Resolution Provenance.

- docs/adr/1411-resolution-provenance.md: P3 narrowing amendment appended.

- tests/resolution.test.cjs: 9 unit tests for makeResolution (new).
- tests/agent-skills.test.cjs: 2 P3 tests for value.block/value.skills_count
  and back-compat of all flat fields.

All 277 tests pass (5 suites). npm run lint clean. All lint checks pass.

Part of #1411

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 07:44:52 -04:00
Tom Boucher
6f2547610e docs(#1412): ADR-1411 — Resolution must report provenance, not fall open silently (#1413)
Decision record for the Resolution Provenance epic (#1411, P0). Establishes that
context resolution (config loading, project-root anchoring, workstream resolution)
must report its provenance rather than fall open silently to defaults — the
resolution-side analog of ADR-227. Binds the Config Loader Module, Project-Root
Resolution Module, and I/O Module. Adds the ADR, the index/seam-map entry, and the
CONTEXT.md glossary term.

Part of #1411
Closes #1412

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:42:17 -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
56d4a1bf39 enhance(#1279): project check_violation_fixture scalar — #1278 locate + #1279 proof compose end-to-end (#1346)
Delivers option (a) from the #1314 maintainer review: thread a fourth flat
scalar check_violation_fixture through the projection so a prohibition authored
at spec-phase machine-proves fail-first and greens through the deterministic
path alone — zero hand-authoring at verify time.

- src/probe-core.cts: Prohibition gains check_violation_fixture?; projectProhibitions
  emits it (both kinds) ONLY for a well-formed descriptor and ONLY when non-empty
  (blank/absent -> projects absent -> producer hard-gates, never a partial green).
- src/prohibition-enforcement.cts: descriptorFromProjection reads it back into
  violationFixture via the same numeric-coercion-safe scalar() normalizer.
- Tests (RED-first, proven non-vacuous by reverting both src edits): CHK-02(#1346)
  projection emit, CHK-08(#1346) read-back, CHK-03(D) example round-trip, the
  fast-check round-trip property extended to the 4th scalar (the contract trek-e
  blocked #1301 on), and a real-subprocess COMPOSE capstone greening end-to-end
  through project -> descriptorFromProjection -> default prover+runner.
- Docs flipped from 'hard-gates until #1346' to 'composes end-to-end': verify-phase.md,
  prohibition-probe.md, spec-phase.md authoring, ADR-550 addendum, changeset.
  #1346 now tracks only the node-test causation residual.

190 affected-suite tests green; eslint + tsc clean; size baseline regenerated.
2026-06-16 14:00:49 -04:00
Dave
33b6ee6f0c fix(#1279): node-test prover fail-closes on missing violationFixture + honest projection docs
Addresses the #1314 maintainer review (trek-e):

- Major 1 (fail-OPEN): defaultProveFailFirst's node-test branch only guarded
  `if (!fixture)`. A missing/typo'd/stale violationFixture made GSD_PROHIB_SUBJECT
  point at a missing file; an honest negative test threw ENOENT *inside its
  callback* (a failing test named distinctly from the file), which
  isNonVacuousNodeTestRed accepted as proof -> a green forged from a setup crash.
  Now requires fs.existsSync(path.resolve(cwd, fixture)) before spawning, symmetric
  with the lint-rule path's file-result guard. Regression test pins it (RED without
  the guard); a second test pins cwd-relative fixture resolution.

- Major 2 (misleading prose): the #1278 projection carries no violationFixture, so
  the deterministic-locate path always hard-gates (fail-closed) until a
  check_violation_fixture scalar is threaded through. verify-phase.md and
  prohibition-probe.md no longer read as if the projected path produces greens;
  the ADR-550 addendum records both items. Tracked as follow-up #1346.

- Documented residual: existence is necessary but not sufficient (a red caused by
  the env being set vs the subject's content); recorded as a constraint, in #1346.

- Nit: stale 'NOT attested fail-first' comment -> 'NOT machine-proven fail-first'.

64 tests pass; eslint + tsc clean; changeset valid.
2026-06-16 13:05:20 -04:00
Dave
49bef1927e Merge remote-tracking branch 'origin/next' into feat/1279-fail-first-prover
# Conflicts:
#	docs/adr/550-spec-phase-probe-contract.md
#	gsd-core/workflows/verify-phase.md
#	tests/workflow-size-baseline.json
2026-06-16 00:32:22 -04:00
Dave
7676420d8e docs(#1279): changeset pr→1314 + node-test non-vacuous-red wording in changeset/ADR 2026-06-16 00:19:51 -04:00
Rezolv
e50ead7ad2 enhance(verify-phase): deterministic auto-locate of the prohibition check descriptor (#1278) (#1301)
* test(1278): RED-first descriptor parity + fail-closed guards + CHK-07 byte-stability (wave 1)

- CHK-03 (RED): extend PROB-14 parity in prohibition-probe.schema.test.cjs to carry the flat
  check_kind/check_target/check_rule scalars through project->write->parseMustHavesBlock; the
  non-droppable check_kind-presence assertion is the load-bearing RED trigger (fails because
  projectProhibitions strips check_* on the current build).
- CHK-07 (GREEN forward-guard): probe-core.test.cjs pins descriptor-less byte-stability +
  dispositionForProhibition fail-closed policy, with a t.todo marker forward-locking plan 01-02.
- CHK-06 (RED): prohibition-enforcement.test.cjs asserts descriptorFromProjection export +
  fail-closed on absent/partial/unknown descriptors via the projection adapter (RED until 01-03).
- No src/*.cts or .cjs edits; no new test files; lint-test-file-count clean.

* feat(1278): add optional flat-scalar check descriptor fields to Prohibition interface (wave 2)

- check_kind?/check_target?/check_rule? mirror CheckDescriptor.kind/target/rule (minus caller-attested failFirst, #1279)
- optional so existing Prohibition consumers compile unchanged

* feat(1278): project check descriptor as flat scalars in projectProhibitions (wave 2)

- emit check_kind/check_target (+ check_rule only for lint-rule with a rule) when descriptor well-formed
- under-specified/descriptor-less items project byte-identically (CHK-07); flat scalars ride existing parseMustHavesBlock continuation-KV path (no parser rewrite)
- add CHK-02 probe-core unit cases pinning the projection
- turns CHK-03 parity test GREEN; dispositionForProhibition untouched

* feat(1278): descriptorFromProjection read-back adapter feeds fail-closed locate (wave 3)

- Add descriptorFromProjection(projected) -> CheckDescriptor | null to
  src/prohibition-enforcement.cts: renames the projected flat scalars
  check_kind/check_target/check_rule -> {kind,target,rule?}, or null when
  the descriptor is absent/non-object (no check_kind key).
- failFirst is NEVER sourced from the projection (stays caller-attested; #1279).
- rule is set only when check_rule is a non-empty string; the adapter does NOT
  re-validate kind/target/rule — an under-specified descriptor reconstructs to
  one the EXISTING runProhibitionEnforcement LOCATE guard rejects (located:false,
  never green). The merged #1259 guard stays the single source of fail-closed truth.
- Turns the RED CHK-06 fail-closed tests (plan 01-01) GREEN end-to-end; CHK-03 /
  CHK-07 stay green. CheckDescriptor type, locate guard, dispositionForProhibition,
  and parseMustHavesBlock are unchanged (additive +36/-0).

* feat(1278): verify-phase locates prohibition check from projected descriptor (wave 3)

- request.check kind/target/rule sourced from projected check_kind/check_target/check_rule via descriptorFromProjection, not verifier invention (CHK-05)
- replaces the #1278 author-supplied / tracked-follow-up note with the delivered deterministic-locate behavior
- preserves fail-closed routing: absent/partial descriptor -> never green, hard-gate in both modes
- failFirst stays a verify-time caller attestation; #1279 bounds the remaining fail-first proof

* feat(1278): spec-phase captures wired-check descriptor on test-tier resolution (wave 3)

- Step 5.6 'Keep it' / verification: test path captures check_kind/check_target/check_rule, projected onto must_haves.prohibitions for verify-phase deterministic locate (CHK-04)
- SOFT capture: a test-tier prohibition without a descriptor is still allowed (no hard authoring block); stays fail-closed/flagged downstream
- --auto captures only an unambiguous descriptor, never fabricates a check path
- failFirst NOT captured at spec-phase (verify-time attestation; #1279)
- PROB-06 soft-gate + text-mode (PROB-09) behavior unchanged

* chore(1278): re-baseline workflow size for grown verify-phase + spec-phase prose (wave 3)

- spec-phase.md 28438 -> 30343 (+1905), verify-phase.md 35362 -> 36498 (+1136)
- regenerated via npm run size:baseline (no hand-picked numbers); growth is the #1278 deterministic-locate + descriptor-capture prose
- workflow-size-budget guard green (122/122)

* docs(1278): ratify optional check descriptor in dated ADR-550 addendum + type:Changed changeset

- Append dated 2026-06-15 ADR-550 addendum ratifying the D3 prohibition-item
  shape extension (optional flat-scalar check_kind/check_target/check_rule)
- Document flat-scalar rationale, deterministic projection/read-back,
  fail-closed on partial/invalid/absent, #1279/policy out-of-scope
- Add .changeset/1278-prohibition-check-descriptor.md (type: Changed)

* docs(1278): document optional check descriptor in prohibition-probe reference + FEATURES

- Add 'Optional wired-check descriptor (deterministic locate, #1278)' section
  to the prohibition-probe reference (flat-scalar keys, projection/read-back,
  fail-closed + backward-compat, failFirst stays attested)
- Add deterministic prohibition-check descriptor source entry to FEATURES.md
- No CONTEXT.md glossary change: descriptor reuses existing wired-check /
  verification:test vocabulary, no new glossary term introduced

* fix(1278): pass packaging gates — changeset pr field + retired slash-form fix

- Add required pr: 1278 to changeset (lint:changeset MISSING_PR hard requirement;
  plan's 'omit if unknown' was inaccurate — issue number per #1259 convention,
  updated to real PR number when opened) [Rule 3 - blocking]
- Fix retired /gsd-spec-phase -> /gsd:spec-phase at verify-phase.md:83 (wave-3
  prose; caught by slash-namespace invariant #3443/bug-2543, blocked CHK-09
  full-suite-green) [Rule 1 - bug]
- size:baseline + INVENTORY manifest verified in-sync post-build (no diff)

* docs(1278): add check descriptor + descriptorFromProjection to CONTEXT.md prohibition glossary

* fix(1278): harden descriptorFromProjection round-trip (numeric-coercion + stray-rule) per review

- MD-01/LW-01: narrow projected scalars to primitives + String()-coerce, so a
  numeric-looking check_target (parseMustHavesBlock coerces ^\d+$ to number)
  reconstructs as a string and locates instead of silently un-locating; no
  as-string type-lie, satisfies no-base-to-string.
- LW-02: attach rule only for the lint-rule kind (drop a stray node-test rule).
- LW-03: document the optional check_* keys in the reference Output schema.
RED->GREEN tests added in prohibition-enforcement.test.cjs.

* chore(1278): set changeset pr to 1301

* test(1278): add fast-check property for the check-descriptor round-trip + fail-closed (trek-e review)

RULESET.TESTS.property-based-testing: the projectProhibitions -> render ->
parseMustHavesBlock -> descriptorFromProjection chain is a bijective/transformation
contract. Adds 2 fc properties to tests/probe-core.property.test.cjs (no new file;
ratchet stays at 2 for probe-core):
- well-formed descriptors survive the round-trip across the full string domain
  incl. the numeric-coercion case (target/rule reconstruct as strings);
- under-specified/invalid descriptors (absent / target-less / rule-less /
  unknown-kind) are always fail-closed (never green, flagged, unlocated).
Stability is asserted at the descriptorFromProjection layer (the raw parse step is
intentionally lossy for numeric scalars; the shared parser is unchanged).

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-16 00:01:03 -04:00
Dave
8cf4716c99 docs(#1279): ratify ADR-550 addendum + FEATURES/reference/verify-phase + changeset
- ADR-550 dated 2026-06-15 addendum: machine-proven fail-first REALIZED (D5d CLOSED); ratify violationFixture, GSD_PROHIB_SUBJECT, failFirst demotion
- FEATURES REQ-PROHIB-07 reads shipped (machine-proven, not caller-attested)
- prohibition-probe reference + verify-phase descriptor shape updated with violationFixture + convention
- tag GSD_PROHIB_SUBJECT + violationFixture PROPOSED/renamable (zero live consumers) as a PR-review flag
- add .changeset/1279-machine-proven-fail-first.md (type: Changed)
2026-06-15 22:57:47 -04:00