* 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)
* 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).
The /gsd-debug orchestrator handled the gsd-debug-session-manager return
with only two literal-string checks (DEBUG SESSION COMPLETE, ABANDONED)
and no else branch, so a usable-but-non-terminal progress summary (the
manager's own turn/context budget exhausted mid-loop, with a valid
on-disk checkpoint) fell through to the user as if the debug were
complete. Same gap at the continue subcommand.
Callee side (agents/gsd-debug-session-manager.md): add an explicit
non-terminal CONTINUE_REQUIRED return marker, distinct from the two
terminal shapes and from a genuine user-input checkpoint.
Orchestrator (gsd-core/workflows/debug.md Sections 4 and 1c): classify
returns exhaustively — recognized terminal markers behave as before,
anything else is non-terminal and auto-resumes by re-spawning the
session manager from the same slug/checkpoint. Anti-loop guard: after
two consecutive no-progress resumes (unchanged next_action/updated),
emit a blocker report instead of looping.
Regression test (source-text contract guard, fix-2196 idiom) asserts
both sections' non-terminal/auto-resume branch, the CONTINUE_REQUIRED
marker, and the anti-loop bound.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(execute-phase): honor gate="blocking-human" in auto-mode checkpoint handling
The package-legitimacy gate (#2827) spans two layers. gsd-executor refuses to
auto-approve a gate="blocking-human" checkpoint and escalates it so a human can
vet the package. execute-phase's checkpoint_handling step then dispatched purely
on checkpoint *type* and never read gate -- so under --auto/--chain it
auto-approved the checkpoint the executor had just refused to auto-approve.
Net effect: the slopsquatting defence was inert in exactly the unattended mode
where it matters. An [ASSUMED]/[SUS] package reached install with no human ever
seeing the prompt.
- gsd-core/workflows/execute-phase.md: carve out gate="blocking-human" (and the
package-legitimacy what-built markers) ahead of every auto-mode branch.
- gsd-core/references/checkpoints.md: document the gate attribute and its two
values. blocking-human previously appeared nowhere outside gsd-executor.md,
so no planner had a documented way to author a non-auto-approvable checkpoint.
- tests/package-legitimacy-gate.test.cjs: the existing regression test asserted
the executor half only, which is why it stayed green while the gate was open.
Now asserts the orchestrator half too.
* chore(changeset): link to issue #2107
* chore(changeset): backfill PR number 2113
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNR8m2pv5U7ubn4iiXVrMa
* test(#2107): refresh golden-install-parity hashes for edited gsd-core files
The golden fixtures pin content hashes for gsd-core/references/checkpoints.md
and gsd-core/workflows/execute-phase.md, both edited by this fix. Regenerated
via UPDATE_GOLDEN=1; only those two keys change across all 17 runtime fixtures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNR8m2pv5U7ubn4iiXVrMa
* fix(#2107): keep the carve-out inside the ADR-857 host-loop budget
The ADR-857 phase-6 ratchet pins execute-phase.md below 93600 LF bytes so
optional-feature logic keeps migrating out of the host loop. The carve-out
first landed 623 bytes over that ceiling.
Move the two-layer rationale (why gsd-executor escalates these checkpoints)
into references/checkpoints.md, where the gate is now documented, and reduce
the workflow to the operative rule. execute-phase.md is 93589 bytes, under
the ceiling; the gate token and both <what-built> marker strings are kept
because the orchestrator matches on them.
Refresh the two baselines the edit invalidates: golden-install-parity
fixtures (only the checkpoints.md and execute-phase.md hashes move) and
workflow-size-baseline.json (one line). The ADR-857 ceiling itself is
untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNR8m2pv5U7ubn4iiXVrMa
* fix(#2107): executor honors blocking-human on the decision branch + gate transport
Review found the fix incomplete one layer down. Two executor-layer gaps:
1. Blocker — agents/gsd-executor.md auto-mode dispatch gated
checkpoint:human-verify on gate="blocking-human" but the checkpoint:decision
branch below auto-selected the first option with no gate check. The executor
resolves a decision itself (auto-selects and continues) without returning it,
so the orchestrator carve-out never runs for it. A planner following the new
checkpoints.md rule 6 ("gate a decision whose default would be wrong to
assume") would have it silently auto-selected under --auto/--chain — the exact
#2107 harm, one checkpoint type over. The decision branch now STOPs and
returns for an explicit human decision when gate="blocking-human".
2. Major (transport) — checkpoint_return_format carried no field conveying the
gate to the freshly-spawned orchestrator, so recognition of the proactive
pre-install checkpoint rested on freeform prose. Added a **Gate:** field to
the return format and re-pointed the execute-phase carve-out at it
("If the returned Gate: is blocking-human"). Net byte-negative: execute-phase.md
drops 93589 -> 93583, widening ADR-857 headroom from 11 to 17 bytes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#2107): cover decision carve-out + gate transport, de-vacuum conditional tests
- New: 'auto mode does not auto-select a blocking-human decision checkpoint'
asserts the executor decision branch STOPs on blocking-human. Verified red on
the pre-fix executor (2 fail), green with the fix (27 pass).
- New: 'checkpoint_return_format transports the gate ...' asserts the **Gate:**
field carries blocking-human across the executor->orchestrator boundary.
- New: 'auto-select rule for decision is conditional' — orchestrator-side mirror
of the human-verify conditional test, for the execute-phase decision branch.
- Fix vacuous test: both conditional tests now assert the anchor matched
(length > 0) before iterating, so anchor drift can no longer pass with zero
assertions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#2107): refresh golden + size baselines for executor + execute-phase edits
Regenerated via UPDATE_GOLDEN=1 and update-size-baseline.cjs. Only the
gsd-executor.md and gsd-core/workflows/execute-phase.md hashes move across the
runtime fixtures (35 ins / 35 del, no keys added or removed); checkpoints.md is
unchanged this round. Size baselines: gsd-executor.md 43607 -> 43973,
execute-phase.md 93589 -> 93583 (still under the ADR-857 ceiling).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
* fix(2119): single SECURITY.md writer — auditor is return-only
The gsd-security-auditor held Write/Edit and was instructed to write
SECURITY.md (no <N>- prefix, no template frontmatter), while the
orchestrator's Step 6 also wrote the correct padded <N>-SECURITY.md
from templates/SECURITY.md. Two writers, two naming conventions, two
shapes — the auditor's unprefixed file was invisible to the workflow's
*-SECURITY.md glob detector and unparseable for the threats_open gate.
Fix (option 1 from the issue): make the auditor return-only.
- Remove Write/Edit from auditor's tools
- Rewrite all 'Write SECURITY.md' instructions to 'Return structured
verdict' with threats_open count
- Add explicit constraint in workflow Step 5 spawn prompt
- Update existing test (was asserting Write in tools — now asserts absence)
- Add new regression test for single-writer contract
- Update docs/AGENTS.md stale Tools/Produces rows
- Regenerate golden fixtures + agent size baseline
* docs(changeset): backfill PR number (#2154)
* chore(#2119): regenerate pi/qwen golden fixtures after next merge
The single-writer change edits gsd-core/workflows/secure-phase.md and
agents/gsd-security-auditor.md; pi.json (added on next) and qwen.json (merge
straggler) were the only runtime fixtures still holding pre-change hashes for
those files. All other runtimes already reflect the change. Regenerated via
the sanctioned gen-golden-install-parity script.
* merge origin/next — regenerate goldens + baseline for merged state
* fix slash-command syntax: /gsd-secure-phase → /gsd:secure-phase (#2154 CI fix)
* fix(#2017: grant mcp__plugin_context7_context7__* for plugin-marketplace context7
The 8 context7-using agents granted only mcp__context7__* (standalone server
form). Claude Code's plugin-marketplace context7 install names tools
mcp__plugin_context7_context7__*, so the grant never matched and every
researcher/planner/executor silently lost doc lookup (fell back to WebSearch).
- 8 agents: add mcp__plugin_context7_context7__* alongside mcp__context7__*.
- scripts/research-profiles.cjs: update the researcher profile tools to match.
- tests/context7-plugin-grant-parity.test.cjs: regression guard — no agent
grants the standalone form without the plugin form.
Closes#2017
* docs(#2017): backfill changeset pr 2029
* fix(#2020): remove dead SDK file refs that triggered infinite find.exe on Windows
gsd-executor.md referenced sdk/src/query/QUERY-HANDLERS.md and reapply-patches.md
referenced sdk/dist/cli.js — both retired with the SDK (ADR-0174). AI runtimes
that locate doc refs via filesystem search ran find /, which on Git Bash for
Windows traverses the whole drive (14h+, orphaned find.exe, 4M+ handles).
- agents/gsd-executor.md: drop dead QUERY-HANDLERS.md ref.
- workflows/reapply-patches.md: drop dead sdk/dist/cli.js clause.
- tests/no-dead-sdk-refs.test.cjs: regression guard — no sdk/src|dist|handlers
file refs in agents/workflows/references markdown.
Closes#2020
* docs(#2020): backfill changeset pr 2027
Wire the two forward-declared mempalace.memory_mode modes so they actually
route recall/capture instead of silently behaving as `augment`:
- kg_backend: the palace temporal KG is the primary knowledge-graph source;
native .planning/graphs/ is the fallback. Non-KG drawer recall stays additive.
- replace: recall resolves through the palace as the source of truth; native
artifacts are the fallback.
Every mode stays onError:skip and default-resilient — an unreachable palace
degrades to native memory and GSD keeps writing .planning/graphs/, so no memory
is lost. Cross-mode .planning/graphs/ migration remains a documented open
question (PRD/ADR §17), out of scope here.
Surfaces updated (instruction-only contract): recall/capture commands (+ generated
skills), discuss/wave fragments, curator agent, capability.json schema. Docs:
how-to Step 3, CONFIGURATION, FEATURES, CONTEXT glossary. Regenerated
capability-registry, golden install-parity fixtures (mempalace hashes only),
agent-size-baseline. Added a routing-contract + cross-surface parity test.
Incidental (folded per no-defer rule): removed pre-existing unused imports
(spawnSync in capability-registry.test.cjs; fs in issue-498-package-identity.test.cjs)
that eslint flagged in/alongside the touched files.
Closes#2007
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#1865): runtime launcher honors CLAUDE_CONFIG_DIR
The gsd_run preamble resolved the Claude global install only at
$HOME/.claude/gsd-core/bin/, but the installer honors CLAUDE_CONFIG_DIR —
so a global install redirected via CLAUDE_CONFIG_DIR was invisible to every
gsd_run call (every command failed with 'gsd-tools.cjs not found').
The Claude resolver arm now uses ${CLAUDE_CONFIG_DIR:-$HOME/.claude},
matching the installer + the other runtimes' ${VAR:-default} pattern.
Default $HOME/.claude behavior is unchanged.
- _runtime-launcher.snippet.sh: Claude arm honors CLAUDE_CONFIG_DIR.
- sync-runtime-launcher.cjs re-run: 95 workflows/agents re-synced.
- review.md / discuss-phase.md: trimmed to stay under their byte budgets.
- runtime-launcher-parity.test.cjs: (A) substring updated for the new form
+ explicit #1865 assertion that the snippet honors CLAUDE_CONFIG_DIR.
- goldens + size baselines recaptured.
Closes#1865
* docs(#1865): backfill changeset pr 2024
* fix(#1863): use named flags for state.* calls in executor + workflows
The named-only state-command router (parseNamedArgs) silently drops
positional args, so state.cjs threw its required-arg error and
metrics/decisions/blockers/session continuity were never recorded.
Convert record-metric / add-decision / add-blocker / record-session in
agents/gsd-executor.md to the named-flag form (mirroring execute-plan.md),
and fix the two remaining positional record-session calls in
gsd-core/workflows/milestone-summary.md and forensics.md. Recapture the
golden-install-parity fixtures and size baselines for the edited files.
Also fix a pre-existing detached-rebuild handle leak in
tests/graphify-auto-update.slow.test.cjs: three dispatch tests returned
after observing only the synchronous "running" status without awaiting the
detached rebuild's terminal state. That leak was latent until the new
#1863 regression block's added runtime shifted --test-force-exit timing
and surfaced it as a non-zero chunk exit. The three tests now await
terminal status via the file's existing waitForBuildStatus helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#1863): add changeset
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
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
execute-phase.md and gsd-ai-researcher.md are installed artifacts; their edits
shift install hashes and file sizes. Diff is scoped to those two files' hashes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* 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>
* 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>
workflow.security_block_on was documented as the minimum threat severity
that blocks advancement, but threats carried no severity and the auditor's
threats_open count (the SECURITY.md gate field) counted every open threat
regardless of severity — so the threshold had no effect, and the auditor's
block_on vocabulary (open/unregistered/none) did not even match the config
enum (critical/high/medium/low/none).
- planner: add a Severity column to the STRIDE threat register; assign
severity per threat.
- auditor: read severity; reconcile the <config> block_on domain to the
severity enum; redefine threats_open as the count of OPEN threats whose
severity is at or above block_on (none => 0). Below-threshold opens are
reported as non-blocking and excluded from threats_open.
- SECURITY.md template + planning-config.md reconciled.
No gate-check site changed: threats_open == 0 stays the gate everywhere;
only its computation is now severity-filtered.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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>
* fix(#1478,#1479,#1480): prohibit ungrounded baselines, error-suppressing fallbacks, and stale-artifact authority in verify blocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add changeset for #1478/#1479/#1480 planner verify gate fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: correct changeset format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#1478,#1479,#1480): fix test contract violations from new planner dimensions
gsd-planner.md exceeded both the planner-decomposition 48K char limit and
the reachability-check 50K char limit after the new HARD RULE blocks were
added inline. The full rule details already exist in planner-antipatterns.md
(added in the same PR); replace the verbose inline blocks with a single
@-reference pointer to the antipatterns file, reducing the file from 50981
to 49130 chars (under both limits).
Also regenerate tests/agent-size-baseline.json to reflect the new sizes of
gsd-planner.md and gsd-plan-checker.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a region/function-scoped negative-grep idiom to the gsd-planner verification guidance plus a warn-only `validate_plan` check (`scanFileWideNegativeGateConflict`) that flags when a task's file-wide negative grep bans a construct a sibling task legitimately requires elsewhere in the same file. ReDoS-safe (linear, no RegExp on author patterns); region-scoped gates are exempt. Warn-only — never errors, never flips `valid`.
Closes#968
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds mcp__perplexity__* to both researcher profiles (generated source-of-truth) and regenerates the agents; adds a generative dispatch-table↔tools parity guard so future provider drift fails CI. Regenerates the agent-size baseline for the +20-byte frontmatter growth.
Fixes#1284
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* enh(#966): gate behavior-dependent truths on behavioral evidence in gsd-verifier
Introduce a per-truth PRESENT_BEHAVIOR_UNVERIFIED state for must-haves that assert a state transition or a cancellation/cleanup/ordering invariant whose only evidence is symbol presence + wiring. Such truths are excluded from the verified_truths score, reported as a behavior_unverified count, recorded in an always-on behavior_unverified_items frontmatter list, and routed to the existing human_needed sink — so a clean N/N can no longer be reached on symbol presence alone.
The overall-status vocabulary and the src/verification.cts seam are unchanged (the new state is per-truth only); gaps_found keeps decision-tree precedence; override-passed truths still count toward verified_truths. Mirrors the calibration into the shipped verify-phase.md workflow (with an infra/foundation carve-out), the VERIFICATION.md templates, and docs (planning-artifacts.md, AGENTS.md). gsd-verifier.md kept under its 48KB LARGE cap; size baselines regenerated.
Closes#966
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#966): add changeset fragment for gsd-verifier behavior-unverified calibration
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#1243): consume Claude plugin-provided skills via native Skill-tool directive + grant Skill to agent_skills-consumer agents
- Relax global skill name validation to accept namespaced form `^[A-Za-z0-9_-]+(:[A-Za-z0-9_-]+)*$`
- Namespaced names (containing colon) on claude runtime emit a Skill-tool load directive instead of a @-include line
- Namespaced names on non-claude runtimes are skipped with a warning
- Bare unresolved names retain existing warn-and-skip behavior (no promotion to directive)
- Grant `Skill` tool to all 22 agent_skills consumer agents; 5 generated agents updated via research-profiles.cjs + regen, 17 hand-authored agents edited directly
- Add 16 TDD tests in describe('bug #1243') covering happy/mixed/precedence/negative/cross-runtime/regression/grant cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(#1243): document plugin-provided skills in agent_skills
Update the Agent Skills Injection reference in CONFIGURATION.md with
the three entry forms (project-relative, global:<name>,
global:<plugin>:<skill>), the Claude-only runtime behaviour of the
namespaced form and the warn-skip on other runtimes, the plugin
pre-install prerequisite, and the consumer-agent Skill tool grant.
Add docs/how-to/attach-a-plugin-skill-to-a-gsd-agent.md with a
step-by-step guide for installing the plugin, locating the namespaced
skill name, wiring it into agent_skills, and verifying injection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#1243): align agent_skills docs with emitted block format + mixed-block regression test (code-review)
- Replace two-section mixed-block example (bogus "Load these plugin-provided skills using the Skill tool:" header) with the actual single-section inline format in CONFIGURATION.md and docs/how-to/attach-a-plugin-skill-to-a-gsd-agent.md
- Fix quoted warning text in how-to doc to exactly match the emitted string: [agent-skills] WARNING: Plugin-namespaced skill "global:<name>" requires a Skill-tool-capable runtime (claude) — skipping on runtime "<runtime>"
- Replace phantom agent slugs (gsd-checker, gsd-researcher, gsd-advisor, gsd-synthesizer) in CONFIGURATION.md Supported Agent Types with real agents/gsd-*.md examples (gsd-plan-checker, gsd-phase-researcher, gsd-code-reviewer, gsd-ui-auditor, gsd-research-synthesizer)
- Add byte-identical mixed-block regression test: one path-resolvable global skill + one plugin-namespaced skill on claude runtime → asserts r.ir.block === single-section interleaved block, no secondary header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(#1243): regenerate agent-size baseline for the Skill-tool grant
The 22 agent_skills-consumer agents each grew +7 bytes from adding `Skill`
to their tools list; refresh the committed per-agent size baseline (#1074 guard).
* chore(#1243): add Added changeset fragment
* fix(#1243): traceable allow-test-rule ref + separator-agnostic byte-identical tests (CI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
* fix(#1205): roadmapper applies phase_id_convention to generated phase IDs
- Add Phase ID Convention section to <phase_identification> block:
documents sequential (default) vs milestone-prefixed forms, and
instructs the agent to read phase_id_convention from config.json
- Update <output_formats> to show both header and checklist forms for
sequential and milestone-prefixed conventions with examples
(e.g. ### Phase 1-01: Name, - [ ] **Phase 1-01: Name**)
- Add TDD regression test tests/bug-1205-roadmapper-convention.test.cjs
(5 assertions, confirmed fail-first then pass after fix)
- Update tests/agent-size-baseline.json to reflect legitimate growth
- Add .changeset/brave-otters-leap.md (Fixed, pr:0 placeholder)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: backfill changeset pr: 1215 for fix/1205
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(#1205): move phase_id_convention regression into roadmapper-granularity.test.cjs
lint-regression-test-names rejects new standalone bug-NNNN-*.test.cjs files;
regression cases must live in the owning module's test file.
Move the 5 phase_id_convention assertions (#1205 regression) from the
removed tests/bug-1205-roadmapper-convention.test.cjs into
tests/roadmapper-granularity.test.cjs as a new describe block, alongside
the existing granularity calibration tests. Also update the allow-test-rule
comment to cover both #163 and #1205 surface contracts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(#1205): fix lint-allow-test-rule-refs for roadmapper-granularity
- Add issue ref (see #1205) to allow-test-rule comment in
tests/roadmapper-granularity.test.cjs so lint-allow-test-rule-refs
passes (new exemptions require #NNN per ADR-456)
- Prune stale 'source-text-is-the-product' entry from
scripts/lint-allow-test-rule-refs.allowlist.json (ratchet-down;
comment now compliant and no longer needs grandfathering)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* 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>
Extends the #1074 scheme to tests/agent-size-budget.test.cjs, which used the
same assertTightCeiling tier ratchet but was still line-based (never rebased in
#717). Completes the migration — the last part of the #1074 epic.
- Rebase agent sizing from lines to LF-normalized bytes (#717/#683).
- Delete the 'SIZE: tier anti-creep' describe (3 assertTightCeiling tests);
add a per-agent baseline (tests/agent-size-baseline.json) as the primary
anti-creep, and byte hard caps (XL 56 KiB / LARGE 48 KiB / DEFAULT 24 KiB),
each above its tier high-water with real headroom. No separate new-file cap:
a net-new agent is DEFAULT-tier, already bounded by the DEFAULT cap.
- Keep the agent-classification tests verbatim.
- scripts/workflow-size.cjs: add generic measureMdFiles(dir, predicate)
(workflows + agents share one byte-measurement path); measureWorkflows now
delegates to it.
- scripts/update-size-baseline.cjs: one 'npm run size:baseline' now regenerates
BOTH the workflow and agent baselines (gsd-* filter for agents).
Rebased onto next after PR 2/3 (#1096) merged: replicate the
scripts/lib/workflow-size.cjs -> scripts/workflow-size.cjs move (PR 1/3) across
the generator and the agent test's require; regenerate the agent baseline
against current agents (a uniform +170 B preamble drift on all 33 since
authoring).
Addresses the #1097 review (trek-e):
- BLOCKER (acceptance criterion 5): document the agent contract in CONTEXT.md.
Adds RULESET.AGENT_SIZE_BUDGET (caps 57344/49152/24576, per-agent baseline,
dual size:baseline, shared measureMdFiles seam) and disambiguates it from the
separate DEFECT.AGENT-FILE-SIZE-CAP-BREACH 45K-CHAR guard (two units, two
purposes).
- Docs: now that #1096's docs/TESTING-SUITES.md "Workflow size budget" section
is in next, fold in the agent coverage here (renamed to "Workflow & agent
size budget"): agent caps + per-agent baseline + the how-to + reference rows,
and the disambiguation from the 45K-char guard.
- Minor (negative proof): add a boundary-fixture test exercising the hard-cap
comparison at cap-1/cap/cap+1 through the real lfByteCount path, so a future
threshold/operator edit can't silently neuter a cap.
- Nit: align the tier test name wording ("stays within") with the <= operator.
Negative proof on a real tracked agent (gsd-planner): baseline catches +10 B;
XL hard cap catches 57,516 > 57,344 with the baseline current.
Closes#1095 (PR 3/3 child); landing this completes the #1074 epic.