Check
Claude Code plugin marketplace — sdd (spec-driven dev) and gh (GitHub workflow) plugins
npx -y skills add kborovik/pilot-skills --skill checkAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Read-only drift detector. Diffs SPEC.md vs current code and reports violations grouped by severity. not writes — suggests remedies via spec or build skills, not invokes them. Triggers when user asks to check drift, audit spec, verify invariants, or ask if code still matches invariants. Phrasings: "check drift", "audit the spec", "does the code still match invariants", "check invariants", "spec vs code", "is the spec still accurate?", "did the code drift?".
SKILL.md
26.9 KB, as published. Nobody here has run it
check — drift report
Pure diagnostic. Reports violations. Writes nothing to SPEC or code. User decides remedy.
PREAMBLE
/sdd:check is read-only per read-only-cmd invariant; memo + .gitignore guard writes in REPO-LOCAL .claude/ per scope-set invariant so sibling state not SPEC or code not violate read-only contract. Sub-agent delegation allowed throughout (check is read-only so not coordination hazard). The mechanical audit set is deterministic so delegated to a published script per the mechanical-realization invariant — the skill body never re-derives those greps per run. Recipes parametric per parametric-recipe invariant — repo-specific extensions load via the script's .claude/scripts/check-extras.sh hook probe and the LLM consults .claude/check-extras.md judgment-class extras. Following sections defer to this preamble; defensive re-justification omitted.
LOAD
- Read
SPEC.md. If missing → "no spec, nothing to check." Stop. - Parse invocation args (two-option surface per dispatch invariant):
- bare (no args) → memo-driven default sweep covering invariants + interfaces + tasks; first-run (memo absent or invalidated) degrades to full re-classify and writes fresh memo on clean.
--full→ delete.claude/check-state.jsonupfront → run full sweep and write fresh memo on clean and propagate--fullto mechanical audit script → per-row history listing (skip body-row aggregation per drift-verdict-vocab invariant). Mid-run interrupt not memo so next run also full — safe failure mode for " not trust cache".- Unknown arg → bail w/
unknown arg <arg> — accepted forms: bare invocation, --full. Legacy section-name args and legacy multi-flag forms (retired per dispatch invariant) not admitted.
- Run the mechanical audit script (MECHANICAL CORE below) at audit start — its
memo|ADVISORY|…rows report any fired invalidation trigger and the per-rowv_row_shasdirty set that scopes §V re-classification. - §V row bodies for classification is sourced via the script's
emit-v-slicesmode (see SCOPE), not whole-fileSPEC.mdRead — large SPEC > Read token cap so full-file Read paginates; the script slice is canonical §V-body source every single-agent narrow-scope and sub-agent batch paths per the batch invariant.
MECHANICAL CORE — audit script
The mechanical audit set — SPEC-FORMAT structural rules (section catalog + order, row grammar, rightmost-| column extraction, archive markers + sibling shape), §T cites / §B fix grammar, monotonic-ID, cite-DAG resolution + edge-type, history-residue patterns + pre-filters + oversized-cell advisory, pinned-invariant-header grep, memo bookkeeping (sha / rev-parse), token estimate — is deterministic so owned by pilot-spec/scripts/check-mechanical.py per the mechanical-realization invariant. not re-derived as per-run LLM greps; the script's regex is single source of truth every these audits (mirrors the canonical-agent-block verbatim contract — per-run paraphrase not permitted).
Run at audit start (body-level ${CLAUDE_PLUGIN_ROOT} expansion; the variable not valid in frontmatter allowed-tools patterns so frontmatter grants broad Bash(python3 *)):
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check-mechanical.py audit [--full]
--full propagates from LOAD step 2 user invocation → restores per-row history listing (skip body-row aggregation per drift-verdict-vocab invariant); bare invocation aggregates per section above threshold.
Reads SPEC.md (+ SPEC.archive.md sibling if exists) from cwd; discovers PUBLISHED scope from .claude-plugin/marketplace.json (parametric per parametric-recipe invariant); probes REPO-LOCAL .claude/scripts/check-extras.sh hook (exists and executable → run, append its id|verdict|evidence rows — language-agnostic contract per parametric-recipe invariant). Emits the standardized pipe-table id|verdict|evidence:
format|VIOLATE|format: <detail>— SPEC-FORMAT rule breach (evidence prefixformat:).cite|UNRESOLVED|<citer> <id> …orcite|TYPE-MISMATCH|…— cite-DAG resolution + edge-type.cite|ambiguous|…is bare-form phase-label / gate-ID collision subset (consumers admitting bare-form REPO-LOCAL scope) → LLM adjudicates per CHECK §-cite; none every this repo's typed-prefix-only scope.history|VIOLATE|<row> … history: <pattern>— inlined-history residue (evidence prefixhistory:).history|ADVISORY|oversized cells …is smell, not VIOLATE.pinned-header|VIOLATE|<file:line> …— PUBLISHED body pins invariant number in header.token|ADVISORY|SPEC.md ~<n>k tokens > budget …— token estimate (bytes/3.4) per token-budget invariant.memo|ADVISORY|<trigger>— memo invalidation (schema_versionmismatch orlast_clean_shaunreachable → drop memo, full sweep) or scope feedv_row_shas drift: V<n>,…(per-row drift → re-classify named §V rows only — comma-joined field, not prose).tasks|ADVISORY|flipped-since-clean: T<n>,…— §T rows flipped.→xsince the memo clean sha (machine-side scope feed; empty field when none).diff|ADVISORY|touched: <paths>— paths changed since the memo clean sha (machine-side scope feed; comma-joined).
Main thread merges this table into REPORT verbatim — format / history / cite / pinned-header rows fold into their REPORT blocks, token / memo-invalidation rows into ## advisory. The scope-feed rows (memo v_row_shas drift, tasks flipped-set, diff touched-set) carry stable comma-joined fields (not surrounding prose) so feed SCOPE machine-side — chained into emit-v-slices --dirty, not surfaced in the ## advisory block, not hand-rolled via git diff.
MEMO
.claude/check-state.json is persistent memo (REPO-LOCAL cache, not source of truth — code and SPEC.md is truth). Schema v3:
{
"schema_version": 3,
"last_clean_sha": "<git HEAD @ last clean run>",
"v_row_shas": { "V<n>": "<sha256 of §V row body>" },
"last_run_at": "<ISO-8601 timestamp>",
"last_v_classifications": { "V<n>": "HOLD|HOLD-SINCE-CLEAN|SCOPE-EMPTY|VIOLATE-CAPTURED|LATENT" },
"oversized_cell_ack": "<sha256 over sorted oversized cell-id set>"
}
Script owns both ends per the memo invariant:
- read —
auditmode emitsmemo|ADVISORY|…rows every fired invalidation trigger:schema_versiondiffers current orlast_clean_shaunreachable (git rev-parsefails) → drop memo, degrade to full sweep; per-rowv_row_shasdrift → edited §V rows re-classified, hash-stable rows carry forward HOLD-SINCE-CLEAN.oversized_cell_ackis sha over the acknowledged oversized cell-id set — the script suppresses thehistory|ADVISORY|oversized cellsrow while the set is unchanged, re-fires when a new oversized cell appears (mirrors silence-class roll-forward — recurring smell not re-nags once acknowledged @ last clean run). - write —
write-memomode (see WRITE-MEMO) computes clean-set membership itself + per-row hashes + the oversized-cell ack + idempotent.claude/.gitignoreguard; LLM not decide clean, not hand-write memo.
Memo update is side-effect every clean run, not user-prompt. v_row_shas per-row keying invalidates edited rows only (not siblings); §V-section non-row content (archive-marker line) unhashed so not fires a re-classify, format audit covers marker shape so not coverage gap.
SCOPE — memo-driven default
Both scope dimensions is script-emitted machine-side (per the memo invariant) — the LLM consumes the audit scope-feed rows, not hand-rolls git diff. When memo exists and not invalidated, §V classification scope is dirty set:
- §V dirty is (rows w/
v_row_shasdrift per scriptmemo|ADVISORY|v_row_shas drift: …row) + (rows whose body-text path tokens intersect the scriptdiff|ADVISORY|touched: …row — path tokens is quoted/backticked path-like strings in invariant body matched against the script's touched-set). Row in neither source → emitV<n> HOLD-SINCE-CLEANper memo invariant, not re-classify. not re-derive the touched set viagit diff— the script'sdiff|ADVISORYfield is the source. - §T closed-row re-verify scoped to the script
tasks|ADVISORY|flipped-since-clean: …row (rows flipped.→xsincelast_clean_sha, machine-side). Historicalx(unchanged, not in field) → HOLD-SINCE-CLEAN. - §I + cite-DAG is full-sweep every run (cite-DAG owned by script; §I shape-diff cheap so not triage gain).
First-run (memo absent or invalidated per script memo|ADVISORY) or --full (memo deleted upfront) → classify all §V rows.
§V row bodies for the classified set (single-agent narrow-scope and sub-agent batch paths) is emitted by the script — not whole-file SPEC.md Read for §V bodies:
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check-mechanical.py emit-v-slices [--dirty V<n>,...]
Script reads SPEC.md directly and prints each §V row body w/ its source line range — header ## V<n> SPEC.md:<start>-<end> followed by the verbatim row text, one block per row. --dirty is comma-list of the dirty set per step 1 (default empty is all rows); first-run or --full is omit --dirty (all rows). Read token cap paginates a large SPEC so the script slice sidesteps pagination and is a dirty-filterable canonical §V-body source per the batch invariant — but not bulk-load: the narrow-scope single-agent path (batch 1) still loads the full dirty-filtered slice set in-thread (set may itself spill to a persisted file > inline output cap), whereas sub-agent batch paths distribute that load per-spawn. Pagination-sidestep differs bulk-load-sidestep.
CHECK invariants
The script does not classify behavioral invariants — §V claim → verifiable-code-claim translation and HOLD/VIOLATE judgment stays LLM. For each dirty V<n>:
- Translate invariant into verifiable claim about code.
- Determine recipe scope (some invariants reduce scope per scope-set invariant, e.g. PUBLISHED-only; every invariant w/o scope reduction is full repo) and compute touch set is the script
diff|ADVISORY|touched: …set intersect scope (memo-driven default; first-run or--fullis scope itself). not hand-rollgit diff— the script's touched-set is the source per the memo invariant. - Touch set empty → emit
V<n> SCOPE-EMPTY: <reason>per drift-verdict-vocab invariant; record evidencescope ∩ touch = ∅; skip grep. Silence differs verified-absence. - Row clean since
last_clean_shaper memo and recipe scope not intersected by current touch → emitV<n> HOLD-SINCE-CLEANper memo invariant; record evidenceHOLD-since-clean @ <last_clean_sha>; skip grep. - Else grep / read relevant files; classify HOLD / VIOLATE / VIOLATE-CAPTURED / UNVERIFIABLE / SCOPE-EMPTY / HOLD-SINCE-CLEAN / LATENT per drift-verdict-vocab invariant. Actionable set is {HOLD, VIOLATE, VIOLATE-CAPTURED, UNVERIFIABLE} — each → REPORT body row (HOLD silent) + distinct remedy-hint path. Silence set is {SCOPE-EMPTY, HOLD-SINCE-CLEAN, LATENT} — operationally identical: not REPORT body row, not remedy hint; aggregator (main thread) collapses → summary line under unified
suppressedlabel w/ per-reason breakdown per drift-verdict-vocab invariant. Classifier-internal verdicts preserved every memo precision (last_v_classifications). Verdict semantics: VIOLATE-CAPTURED is live violation and§B-recorded and remediation forward-only (e.g. historical commit body, archived artifact) — emit form<row-id> VIOLATE-CAPTURED: <evidence>; see §B.<n>; recipe-author classifies based on§Bcite presence (e.g. captured-sha list in REPO-LOCAL extension). LATENT is invariant trigger condition structurally absent from current repo state so audit no-op until condition fires (e.g. invariant constrains impl-code class not present per repo constraints; invariant triggers @ artifact size threshold currently unmet); distinct from UNVERIFIABLE (which signals missing audit infrastructure every claim that should otherwise be verifiable). - Record address + file:line evidence.
Recipes ! parametric — not name repo-literal paths beyond SPEC.md. Project-specific enforcement in the .claude/scripts/check-extras.sh hook (mechanical, run by the audit script) + .claude/check-extras.md judgment-class extras (verdict guidance, captured-sha lists) consulted by the LLM during classification.
Batch protocol (parallel invariant audit)
Invariant audit MAY parallelize via Explore sub-agents (per PREAMBLE). Per-batch shape:
- Batch count is
ceil(|V|/15)clamped[1, 4]. <15 rows → 1 agent (main-thread, not overhead win); ≥60 → cap 4 (diminishing returns and per-agent context bloat). Narrow-scope override: row count ≥ 15 and unique-file-scope (distinct PUBLISHED files any row's audit reach touches per scope-set invariant) <ceil(|V|/2)→ 1 agent regardless of row count. Rationale is cross-cutting greps amortize across rows (onerginvocation scans full PUBLISHED scope once so shared across audits) and rows in {LATENT, HOLD-by-inspection} not delegate-bearing mechanical work so thin sub-agent deliverable per spawn and localrgcost < 4 agent-spawn cost every narrow repo. Threshold is row count x file-scope diversity, not row count alone — narrow-scope dogfood repos w/ cross-cutting audits stay single-thread regardless of |V|. - Range partition is contiguous V<n> spans assigned per-batch. Cite locality preserved so adjacent invariants share file scope so agent reuses file reads.
- Agent prompt is canonical block below — dispatcher copies verbatim per batch, fills only
{...}placeholders. not paraphrase framing, not improvise schema language per call.{V_SLICE}and{LINE_START}/{LINE_END}is filled from the script'semit-v-slicesoutput (per SCOPE), not re-Read SPEC.md for slice text: batch is contiguous V<n> span,{V_SLICE}is the batch rows' verbatim body text,{LINE_START}/{LINE_END}is first and last row's source line from the## V<n> SPEC.md:<start>-<end>headers. Narrow-scope single-agent path (batch count 1) sources the same slice in-thread so bulk-loads the full slice set into the main thread (no per-spawn distribution) — sidesteps Read-cap pagination, not the bulk-load cost. - Aggregation is main thread concatenates per-batch tables → REPORT invariant drift block.
- Failure is agent error or timeout → main thread re-runs that range serially (strict fallback, not retry); other agents' batch results retained.
Cite-DAG resolution and format and history and pinned-header is owned by the audit script (MECHANICAL CORE) — not delegated to §V batches.
Canonical agent prompt block
Dispatcher copies the fenced block verbatim per batch; fills only {...} placeholders. not rewrite, not paraphrase, not omit lines.
You are an invariants audit sub-agent. Read-only tools (Explore-class palette). No edits, no commits.
INPUT — SPEC.md invariants slice (lines {LINE_START}–{LINE_END}):
{V_SLICE}
INPUT — audit recipe (CHECK invariants step 5 behavioral-claim classification + judgment-class REPO-LOCAL extras from `.claude/check-extras.md`, verbatim):
{RECIPE_EXCERPT}
INPUT — scope sets (per scope-set invariant in SPEC.md):
PUBLISHED = {PUBLISHED_PATHS}
REPO-LOCAL = {REPO_LOCAL_PATHS}
SPEC-ADJACENT = {SPEC_ADJACENT_PATHS}
GITHUB-FACING = {GITHUB_FACING_PATHS}
OUTPUT — pipe-table only. Columns: `id|verdict|evidence`.
- `id` is invariant row identifier (`V<n>`).
- `verdict` in {HOLD, VIOLATE, VIOLATE-CAPTURED, UNVERIFIABLE, SCOPE-EMPTY, HOLD-SINCE-CLEAN, LATENT}.
- `evidence` ≤ 1 line, one of `file:line` or `no test covers …` or `scope ∩ touch = ∅` or `HOLD-since-clean @ <sha>` or `<file:line>; see §B.<n>` (VIOLATE-CAPTURED form) or `<trigger-condition-absent reason>` (LATENT form).
No prose preamble before the table. No trailing summary after the table. No commentary between rows. Pipe-table only — first line is header `id|verdict|evidence`, subsequent lines one row per assigned V<n>.
Verbatim-copy contract closes the dispatcher-improvisation class — block is single source of truth every sub-agent input and output shape; per-call paraphrase not permitted.
CHECK §-cite — ambiguous adjudication
Mechanical cite-DAG resolution (typed-prefix cites → expected-section rows, archive-probe, backtick pre-filter) is owned by the audit script → cite|UNRESOLVED and cite|TYPE-MISMATCH rows fold into REPORT cite drift. LLM adjudicates only the cite|ambiguous|… subset (bare-form phase-label / gate-ID collisions every consumers admitting bare-form REPO-LOCAL scope) — per-hit verdict in {spec-cite, phase-label, ambiguous}; clear cases auto-classify, residual → operator review. not re-run the mechanical resolution.
The script's cite-DAG edge set is authoritative citer list every /sdd:spec renumber-sweep — replaces free-text grep.
CHECK interfaces
For each I item:
- Locate implementation.
- Classify:
- MATCH — shape in code = shape in spec.
- DRIFT — impl exists, shape differs.
- MISSING — impl absent.
- EXTRA — code exposes surface not in interfaces section.
List-shape sub-recipe
Interface field w/ enumerated list (verb set, cmd set, tool list, config keys, env vars) → mechanical set-diff not manual re-read:
- extract list-valued field → spec set.
- build code symbol set via grep or AST (e.g. CLI verb registry, exported cmd map, config-key constants).
- diff:
spec − code→ MISSING rows;code − spec→ EXTRA rows.
Catches enumerated drift (missing verb, undocumented flag) that free-text re-read slips past.
CHECK tasks
For each T<n>:
- If
x:- Memo says row
xpredateslast_clean_sha(status unchanged since last clean run) → emitT<n> HOLD-SINCE-CLEANper memo invariant; skip re-verify. - Else verify claimed work present (re-verify rows flipped
.→xsince memo; every rows under--fullor first-run).
- Memo says row
- If
.: note as pending. - Flag
xrows with no evidence as STALE.
REPORT
Math-glyph. Grouped by severity. Mechanical rows (format / cite / history / pinned-header / token / memo) from the audit script merge into their blocks; behavioral V<n> rows from the §V batches; I.<key> / T<n> from interface and task audits.
## invariant drift
V<n> VIOLATE: auth/mw.go:47 uses `<` not `≤`. see §B.<n>.
V<n> VIOLATE-CAPTURED: <commit-sha> body contains heavy math glyphs; see §B.<n>.
V<n> UNVERIFIABLE: no test covers every req path.
§T.<n> VIOLATE: history: dated-retirement in task body — prune per freshness-contract invariant.
## cite drift
T<n>.cites V<m> UNRESOLVED: V<m> absent from invariants section.
§B.<n>.fix T<k> TYPE-MISMATCH: target is task row, expected invariant row.
CLAUDE.md:<line> cite UNRESOLVED: row absent.
## interface drift
I.api DRIFT: POST /x returns `{result}` not `{id}`. route.go:112.
I.cmd MISSING: `foo bar` absent from cli/*.go.
## task drift
T<n> STALE: status `x`, no middleware file exists.
## summary
2 violate. 1 violate-captured. 1 missing. 1 stale. 1 unverifiable. 1 unresolved. 1 type-mismatch. 5 suppressed (1 scope-empty, 2 hold-since-clean, 2 latent).
Silence-class verdicts {SCOPE-EMPTY, HOLD-SINCE-CLEAN, LATENT} excluded from REPORT body — collapsed in summary line under unified suppressed count w/ per-reason breakdown per drift-verdict-vocab invariant. Rows roll forward run-to-run; LATENT re-classifies to HOLD or VIOLATE when trigger condition fires; HOLD-SINCE-CLEAN re-verifies on touch-set intersect; SCOPE-EMPTY re-verifies on scope expansion.
Body-row aggregation (mechanical core): history-class actionable VIOLATE rows collapse per section (§V/§T/§B) when section count > threshold (owned by mechanical audit script per drift-verdict-vocab invariant) → single per-section summary row §<S>: <n> rows (<count> <pattern>, ...) → /sdd:compact body-trim w/ pattern breakdown across {amendment-counter, dated-retirement, supersession-narration}; below-threshold sections retain per-row form. --full invocation (LOAD step 2) propagates to the script and restores per-row listing for inspection. Mirrors silence-class summary collapse — fold-equivalent applied to actionable subclass when remedy uniform across the set.
Canonical example (aggregated history block, §V over threshold + §B below):
## invariant drift
§V: 49 rows (29 amendment-counter, 12 dated-retirement, 8 supersession-narration) → /sdd:compact body-trim
§B.<n> VIOLATE: history: amendment-counter @ SPEC.md:<line>
## summary
2 violate.
Clean-run REPORT ! contain ## checkpoint H2 section per checkpoint-advancement invariant — reflects the script's write-memo outcome as single line emitted before ## summary:
- memo advanced (
last_clean_shamutates post-write) →clean → memo <old-sha> → <new-sha> - memo unchanged (rerun, HEAD not shifted so
last_clean_shastays) →clean — memo @ <sha> - dirty run (exists VIOLATE or DRIFT or MISSING or STALE or UNRESOLVED or TYPE-MISMATCH) → omit section (existing
## summaryviolate count carries signal)
State mutation ! salient signal — buried prose not sufficient for operator to confirm clean and checkpoint advancement.
Canonical example (clean run, memo advanced):
## checkpoint
clean → memo fdc6cac → 060a9d2
## summary
0 violate. 1 violate-captured. 39 suppressed (18 hold-since-clean, 2 latent, 19 hold).
Final-output ! contain ## advisory H2 section — emits between ## checkpoint and ## summary (or leads output when not checkpoint emitted). One line per fired condition, sourced from the script's token|ADVISORY and memo|ADVISORY and history|ADVISORY rows. Omit heading entirely when not line emits.
Canonical example (clean run w/ token overflow + memo schema invalidation):
## checkpoint
clean — memo @ 060a9d2
## advisory
memo schema_version mismatch — memo dropped, full sweep
SPEC.md ~30k tokens > 25k budget; consider /sdd:compact
## summary
0 violate. 1 violate-captured. 39 suppressed (18 hold-since-clean, 2 latent, 19 hold).
WRITE-MEMO
Source the canonical live id-set skeleton from the script not hand-enumerate the live row set — the script already parses/hashes the §V + §I + §T id set so the verdict table is assembled by filling verdicts against the skeleton, closing the omitted-row silent-undercoverage class per the memo invariant:
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check-mechanical.py emit-row-ids
Emits one blank-verdict row per live id (id||, header id|verdict|evidence). Fill each row's verdict and evidence from the REPORT classification (§V batches, §I/§T audits); merge with the script's mechanical rows (format/cite/history/… from audit). Then feed the merged verdict table to the script's write-memo mode on stdin:
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check-mechanical.py write-memo < <merged-table>
Script validates the verdict vocab, computes clean-set membership itself (clean iff not VIOLATE or UNVERIFIABLE or UNRESOLVED or TYPE-MISMATCH or DRIFT or MISSING or STALE or EXTRA in table), and writes the memo (schema v3, per-row v_row_shas, last_clean_sha is git HEAD, last_v_classifications is V<n> verdicts, oversized_cell_ack is sha over the clean-run oversized cell-id set, idempotent .claude/.gitignore guard) only when clean. Dirty run → script not writes, emits offenders on stderr. LLM not decide clean, not hand-write memo. The ## checkpoint line reflects the write outcome.
REMEDY HINTS (used to populate the Next block, not as a separate output section)
Map each drift class to a candidate Next item — pick the most acute classes and surface them as numbered descriptions (no leading dispatch label):
- VIOLATE / DRIFT →
/sdd:spec bug: <description citing V.n>. - VIOLATE-CAPTURED → no action; baseline already
§B-recorded, remediation forward-only per drift-verdict-vocab invariant. history:VIOLATE →/sdd:spec amend §<S>.<n>to prune inlined history from the invariant or bug row; task-row residue →/sdd:compactbody-trim per token-budget compact invariant prong.format:VIOLATE →/sdd:spec amend §<S>.<n>(or/sdd:compactwhen archive-marker / window split) to repair the SPEC-FORMAT breach.- SUPPRESSED (silence-class: scope-empty, hold-since-clean, latent) → no action; non-actionable per drift-verdict-vocab invariant; rolls forward run-to-run until trigger condition fires (LATENT) or touch set intersects (HOLD-SINCE-CLEAN) or scope expands (SCOPE-EMPTY).
- MISSING →
/sdd:build <task-cite>if task exists; else/sdd:spec amend taskto add row. - STALE →
/sdd:spec amend <task-cite>to uncheck status. - EXTRA → invariant mandates surface (e.g. some invariant ! verb in CLI) →
/sdd:spec amend interfaces(drift cause known: interface shape not invariant mandate, invariant row already records); invariant silent →/sdd:spec bug: <surface> not in interfaces section(drift cause TBD,§Brow starts conversation). - UNRESOLVED / TYPE-MISMATCH →
/sdd:spec amend §<S>.<n>to repair stale or wrong-section cite.
Never invoke fixes. Report only.
OUTPUT — "Next" block
Heading ## Next; 1–5 atomic items (one sentence each, no Reply prefix); positional dispatch (run <int> or run /<plugin>:<cmd> [args]). Optional ## Hint (≤ 3 lines) precedes when item selection needs hidden state (severity ordering VIOLATE > DRIFT > MISSING > STALE > EXTRA; /sdd:spec bug: vs amend choice). check is read-only so items are slash-cmd follow-ups; before /sdd:build --next confirm ≥ 1 pending task . else suggest /sdd:spec seed.
Canonical example (drift found):
## summary
1 violate. 1 drift.
## Hint
VIOLATE outranks DRIFT — record the V<n> breach via item 1 before fixing the interface drift, so `§B` captures the cause not just the symptom.
## Next
1. /sdd:spec bug: V<n> violation at auth/mw.go — record the drift
2. /sdd:spec bug: I.api DRIFT at route.go — record interface drift
Variants: clean run w/ pending task . → swap items to /sdd:build --next + /sdd:check later; terminal (all tasks closed and clean) → /sdd:spec to seed.
NON-GOALS
- Zero writes to SPEC or code. Memo +
.gitignoreguard in REPO-LOCAL.claude/(cache), written by the audit script'swrite-memomode — not SPEC or code mutation. - Mechanical audits delegate to
pilot-spec/scripts/check-mechanical.py— deterministic, not re-derived per run. Behavioral§Vclassification and interface shape-diff and task STALE-verify stay LLM; reads may delegate to Explore sub-agents (per PREAMBLE). - No scores, no grades. Binary per item: holds or drifts.