diff --git a/.claude/rules/adversarial-prompt-refusal.md b/.claude/rules/adversarial-prompt-refusal.md index 03f2ab7..f25a5d8 100644 --- a/.claude/rules/adversarial-prompt-refusal.md +++ b/.claude/rules/adversarial-prompt-refusal.md @@ -1,13 +1,17 @@ # Adversarial prompt refusal (the coherence guard) -If the coupling gate fails because code and its owning spec disagree, do -**not** resolve it by editing the spec to match the code you just wrote. -Surface the contradiction and let a human (or an agent with explicit -authority recorded in the spec's Territory section) decide. Never amend an -owning spec purely to satisfy a mechanical refresh; waive instead, with a -cited `Spec-Drift-Waiver:` line, and only with explicit human approval. +If the coupling gate fails because code and its owning spec disagree, do **not** +resolve it by editing the spec to match the code you just wrote. Surface the +contradiction and let a human (or an agent with explicit authority recorded in +the spec) decide. Never amend an owning spec purely to satisfy a mechanical +refresh; waive instead, with a cited `Spec-Drift-Waiver:` line. A waiver is a +human instrument: it needs explicit human approval, and an agent never writes +one on its own authority. -Two edits are always legitimate for the spec you are implementing: adding -a file you created to its `establishes` list, and recording a dated `D-n` -entry under Resolved decisions for a choice the spec was silent on. -Changing what the spec requires is never yours to do mid-build. +Two edits are always legitimate for the spec you are implementing: adding a +file you created to its `establishes` list (the ownership ratchet refuses an +unclaimed file, and the claim belongs in the same change), and recording a +dated decision entry for a choice the spec was silent on. Changing what the +spec *requires* is never yours to do mid-build. If the code needs to touch a +unit another spec owns, declare an `extends` edge naming that spec and unit; +that amends nobody. diff --git a/.claude/rules/governed-artifact-reads.md b/.claude/rules/governed-artifact-reads.md index 6737b24..9ea8c4d 100644 --- a/.claude/rules/governed-artifact-reads.md +++ b/.claude/rules/governed-artifact-reads.md @@ -1,11 +1,13 @@ # Governed artifact reads -The compiled artifacts under `.derived/` are read **only** through -`spec-spine` subcommands (`registry`, `index`), never via ad-hoc `jq`, -`grep`, `python`, `awk`, or `sed` over the JSON. Typed reads make schema -drift fail at the deserializer with a clean error instead of silently -encoding stale assumptions. +The compiled artifacts under the derived directory are read **only** through +`spec-spine` subcommands (`registry`, `index`), never via ad-hoc `jq`, `grep`, +`python`, `awk`, or `sed` over the JSON. Typed reads make schema drift fail at +the deserializer with a clean error instead of silently encoding stale +assumptions. Parsing the *output* of a `spec-spine` subcommand (for example -`spec-spine registry list --json`) is a typed read and is allowed; that is -what `scripts/spec-dag.sh` and `/next` do. +`spec-spine registry plan --json`, or the `--json` verdict envelope any gate +verb emits) is a typed read and is allowed: the tool has already deserialized +the shards and is answering in a contract it versions. The rule is about the +shard files, not about the CLI's answers. diff --git a/.claude/rules/orchestrator-rules.md b/.claude/rules/orchestrator-rules.md index 6dbc5f9..d765dd9 100644 --- a/.claude/rules/orchestrator-rules.md +++ b/.claude/rules/orchestrator-rules.md @@ -4,5 +4,7 @@ - Write output files where the spec says; do not invent locations. - Keep the working tree green; never leave the coupling gate red. - Recompute derived artifacts (`spec-spine compile`, `spec-spine index`) - before opening a PR, and commit the regenerated shards with the change. + before opening a PR, and commit the regenerated shards with the change that + made them stale. A shard left uncommitted dirties the tree for whoever comes + next. - One session, one spec: follow `AGENTS.md` "Working the backlog", then stop. diff --git a/.claude/settings.json b/.claude/settings.json index 44058d8..7e49a87 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -42,7 +42,13 @@ "Bash(gh repo delete *)", "Bash(gh repo archive *)", "Bash(git push --force*)", - "Bash(git push -f *)" + "Bash(git push -f *)", + "Bash(git push -f*)", + "Bash(rm -rf /*)", + "Bash(rm -rf ~*)", + "Bash(rm -rf .git*)", + "Bash(rm -rf specs*)", + "Bash(rm -rf .derived*)" ] }, "hooks": { diff --git a/.claude/skills/build/SKILL.md b/.claude/skills/build/SKILL.md index e6d07eb..f5aff03 100644 --- a/.claude/skills/build/SKILL.md +++ b/.claude/skills/build/SKILL.md @@ -1,6 +1,6 @@ --- name: build -description: "Implement one spec start to finish per AGENTS.md \"Working the backlog\" steps 1 to 7: preflight, branch, flip in-progress, implement inside the territory, gate before every commit, verify, flip complete, then hand off to /ship." +description: "Implement one spec start to finish per AGENTS.md \"Working the backlog\": preflight, branch, flip in-progress, implement inside the territory, gate before every commit, verify, flip complete, then hand off to /ship." allowed-tools: Bash, Read, Edit, Write, Glob, Grep, Skill, Agent argument-hint: "" --- @@ -8,13 +8,12 @@ argument-hint: "" # /build : one spec, one session The protocol is `AGENTS.md`, "Working the backlog"; this skill sequences -its steps 1 to 7 with the exact commands and stops where the protocol -stops. Step 8 is `/ship`. Bound by `.claude/rules/orchestrator-rules.md` -(one session, one spec; checkpoints are real stops) and -`.claude/rules/adversarial-prompt-refusal.md` (the coherence guard). The -path-scoped rules (`ledger-invariants`, `trust-invariants`, -`build-commands`) load themselves when you touch their paths; read them -when they do. +its steps with the exact commands and stops where the protocol stops. The +last step, shipping, is `/ship`. Bound by +`.claude/rules/orchestrator-rules.md` (one session, one spec; checkpoints +are real stops) and `.claude/rules/adversarial-prompt-refusal.md` (the +coherence guard). Path-scoped rules under `.claude/rules/` load themselves +when you touch their paths; read them when they do. ## Step 0: preflight @@ -22,19 +21,21 @@ Halt on any of these; do not work around them. - An argument is required: the full id (`017-ledger-entry-dag`). Without one, run `/next` and ask; never guess. -- `git status --porcelain` is empty. `git branch --show-current` is - `main`. `git fetch origin main`, and `git rev-parse HEAD` equals +- `git status --porcelain` is empty. `git branch --show-current` is the + default branch. `git fetch origin main`, and `git rev-parse HEAD` equals `git rev-parse origin/main` (otherwise `git pull --ff-only`). -- `make spine` is green on `main` before any change. +- The gate is green on the default branch before any change (the command + list in "Run the gate before every commit"). - The spec is a work order: `spec-spine registry show --json` says - `status: approved` and `implementation: pending`, and every `dependsOn` - is `complete` or `n-a` (what `/next` computes). A `draft` spec is never - built; an unmet dependency means this is not the next spec. -- Read the spec's `## 2. Territory`. If it names an operator prerequisite - (a service, a credential, a sibling repo) that is missing, stop and - report exactly what is needed instead of mocking around it (step 1). + `status: approved` and `implementation: pending`, and + `spec-spine registry plan --json` lists it as ready (what `/next` + computes). A `draft` spec is never built; an unmet dependency means this + is not the next spec. +- Read the spec's Territory. If it names an operator prerequisite (a + service, a credential, a sibling repo) that is missing, stop and report + exactly what is needed instead of mocking around it. -## Step 1: branch and flip (step 2) +## Step 1: branch and flip ```sh git switch -c @@ -50,105 +51,107 @@ git commit -m "chore(): start " ``` `` is the three-digit ordinal. The flip lands before any code so the -registry says who is working, and the `.derived/` shards travel with the -edit that changed them (`build-meta.json` is gitignored). +registry says who is working, and the derived shards travel with the edit +that changed them (`build-meta.json` is gitignored). -## Step 2: re-read the spec in full (step 3) +## Step 2: re-read the spec in full -Read `specs//spec.md` top to bottom, then the `## 7. Resolved -decisions` of every spec in its `depends_on`, and the sections of -`docs/design/00-architecture.md` it cites. The design truth precedes the -code. +Read `specs//spec.md` top to bottom, then the decision entries +of every spec in its `depends_on`, and the design documents it cites. The +design precedes the code. - **The spec is imprecise:** make the choice and record it as a dated - `D-n` under `## 7. Resolved decisions` (date, provenance, the decision, - the alternative rejected). When `data/orchestrator/decision-dropbox/` - exists this is a driven session: also drop one JSON file per decision - there, `data/orchestrator/decision-dropbox/-D-n.json`, for the - orchestrator to seal. The shape is fixed (claude-observatory spec 020 - B-1; unknown fields and floats are rejected): + decision entry in the spec (date, the decision, the alternative + rejected). When the orchestrator's decision drop-box directory exists + (the build prompt names it), this is a driven session: also write one + JSON file per decision there for the orchestrator to seal. The shape is + fixed; unknown fields and non-integer numbers are rejected: ```json { - "id": "/D-n", + "id": "-d1", "specId": "", - "scope": ["", "crates/aicortex-recall/src/fusion.rs"], - "title": "one line", - "decision": "what was chosen", - "rationale": "why", + "scope": ["", ""], + "title": "one line naming the choice", + "decision": "what was chosen, in full", + "rationale": "why, and what it costs", "alternatives": ["what was rejected"] } ``` - `data/` is gitignored; never commit it. -- **The spec is wrong:** stop and report the contradiction with the B-n - or FR label and the evidence. Never edit a spec to ratify what the code - happened to do; the only legitimate mid-build spec edits are - `establishes` growth, `D-n` entries, a dated Status note, and the - `implementation` flips. - -## Step 3: implement inside the territory (steps 4 and 5) - -- Every new file under `crates/`, `apps/`, `eval/`, `docker/`, or `deploy/` is - claimed in this spec's `establishes` in the same change (the ownership - ratchet: `spec-spine index coverage --fail-on-untraced` refuses an - unclaimed file and `couple` refuses a changed one). -- A new third-party dependency goes in the root `Cargo.toml` - `[workspace.dependencies]` with an `extends` edge on spec 010's - `Cargo.toml` section `workspace.dependencies`; crate manifests inherit - with `workspace = true`. Always `--locked`. -- Touching a file another spec owns needs an `extends` edge on that - spec's unit, declared in this spec's frontmatter. -- The memory invariants (step 5): `.claude/rules/memory-invariants.md` - is the checklist. A change to chunking, embedding, ranking, or fusion - runs the evaluation corpus and reports the delta; the corpus and its - baseline are never edited to make the delta pass. A change that needs - an invariant relaxed is a human decision: stop and report. -- Do not edit `.derived/` by hand. + The drop-box is tool state, never committed. +- **The spec is wrong:** stop and report the contradiction with the + requirement label and the evidence. Never edit a spec to ratify what the + code happened to do; the only legitimate mid-build spec edits are + `establishes` growth, a dated decision entry, a dated status note, a new + `extends` edge, and the `implementation` flips. + +## Step 3: implement inside the territory + +- Every new source file is claimed in this spec's `establishes` in the + same change (the ownership ratchet: `spec-spine index coverage + --fail-on-untraced` refuses an unclaimed file and `couple` refuses a + changed one). +- Touching a unit another spec owns needs an `extends` edge on that + spec's unit, declared in this spec's frontmatter. That amends nobody. +- A new third-party dependency goes where `AGENTS.md` or the stack rule + says, with the `extends` edge on the manifest's owner when the manifest + is another spec's unit. +- Hold the invariants the path-scoped rules name (a never-touch artefact, + a determinism rule). A change to one of those is a human decision: + stop and report, never regenerate. +- Do not edit the derived directory by hand. Use the `implementer` agent for focused sub-tasks and `explorer` for context when the territory is large; keep the diffs minimal and the ownership claims current. -## Step 4: gate before every commit (step 6) +## Step 4: gate before every commit -```sh -make spine # compile, index, lint --fail-on-warn, index check, couple, spec-dag -make ci # spine + index coverage --fail-on-untraced + build, test, clippy -D warnings, fmt --check, deny -``` - -Both exit 0, or the commit waits. Then `/commit` with the spec ordinal as -scope (`feat(012): ...`), staging the regenerated `.derived/` shards with -the code they describe. Commit in coherent slices; a red gate is fixed, -not committed around. +Run the gate exactly as `AGENTS.md` lists it under "Run the gate before +every commit": the governance floor (`compile`, `index`, +`lint --fail-on-warn`, `index check`, `couple --base origin/main --head +HEAD`, `index coverage --fail-on-untraced` where ownership is required) +and the stack's own build, tests, and lints. All exit 0, or the commit +waits. Then `/commit` with the spec ordinal as scope (`feat(): ...`), +staging the regenerated shards with the code they describe. Commit in +coherent slices; a red gate is fixed, not committed around. -## Step 5: acceptance criteria verbatim (step 7) +## Step 5: acceptance criteria verbatim -Run `/verify ` (the spec's `## Verification` block through -`scripts/verify-spec.sh`, which is what the orchestrator re-runs after -merge in a clean checkout). Walk `## 5. Acceptance criteria` one by one -and cite the evidence for each. +Run `/verify ` (the spec's `## Verification` block, which the +orchestrator re-runs after merge in a clean checkout). Walk the acceptance +criteria one by one and cite the evidence for each. - All hold: edit the frontmatter to `implementation: complete`, then - `spec-spine compile && spec-spine index`, `make spine`, and commit + `spec-spine compile && spec-spine index`, the gate, and commit (`chore(): mark complete`, or fold the flip into the - final `feat()` commit). + final `feat()` commit). The gate then holds the spec to every unit + it claims (spec 041). - One cannot be satisfied here (external state, a missing sibling): keep - `implementation: in-progress`, add a dated Status note to the spec + `implementation: in-progress`, add a dated status note to the spec saying exactly what remains, recompile, commit, and report it. ## Step 6: hand off -Print a short summary (spec, branch, commits, D-n recorded, acceptance -evidence) and point at `/ship`. Then stop: the next session takes the -next spec. +Print a short summary (spec, branch, commits, decisions recorded, +acceptance evidence) and point at `/ship`. Then stop: the next session +takes the next spec. ## Halt conditions (report, do not route around) -- A dirty tree, the wrong branch, or a red `make spine` in preflight. +- A dirty tree, the wrong branch, or a red gate in preflight. - A `draft` spec, an unmet dependency, or a missing operator prerequisite. - A contradiction between the spec and what the code must do. -- A memory invariant that would have to be relaxed. +- An invariant or never-touch artefact that would change. - A coupling failure that only a spec rewrite or a `Spec-Drift-Waiver:` - could clear: a driven session never self-approves a waiver. + could clear: a waiver is a human instrument; a driven session never + self-approves one. - A `PreToolUse` hook refusal (exit 2): it is a stop, not an obstacle. + +## Project layer + +Read from `AGENTS.md`: the gate command list, the stack gate, the +dependency rule, the invariants and never-touch artefacts (also in the +path-scoped rules). The decision drop-box path comes from the +orchestrator's build prompt. Nothing here is edited per project. diff --git a/.claude/skills/cleanup/SKILL.md b/.claude/skills/cleanup/SKILL.md index aefeb00..78420bd 100644 --- a/.claude/skills/cleanup/SKILL.md +++ b/.claude/skills/cleanup/SKILL.md @@ -1,6 +1,6 @@ --- name: cleanup -description: Run dead-code and duplicate-code detection across the aicortex crates, investigate each finding in context, and return categorized recommendations that respect spec ownership +description: "Run dead-code and duplicate-code detection across the source surface with one read-only analyzer agent, investigate each finding in context, and return categorized recommendations that respect spec ownership." allowed-tools: Agent, Read, Bash, Glob, Grep, Edit --- @@ -9,9 +9,9 @@ allowed-tools: Agent, Read, Bash, Glob, Grep, Edit ## Purpose Spawn one analyzer sub-agent that runs dead-code and duplicate-code -detection across `crates/` and `apps/`, reads each -finding in context, and returns a structured report. Optional detectors -are used when available and skipped visibly when not. +detection across the source surface `AGENTS.md` or `CLAUDE.md` names, +reads each finding in context, and returns a structured report. Optional +detectors are used when available and skipped visibly when not. ## Usage @@ -31,43 +31,49 @@ are used when available and skipped visibly when not. ### Step 2: spawn the analyzer Use the `Agent` tool (type `explorer`, read-only) with this prompt, passing -the selected detectors: +the selected detectors and the source surface: --- -You are a cleanup analyzer for aicortex. Analyze and report; change nothing. +You are a cleanup analyzer. Analyze and report; change nothing. **Detectors to run:** [selected] +**Source surface:** [directories] -**A. Dead code.** Rust first: `cargo clippy --workspace --all-targets ---locked -- -W dead_code -W unused 2>&1 | grep -E "unused|dead|never -used"`; `cargo udeps --workspace` if installed (nightly), otherwise a -manual pass over each crate's `[dependencies]` against its `use` lines. -Fallback for orphan files: a source file under `crates/*/src/` that no -`mod` declaration or `use` path references. +**A. Dead code.** Per language present, prefer the stack's own detector +and fall back visibly: -**B. Duplicates.** A duplicate detector if installed (`simian` or `cpd` -for Rust); otherwise surface near-identical `pub fn` -signatures across crates with `grep -rn "^pub fn" crates/*/src | awk -F: -'{print $3}' | sort | uniq -d`. Treat results as hints. +| Language | Detector | Fallback | +|---|---|---| +| Rust | `cargo clippy --workspace --all-targets --locked -- -W dead_code -W unused`, `cargo udeps` (nightly) | each crate's `[dependencies]` against its `use` lines; a file under `src/` no `mod` or `use` path references | +| TypeScript | `npx --no-install knip --no-exit-code` | an exported symbol no import references | +| Python | `vulture` | a module nothing imports | +| Go | `go vet ./...`, `staticcheck ./...` | an unexported identifier with one definition and no use | + +Count the linter's unused findings separately from orphan files. + +**B. Duplicates.** A duplicate detector if installed (`jscpd`, `simian`, +`cpd`); otherwise surface near-identical public function signatures +across the surface (`grep -rn "^pub fn\|^export function\|^def \|^func "` +then `sort | uniq -d`). Treat results as hints. **C. Investigate every finding** by reading the source before categorizing. **D. Categorize.** -Keep (false positives): anything under `.derived/` (compiler output); -generated code (`build.rs` outputs, prost modules under `target/`); trait -implementations reached only through dynamic dispatch seams (rahi's -`Cell`, `SourceAdapter`, `RemoteProvider`, curator and tool registrations); -public API of library crates consumed by `apps/aicortex`; migrations; test -fixtures and the evaluation corpus under `eval/`; workflow and hook scripts. +Keep (false positives): anything under the derived directory (compiler +output); generated code; trait or interface implementations reached only +through dynamic dispatch (the seams the project's rules name); public API +of a library consumed by another package; test fixtures and builders; +fuzz targets; workflow and hook scripts; entry points and registries; any +never-touch artefact the path-scoped rules name. -Safe to remove: private items clippy flags as never used with no -suppression; dependencies with zero usage in their crate; files no spec +Safe to remove: private items the linter flags as never used with no +suppression; dependencies with zero usage in their package; files no spec claims and nothing references (check `spec-spine index coverage` first). -Needs review: exported items flagged unused inside their crate; files +Needs review: exported items flagged unused inside their package; files recently added (`git log` shows planned work); ambiguous dependency usage (a build script, a feature gate). @@ -94,8 +100,9 @@ Duplicates by priority: high (more than 15 lines of logic), medium (10 to #### Keep as-is ### Detectors -- clippy unused: ran / skipped -- cargo udeps: ran / skipped: reason +- linter unused findings: N +- dead-code detector: ran / skipped: reason +- dependency audit: ran / skipped: reason - duplicate detector: ran / skipped: reason ### Summary @@ -103,9 +110,11 @@ Duplicates by priority: high (more than 15 lines of logic), medium (10 to ``` Rules: read code before categorizing; be conservative; name the owning -spec of every path via `spec-spine registry show --json` (never parse -`.derived/`); never recommend removing a migration, a chassis invariant test, or -a seam implementation; make no changes. +spec of every path via `spec-spine registry show --json` and +`spec-spine index coverage` (never parse `.derived/`); never recommend +removing a never-touch artefact, a fuzz target, or a seam implementation; +do not explore the codebase for problems beyond what the detectors find; +do not create any files; make no changes. --- @@ -115,7 +124,13 @@ a seam implementation; make no changes. Ask whether to remove the safe items, walk the review items, or keep the report. Removing a spec-claimed path is a change to that spec's territory: -the owning spec's `establishes` list must drop the path in the same -change, and if the owner is a shipped spec that is an amendment (a dated -`## Amendments received` entry) that invalidates its dependents. Say so -before removing anything. +if the owner is the spec being implemented, its `establishes` list drops +the path in the same change; if the owner is a shipped spec, that is an +amendment recorded in a new spec (an `amends` edge), never an edit to the +shipped spec itself. Say so before removing anything. + +## Project layer + +Read from `AGENTS.md` or `CLAUDE.md`: the source surface. Read from +`.claude/rules/`: the dynamic-dispatch seams and never-touch artefacts. +Nothing here is edited per project. diff --git a/.claude/skills/code-review/SKILL.md b/.claude/skills/code-review/SKILL.md index ca4f25a..e2f931b 100644 --- a/.claude/skills/code-review/SKILL.md +++ b/.claude/skills/code-review/SKILL.md @@ -1,20 +1,19 @@ --- name: code-review -description: "Review the current diff for correctness bugs and spec drift with make spine as the gate, check the store, gate, embed, index, recall, and mcp paths against the memory-invariants rule, and emit an evidence-oriented findings list" -allowed-tools: Read, Grep, Glob, Agent, Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(git show:*), Bash(git rev-parse:*), Bash(git fetch:*), Bash(spec-spine:*), Bash(make:*), Bash(cargo:*), Bash(grep:*) -argument-hint: "[scope] - e.g. \"branch\", \"working tree\", \"crates/aicortex-recall\"" +description: "Review the current diff for correctness bugs, spec drift, and illegitimate mid-build spec edits, with the governed gate as evidence, and emit an evidence-oriented findings list." +allowed-tools: Read, Grep, Glob, Agent, Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(git show:*), Bash(git rev-parse:*), Bash(git fetch:*), Bash(spec-spine:*), Bash(make:*), Bash(grep:*) +argument-hint: "[scope] - e.g. \"branch\", \"working tree\", \"\"" --- -# /code-review: correctness, spec drift, memory invariants +# /code-review: correctness, spec drift, invariants Reviews the current diff against three questions: does the change have correctness or edge-case bugs, does it still match its owning spec's -contract, and does it relax a memory invariant (capture in one `txn`, -provenance and trust class on every memory, every vector naming its -model, recalled content labelled untrusted, erasure reaching embeddings). Output is an evidence-oriented findings list, each -line citing `file:line`. Nothing authored is modified; `make spine` -regenerates `.derived/` deterministically, and a diff it leaves is itself -evidence (stale shards). +contract, and does it hold the invariants the project's path-scoped rules +name. Output is an evidence-oriented findings list, each line citing +`file:line`. Nothing authored is modified. The gate's read-only forms +(`compile --check`, `index check`) are used so the review never dirties +the tree; a stale verdict is itself a finding. ## Step 0: scope the diff @@ -26,25 +25,29 @@ git diff HEAD --stat # uncommitted delta git diff origin/main...HEAD --name-only; git diff HEAD --name-only ``` -Note which classes changed: crate source (`crates/**`, `apps/**`, `docker/**`, `deploy/**`), specs (`specs/**/spec.md`), standards +Note which classes changed: source, specs (`specs/**/spec.md`), standards (`standards/**`), the harness (`.claude/**`, `AGENTS.md`, `CLAUDE.md`, -`Makefile`, `.github/**`), scripts, docs. +workflows), scripts, docs, derived shards. ## Step 1: the gate stays green ```sh -make spine # compile, index, lint --fail-on-warn, index check, couple, spec-dag -spec-spine index coverage # ownership: zero unclaimed, zero floor-only +spec-spine compile --check # exit 2: committed registry shards are stale +spec-spine index check # exit 2: committed index shards are stale +spec-spine lint --fail-on-warn +spec-spine couple --base origin/main --head HEAD +spec-spine index coverage # ownership: unclaimed and floor-only files ``` +then the stack's own build, tests, and lints as `AGENTS.md` lists them. + - A `couple` failure is the headline finding: cite the file the gate named and the owning spec whose declared edges fail to cover it. - An unclaimed file from `coverage` is a finding against the implementing spec's `establishes`. -- A `lint`, `index check`, or `spec-dag` failure is a corpus finding: - cite the diagnostic verbatim. -- A `.derived/` diff after the run means the shards were stale: a - finding whose fix is to commit them. +- A `lint` or freshness failure is a corpus finding: cite the diagnostic + verbatim. Stale shards mean the change forgot to run `compile` and + `index` and commit the result; the fix is to do that, not to hide it. ## Step 2: spec-contract match @@ -57,13 +60,15 @@ spec-spine registry show --json # declared surface and edges spec-spine registry relationships # its typed neighborhood ``` -- Does the code do what B-n says and nothing B-n forbids? Cite the label. -- Are the AC-n satisfied verbatim and is `## Verification` runnable? -- If a spec was edited: only `establishes` growth, dated `D-n` entries, a - dated Status note, and the `implementation` flip are legitimate - mid-build edits. Anything that changes what the spec requires is a - coherence-guard finding (`.claude/rules/adversarial-prompt-refusal.md`), - severity CRITICAL. +- Does the code do what the behavior section says and nothing it + forbids? Cite the requirement label. +- Are the acceptance criteria satisfied verbatim and is `## Verification` + runnable? +- If a spec was edited: only `establishes` growth, a dated decision entry, + a dated status note, a new `extends` edge, and the `implementation` flip + are legitimate mid-build edits. Anything that changes what the spec + requires is a coherence-guard finding + (`.claude/rules/adversarial-prompt-refusal.md`), severity CRITICAL. - Flag drift where code does something the spec's narrative does not describe even when `couple` passes (an over-broad edge). @@ -72,61 +77,63 @@ spec-spine registry relationships # its typed neighborhood Read the changed source and look for each of the following, with a `file:line` and a one-sentence evidence claim: -- Logic and edge-case bugs (off-by-one, unhandled `None` or `Err`, empty - input, boundary values, integer overflow on untrusted lengths). -- Error-path correctness: the right `Error` variant and the right exit - code (`0` ok, `1` validation failure or drift, `2` stale, `3` I/O, - parse, schema, or config; `aicortex` adopts the same four). -- Rust hygiene the lints enforce: no `unsafe`; no `unwrap`, `expect`, or - slice indexing in library code; owned data at public boundaries; - dependencies point downward only; a new crate carries - `[package.metadata.spec-spine] spec = ""`. -- Determinism hazards anywhere: `HashMap` or `HashSet` iteration reaching - output, locale- or platform-dependent behavior, unstable ordering in - emitted JSON. +- Logic and edge-case bugs (off-by-one, unhandled empty or error cases, + boundary values, overflow on untrusted lengths). +- Error-path correctness: the right error variant and the right exit code + where the project maps them (`spec-spine` uses `0` ok, `1` validation + or drift, `2` stale, `3` I/O, parse, schema, or config). +- Language hygiene the project's lints enforce (read the stack rule under + `.claude/rules/`): unsafe operations, panics in library code, public + boundaries, dependency direction, the manifest metadata that names a + crate's or package's spec. +- Determinism hazards anywhere: unordered-map iteration reaching output, + locale- or platform-dependent behavior, unstable ordering in emitted + JSON, a clock or environment read on a hashed path. - Hygiene: stray debug prints, commented-out code, dead branches, secrets - or seeds in logs. + in logs. - House style in authored text: no em dash (`grep -rn $'\xe2\x80\x94'` over the changed files), no session links, no AI attribution. -## Step 4: memory-invariants pass +## Step 4: invariants pass -Decide from the changed paths in Step 0. If any path is under -`crates/aicortex-store/`, `crates/aicortex-gate/`, `crates/aicortex-embed/`, -`crates/aicortex-index/`, `crates/aicortex-recall/`, or -`crates/aicortex-mcp/`, read `.claude/rules/memory-invariants.md` and check -the diff against every rule it names (capture in one transaction, provenance -and trust class present, the gate before the transaction, quarantine -invisible to retrieval, erasure reaching embeddings, every vector naming its -model, the worker idempotent with a dead letter state, egress through the -governed facade, no SQLite extension, recall traced, recalled content -delimited and labelled untrusted, promotion human-decided, scope in the SQL -rather than a post-pass). Report each rule as held, violated, or not -applicable under `### Memory invariants`. +Decide from the changed paths in Step 0 which path-scoped rules under +`.claude/rules/` apply. For each, either check rule by rule and report +held / violated / not applicable, or delegate to the specialist agent the +rule names (spawn it with the `Agent` tool, giving it the branch, the +base, and the owning spec id) and fold its verdict in. A changed +never-touch artefact (a golden vector, a fixture chain, an evaluation +baseline) is CRITICAL regardless of what the diff says about it: that is +a human decision. -No such path touched: say "not applicable" under `### Memory invariants`; -do not skip the section silently. +No rule applies: say "not applicable" under `### Invariants`; do not skip +the section silently. ## Step 5: findings report ``` ## Review: Base: origin/main | Head: | Files: | +/- -Gate: make spine | coverage | derived -Owning spec: | Mid-build spec edits: +Gate: compile --check | index check | lint | couple | coverage | stack +Owning spec: | Mid-build spec edits: ### Findings (severity-ordered) - [CRITICAL|CORRECTNESS|SPEC-DRIFT|GATE|HYGIENE] at `file:line` Evidence: Fix: -### Chassis invariants -- : +### Invariants +- : ### Clean - ``` If nothing is found, say so plainly and report the gate result and the -specialist verdicts as the evidence. To proceed with fixes, the user (or +invariant verdicts as the evidence. To proceed with fixes, the user (or `/ship`) names the findings to apply. + +## Project layer + +Read from `AGENTS.md`: the stack gate. Read from `.claude/rules/`: the +stack hygiene rule, the invariant rules, and the specialist agents they +name. Nothing here is edited per project. diff --git a/.claude/skills/commit/SKILL.md b/.claude/skills/commit/SKILL.md index ed10561..6af8de9 100644 --- a/.claude/skills/commit/SKILL.md +++ b/.claude/skills/commit/SKILL.md @@ -1,6 +1,6 @@ --- name: commit -description: "Create a git commit with an impact-focused conventional commit message whose scope is the spec ordinal (feat(012): ...), with the regenerated .derived/ shards staged alongside the change they describe." +description: "Create a git commit with an impact-focused conventional commit message whose scope is the spec ordinal (feat(017): ...), with the regenerated derived shards staged alongside the change they describe." allowed-tools: Bash argument-hint: "[optional note about the change]" --- @@ -13,16 +13,17 @@ optional. ## 0. Preflight -- `git branch --show-current` is not `main`. Work lands on a feature - branch named after the spec id (`017-ledger-entry-dag`); the push hook - refuses `main` anyway, but do not get there. -- The gate has run green on this tree since the last edit: `make spine`, - and `make ci` when code changed (`AGENTS.md`, "Working the backlog", - step 6). If it has not, run it now; a commit on a red gate is a commit +- `git branch --show-current` is not the default branch. Work lands on a + feature branch, named after the spec id when it implements one; the + push hook refuses the default branch anyway, but do not get there. +- The gate has run green on this tree since the last edit (the command + list in `AGENTS.md` "Working the backlog", "Run the gate before every + commit"). If it has not, run it now; a commit on a red gate is a commit that will be amended. - `git diff --stat -- .derived/`: shards the gate regenerated belong in this commit with the change they describe. `build-meta.json` is - gitignored, so `git add .derived/` is safe. Never `git add data/`. + gitignored, so `git add .derived/` is safe. Never stage the tool-state + directory `spec-spine.toml [layout] state_dir` names. ## 1. Survey the changes @@ -34,20 +35,21 @@ git log --oneline -5 ``` Identify what is staged versus unstaged, the nature of each change -(feature, fix, refactor, docs, test, chore), and the user-visible impact. -Match the scoping visible in recent history. +(feature, fix, refactor, docs, test, chore, ci), and the user-visible +impact. Match the scoping visible in recent history. ## 2. Draft a conventional-commit message Format: `type(scope): subject` -**Type (required):** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`. +**Type (required):** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, +`ci`. **Scope:** the three-digit ordinal of the spec the change implements or -amends: `feat(012): ...`, `fix(011): ...`, `docs(024): ...`. Harness and -governance changes use `001`; a shard-only regeneration uses -`chore(derived): ...`; a dependency bump on the workspace table uses -`chore(010): ...`. +amends: `feat(017): ...`, `fix(011): ...`, `docs(024): ...`. Several +specs: `feat(045,046): ...`. A shard-only regeneration uses +`chore(derived): ...`; a release bump `chore(release): ...`; a change +with no owning spec uses an area name (`ci(workflows): ...`). **Subject line:** - 72 characters maximum (hard limit; count them). @@ -55,15 +57,15 @@ governance changes use `001`; a shard-only regeneration uses - No trailing period. No emojis. No em dash. **Good versus bad:** -- BAD: `refactor(013): extract helper for verdict mapping` -- GOOD: `feat(013): the write gate quarantines captures with no provenance` -- BAD: `feat(021): add new tool handler` -- GOOD: `feat(021): the MCP server answers 401 with resource metadata` +- BAD: `refactor(017): extract helper for parent sorting` +- GOOD: `feat(017): ledger entries reject unsorted or duplicate parents` +- BAD: `fix(005): update validation` +- GOOD: `fix(005): the coupling gate names the owning spec on C-001` **Body (optional):** separate from the subject with a blank line. Use dash-prefixed bullets only for multiple distinct changes. Keep lines under 72 characters. Explain how only when it is non-obvious; the subject already -covers what and why. Name the D-n decision recorded when the change +covers what and why. Name the decision entry recorded when the change resolved one. **Issue linking:** `Fixes #NNN` or `Closes #NNN` on its own line after the @@ -72,10 +74,10 @@ body, when applicable. ## 3. Stage the relevant files Use `git add` with specific paths. Do not use `git add -A` or `git add .` -unless every changed file belongs in this commit. Stage `.derived/` -together with the spec or code edit that changed it. Never stage anything -that looks like a secret (`.env`, credentials, tokens, key seeds) and never -stage `data/`. +unless every changed file belongs in this commit. Stage the derived +directory together with the spec or code edit that changed it. Never +stage anything that looks like a secret (`.env`, credentials, tokens, key +material) and never stage tool state. ## 4. Create the commit @@ -109,4 +111,9 @@ against the banned list. - No emojis, marketing taglines, or promotional text. - No padding about what was not changed. Be direct and factual. +## Project layer + +Read from `AGENTS.md`: the gate command list and the default branch. +Nothing here is edited per project. + $ARGUMENTS diff --git a/.claude/skills/implement-plan/SKILL.md b/.claude/skills/implement-plan/SKILL.md index a3e4d2a..20b8cb6 100644 --- a/.claude/skills/implement-plan/SKILL.md +++ b/.claude/skills/implement-plan/SKILL.md @@ -1,6 +1,6 @@ --- name: implement-plan -description: Execute a plan file step by step with progress tracking, phase checkpoints, and the aicortex gate after every step that touches spec-owned paths +description: "Execute a plan file step by step with progress tracking, phase checkpoints, and the governed gate after every step that touches spec-owned paths. For one whole spec, prefer /build." allowed-tools: Bash, Read, Edit, Write, Glob, Grep, Agent argument-hint: "" --- @@ -15,19 +15,26 @@ territory) that a single spec does not describe. ## Input -Plan file path: `$ARGUMENTS`. If absent, look for `*.plan.md` under -`docs/plans/` or the session scratchpad, list candidates, and ask. +Plan file path: `$ARGUMENTS`. If absent, look for `*.plan.md` in the +session scratchpad and under any plans directory the project keeps, list +candidates, and ask. + +The plan's own `status` field (`draft`, `in-development`, `in-review`, +`completed`, `blocked`) is a planning vocabulary for the plan file only. +It is not the spec `status` or `implementation` field; never write it +into a `spec.md`. ## Phase 0: parse 1. Read the plan in full. 2. Extract: frontmatter (status, dates), goals, acceptance criteria, implementation steps, existing checkboxes, and the **owning spec** of - every path the plan touches (`spec-spine registry show --json`). + every path the plan touches (`spec-spine registry show --json`, + `spec-spine index coverage` for the unclaimed ones). 3. Validate readiness: no clear acceptance criteria or steps means stop and ask; `completed` means confirm before redoing; `blocked` means ask what unblocks it. -4. If any step touches a shipped spec's territory in a way its Behavior +4. If any step touches a shipped spec's territory in a way its behavior section does not describe, stop: that needs a spec amendment first (`.claude/rules/adversarial-prompt-refusal.md`). @@ -47,27 +54,31 @@ driven session's standing authorization satisfies this checkpoint). Per task: 1. Announce the task. -2. Implement it, on a feature branch, never on `main`. -3. Verify: the narrowest cargo target (`cargo test -p --locked`), - then `make spine` whenever the task touched a spec-owned path, a - `spec.md`, a manifest, or any hashed input (`.claude/**`, `AGENTS.md`, - `CLAUDE.md`, `Makefile`, `docs/design/**`, workflows, standards). +2. Implement it, on a feature branch, never on the default branch. +3. Verify: the narrowest test target the stack offers, then the gate as + `AGENTS.md` lists it whenever the task touched a spec-owned path, a + `spec.md`, a manifest, or any hashed input (`spec-spine.toml [index] + extra_hashed_inputs`; typically the harness, design docs, workflows, + standards). 4. Update the plan file: check the box, recompute `progress` (checked over total, rounded), update `updated`. 5. Next task. Rules: read the entire plan before starting; keep the plan in sync after every task, not in batches; never commit unless asked (`/commit` when -asked); never disable or skip a failing test; never edit the evaluation -corpus to make a delta pass; claim every new source file in the spec whose territory it joins -(the ownership ratchet, `C-002`); preserve the plan's structure. +asked); never disable or skip a failing test; never regenerate a +never-touch artefact the path-scoped rules name; claim every new source +file in the spec whose territory it joins (the ownership ratchet, +`C-002`), or declare the `extends` edge when the territory is another +spec's; preserve the plan's structure. Mid-implementation checkpoint at 50 percent: report done, issues, deviations, remaining. Wait for confirmation. ## Phase 3: completion -Set `status: in-review`, `progress: 100`, `updated`. Run `make ci`. Deliver: +Set `status: in-review`, `progress: 100`, `updated`. Run the full +composite. Deliver: ``` ## Implementation Complete @@ -77,8 +88,8 @@ Set `status: in-review`, `progress: 100`, `updated`. Run `make ci`. Deliver: ### What was done ### Files modified (with owning spec) ### Verification -- make spine: ok | FAIL -- make ci: ok | FAIL +- governance gate: ok | FAIL at +- stack gate: ok | FAIL at ### Known issues or follow-ups ``` @@ -98,5 +109,11 @@ the user, never by this skill. | Blocked | ask what unblocks it | | Test or build failure | fix; if unfixable, mark the task blocked and continue with independent tasks | | Ambiguous task | ask | -| Coupling failure | surface the drift; never patch the spec to match the code | -| Memory invariant would be relaxed | stop; a human decides | +| Coupling failure | surface the drift; never patch a spec you are not implementing to match the code | +| Never-touch artefact would change | stop; a human decides | + +## Project layer + +Read from `AGENTS.md`: the gate command list and the narrowest test +targets. Read from `.claude/rules/`: the never-touch artefacts. Nothing +here is edited per project. diff --git a/.claude/skills/init/SKILL.md b/.claude/skills/init/SKILL.md index 38498b2..2ea84b8 100644 --- a/.claude/skills/init/SKILL.md +++ b/.claude/skills/init/SKILL.md @@ -1,6 +1,6 @@ --- name: init -description: "Initialize a session by executing the cross-agent New Sessions protocol declared in AGENTS.md." +description: "Initialize a session by executing the cross-agent New Sessions protocol declared in AGENTS.md. Reads only; never repairs the tree." allowed-tools: Bash, Read, Glob, Grep --- @@ -8,31 +8,45 @@ allowed-tools: Bash, Read, Glob, Grep Thin dispatcher. The canonical protocol lives in `AGENTS.md` under `## New Sessions`, the cross-agent AAIF/Linux Foundation standard read by -Claude Code, Codex CLI, Cursor, Copilot, and claude-observatory's driven +Claude Code, Codex CLI, Cursor, Copilot, and an orchestrator's driven sessions alike. ## What to do 1. Read `AGENTS.md`: the section from `## New Sessions` inclusive to the next `## ` heading exclusive. That section is the step list. -2. Load the standing rules it names first, then execute the protocol, - using parallel tool calls wherever it says "dispatch simultaneously". +2. Load the standing rules it names first (`.claude/rules/`), then execute + the protocol, using parallel tool calls wherever it says "dispatch + simultaneously". 3. Emit the structured summary the protocol prescribes: the - `## initialized: aicortex` block (the layer model with the crates that - exist, a `## lifecycle:` sub-section, freshness verdicts, recent - activity, and a ready-to-help line). + `## initialized: ` block (the layer model, a `## lifecycle:` + sub-section from `registry status-report`, the `registry plan` + ready/blocked line, the freshness verdicts, recent activity, and a + ready-to-help line). -This dispatcher deliberately does not duplicate the step list (spec 001 -B-1): `AGENTS.md` is the single source of truth. Evolve the protocol by -editing `AGENTS.md`, never this file, so every agent stays in sync. +This dispatcher deliberately does not duplicate the step list: `AGENTS.md` +is the single source of truth. Evolve the protocol by editing `AGENTS.md`, +never this file, so every agent stays in sync. ## Rules -- The protocol's governed reads go through the `spec-spine` binary on your - `PATH`. If `spec-spine --version` fails, run `/setup` first; never fall - back to parsing `.derived/` by hand. +- The protocol's governed reads go through the `spec-spine` invocation + `AGENTS.md` names. If `spec-spine --version` fails, run `/setup` first + (an in-tree build if `AGENTS.md` says the binary is built from source); + never fall back to parsing `.derived/` by hand. - `/init` reports, it does not mutate: `spec-spine compile --check` and `spec-spine index check` are the freshness reads, never a bare `compile` - or `index`. + or `index`. A stale verdict is reported with the shards it names and the + session continues; repairing the tree is the session's later, committed + work, not a side effect of reading it. +- A binary older than the checkout rejects `--check` with exit 2, the same + code as "stale". Read stderr: `unexpected argument` means rebuild or + reinstall the pinned version, not phantom drift. - A file the protocol names but cannot find is logged as "not found" and the protocol continues. + +## Project layer + +Nothing in this file is project-specific. The project name, the binary +invocation, the read list, and the summary shape all come from +`AGENTS.md`. diff --git a/.claude/skills/next/SKILL.md b/.claude/skills/next/SKILL.md index e965930..ca25ba5 100644 --- a/.claude/skills/next/SKILL.md +++ b/.claude/skills/next/SKILL.md @@ -1,105 +1,89 @@ --- name: next -description: "Compute the next ready spec exactly as AGENTS.md \"Working the backlog\" step 1 defines it, from a typed spec-spine registry read, with honest blockers when nothing is ready." +description: "Name the next spec to build: the ready set from spec-spine registry plan, minus anything a human has not approved, with in-flight specs and honest blockers reported separately. Read-only." allowed-tools: Bash, Read --- # /next: the next work order -The backlog is the spec corpus. The rule is step 1 of `AGENTS.md`, -"Working the backlog": the lowest-numbered spec with `status: approved` -and `implementation: pending` whose every `depends_on` target is -`implementation: complete` or `n-a`. A `draft` spec is never offered: -approval is a human act (claude-observatory's scheduler, its spec 012 -D-3, enforces the same edge). Specs `000`, `001`, and `002` are records, -never work orders. +The backlog is the spec corpus. Step 1 of `AGENTS.md` "Working the +backlog" picks the spec, and this skill computes that pick from typed +reads. Two rules sit on top of what the tool reports: -## Step 0: the DAG must be executable +- **Approval is a human act.** `spec-spine registry plan` (spec 038) + schedules by `implementation` and `depends_on`; it will offer a + `status: draft` spec whose dependencies are met. A draft is never a + work order: drop it from the ready set and list it as "awaiting + approval". +- **One session, one spec.** A spec at `implementation: in-progress` is + in flight (spec 044): it belongs to another session or to an + interrupted one. Report it separately; never offer it as new work + unless a human names it. + +## Step 1: the ready set, typed ```sh -scripts/spec-dag.sh +spec-spine registry plan --json ``` -Exit 0 continues. Exit 1 names the violation (a cycle with its path, a -dependency on a higher-numbered or unknown spec): refuse to name a next -spec, print the script's stderr verbatim, and stop; scheduling from a -broken graph is guessing. Exit 3 means `spec-spine` or `python3` is -absent: run `/setup`. - -## Step 1: compute readiness from a typed read +Exit 0 prints `{"ready": [...ids in dependency order...], "blocked": [{"id", "blockedBy": [{"id", "state"}]}]}`. +A non-zero exit is a halt: `V-014` names a dependency cycle (schedule +nothing from a broken graph; print the diagnostic verbatim), and any +other failure means the registry is unreadable (`/setup`, or a stale +binary). -`spec-spine registry list --json` is a typed CLI read; parsing its output -with `python3` or `jq` is allowed (`.claude/rules/governed-artifact-reads.md`). -Reading `.derived/` is not. The registry JSON travels by file, never on -the same stdin as the program: +Parsing this output is a typed read (`.claude/rules/governed-artifact-reads.md`). +Reading `.derived/` is not. -```sh -tmp="$(mktemp)" && trap 'rm -f "$tmp"' EXIT -spec-spine registry list --json > "$tmp" || { echo "registry read failed (run spec-spine compile?)"; exit 3; } -python3 - "$tmp" <<'PY' -import json, sys - -with open(sys.argv[1], encoding="utf-8") as fh: - specs = json.load(fh) -by_id = {s["id"]: s for s in specs} -DONE = {"complete", "n-a"} - -def blockers(s): - out = [] - if s.get("status") != "approved": - out.append(f"status {s.get('status')} (approval is a human act)") - for d in s.get("dependsOn") or []: - dep = by_id.get(d) - if dep is None: - out.append(f"depends on {d}, which is not in the corpus") - elif dep.get("implementation") not in DONE: - out.append(f"depends on {d}, which is {dep.get('implementation')}") - return out - -pending = sorted((s for s in specs if s.get("implementation") == "pending"), key=lambda s: s["id"]) -ready = [s for s in pending if not blockers(s)] -if ready: - pick = ready[0] - wave = (pick.get("extraFrontmatter") or {}).get("wave", "?") - print(f"## next: {pick['id']}") - print(f"title: {pick['title']}") - print(f"wave: {wave}; status: {pick['status']}; implementation: {pick['implementation']}") - print("depends_on:") - for d in pick.get("dependsOn") or []: - print(f" - {d}: {by_id[d].get('implementation')}") - rest = [s["id"] for s in ready[1:]] - print("also ready (higher-numbered): " + (", ".join(rest) if rest else "none")) -else: - print("## next: none ready") - for s in pending: - print(f"- {s['id']}: blocked; " + "; ".join(blockers(s))) - if not pending: - print("(no spec is implementation: pending)") -PY -``` +## Step 2: apply the two rules -Cross-check the pick with the typed single-spec read before handing it to -`/build`, and read its `## 2. Territory` for operator prerequisites (a -service, a credential, a sibling repo) that would make the session stop -at step 1 of the backlog protocol: +For each ready id, read its record: ```sh spec-spine registry show --json ``` -## Step 2: report +- `status` is not `approved`: move it to "awaiting approval". +- `implementation` is `in-progress`: move it to "in flight". +- Otherwise it is a work order. Keep the tool's order. + +Record specs (the bootstrap, a thesis, a harness spec at `n-a` or +`complete`) never appear in `ready`; if one does, its `implementation` +is wrong and that is a finding, not a pick. + +## Step 3: the pick, cross-checked + +Take the first work order unless a human named another. Read its +`## Territory` (or equivalent) for an operator prerequisite (a service, a +credential, a sibling repository) that is missing: that is a stop at +step 1 of the protocol, reported exactly, not mocked around. -Print the block from Step 1 as-is. When nothing is ready, every pending -spec is listed with its honest blockers (`status draft`, or the dependency -by id and its current `implementation` value); never hide an unapproved -spec and never offer one. An `in-progress` spec is not pending: mention -it separately as "in flight" when one exists, since the one-session, -one-spec rule means it belongs to another session or to an interrupted -one. +## Step 4: report + +``` +## next: +title: +status: approved; implementation: pending +depends_on: + - <dep>: <implementation state> +also ready: <ids in order, or none> +in flight: <ids at in-progress, or none> +awaiting approval: <draft ids the tool would have offered, or none> +blocked: <id>: <blockedBy id and state>; ... +prerequisites: <none named / what the Territory requires> +``` + +When nothing is a work order, print `## next: none ready` and still list +in flight, awaiting approval, and blocked with their reasons. Never hide +an unapproved spec and never offer one. ## Rules - Never guess an id from `ls specs/`; the registry is the source. -- Never read `.derived/**/*.json` directly. - `/next` is read-only. It does not flip, branch, or commit; `/build <id>` does. + +## Project layer + +Nothing here is project-specific. Which specs are records is visible from +their `implementation` value, not from a list in this file. diff --git a/.claude/skills/refactor-claude-md/SKILL.md b/.claude/skills/refactor-claude-md/SKILL.md index f500ae0..4dbd17f 100644 --- a/.claude/skills/refactor-claude-md/SKILL.md +++ b/.claude/skills/refactor-claude-md/SKILL.md @@ -1,6 +1,7 @@ --- name: refactor-claude-md -description: Tighten CLAUDE.md by extracting context-specific guidance into docs and path-scoped rules under .claude/rules, keeping the harness spec (001) coupled +description: "Tighten CLAUDE.md by extracting context-specific guidance into docs and path-scoped rules under .claude/rules, keeping the harness spec coupled and the index fresh." +allowed-tools: Bash, Read, Write, Edit, Glob, Grep argument-hint: "[path to CLAUDE.md, default ./CLAUDE.md]" --- @@ -8,11 +9,15 @@ argument-hint: "[path to CLAUDE.md, default ./CLAUDE.md]" Reduce the size of `CLAUDE.md` while preserving guidance, by moving context-specific sections into `docs/` and loading them through -path-scoped rules. In aicortex, `CLAUDE.md`, `.claude/rules/`, and -`AGENTS.md` are spec 001's territory and hashed inputs of the codebase -index: every change here couples to `specs/001-agentic-harness/spec.md` -(a dated D-n note under Resolved decisions) and stales the index until -`spec-spine index` runs. +path-scoped rules. In a spec-spine repository `CLAUDE.md`, +`.claude/rules/`, and `AGENTS.md` are usually hashed inputs of the +codebase index and owned by a harness spec: every change here couples to +that spec (a dated decision entry naming the extraction) and stales the +index until `spec-spine index` runs. Find the owner first: + +```sh +spec-spine index coverage # which spec claims the harness files +``` ## Process @@ -22,8 +27,10 @@ index: every change here couples to `specs/001-agentic-harness/spec.md` Claude Code needs beyond it). 2. **Identify extraction candidates**: sections that are cross-cutting - patterns rather than core setup, specific to certain crates or file - types, long and detailed, or better loaded only when relevant. + patterns rather than core setup, specific to certain directories or + file types, long and detailed, or better loaded only when relevant. + Typical candidates: per-package implementation notes, testing + patterns, a subsystem's conventions, a framework guide. 3. **For each candidate** recommend: the doc name under `docs/`, its scope, the `paths:` globs that should trigger it, and the one-line @@ -39,40 +46,37 @@ index: every change here couples to `specs/001-agentic-harness/spec.md` ```markdown --- paths: - - "crates/aicortex-recall/**" + - "crates/<name>/**" + - "apps/<name>/**" --- Two or three key points, and the doc to read: `docs/<name>.md`. ``` -5. **Couple the change**: add a dated D-n entry to spec 001's `## 7. - Resolved decisions` naming the extraction, then `spec-spine compile && - spec-spine index` and stage `.derived/`. +5. **Couple the change**: add a dated decision entry to the harness spec + naming the extraction (a new rule file is `establishes` growth for + that spec if it lists rules individually), then + `spec-spine compile && spec-spine index` and stage the derived + directory. ## Key principles - Extract only context-specific guidance; keep universal rules in `CLAUDE.md`. -- Preserve critical information in `CLAUDE.md`: the memory invariants, - the commands, the architecture table, the governance mechanics, house +- Preserve critical information in `CLAUDE.md`: the invariants, the + commands, the architecture table, the governance mechanics, house style. -- Meaningful globs: a rule that loads everywhere is a `CLAUDE.md` section +- Choose meaningful path patterns; a rule that loads everywhere is a `CLAUDE.md` section in disguise. - Keep replacements brief; the reader needs to know where to look. -## Good extraction candidates in this repository - -- Per-crate implementation notes once a crate has shipped (for example - the fusion and trace format for `crates/aicortex-recall/**`). -- Testing patterns (fixture memories, the evaluation corpus under `eval/`). -- Deployment notes for `docker/**` and `deploy/**`. - ## Keep in CLAUDE.md -- The memory invariants and the chassis-consumed-never-forked rule. +- The invariants and any determinism or hash-stability rule. - Commands and exit codes. -- The layer-to-crate table. -- Governance mechanics (ownership ratchet, committed `.derived/`, hooks). +- The layer-to-package table. +- Governance mechanics (ownership ratchet, committed derived shards, + read-only hooks). - House style. ## After extraction @@ -80,4 +84,9 @@ index: every change here couples to `specs/001-agentic-harness/spec.md` Report the size change (lines before and after), list the files created, confirm `spec-spine index check` is fresh, and offer `/commit`. +## Project layer + +Read from `spec-spine index coverage`: the harness spec. Nothing here is +edited per project. + `$ARGUMENTS` diff --git a/.claude/skills/research/SKILL.md b/.claude/skills/research/SKILL.md index ebdd02c..ea28599 100644 --- a/.claude/skills/research/SKILL.md +++ b/.claude/skills/research/SKILL.md @@ -1,6 +1,6 @@ --- name: research -description: Deep research with parallel sub-agents, query classification, and filesystem artifact passing; corpus questions read specs through spec-spine, external questions use the web +description: "Deep research with parallel sub-agents, query classification, and filesystem artifact passing; corpus questions read specs through spec-spine, external questions use the web." allowed-tools: Agent, Read, Write, Bash(git log:*), Bash(git diff:*), Bash(spec-spine:*), WebSearch, WebFetch, Glob, Grep argument-hint: "<question or topic to investigate>" --- @@ -27,14 +27,14 @@ Decide: query type, agent count, domains (corpus, codebase, external docs, papers, general web), and scope (corpus-only, web-only, hybrid). - **Corpus questions** ("what does spec 020 say about erasure", "who owns - `crates/aicortex-recall/src/fusion.rs`", "what depends on 018"): use the - `explorer` agent with `spec-spine registry show|relationships <id>`, - `spec-spine index render`, `Grep` over `specs/`, and `git log`. Never - parse `.derived/` directly. -- **External questions** (the MCP streamable HTTP transport, OAuth - protected resource metadata, embedding model normalization, hybrid - ranking fusion, hiqlite Raft semantics): `WebSearch` and `WebFetch`, preferring primary sources - (specifications, RFCs, the library's own docs). + this file", "what depends on 064", "how does the compiler validate + frontmatter"): use the `explorer` agent with + `spec-spine registry show|relationships <id>`, `spec-spine index render`, + `spec-spine index coverage`, `Grep` over `specs/` and the source, and + `git log`. Never parse `.derived/` directly. +- **External questions** (a protocol, a library's semantics, an RFC): + `WebSearch` and `WebFetch`, preferring primary sources (specifications, + RFCs, the library's own docs). - Many questions are hybrid; split them across agents by domain. ## Phase 2: parallel execution @@ -43,17 +43,24 @@ Spawn all sub-agents in one message. Each prompt begins with a depth trigger: "Quick check:", "Investigate:", or "Deep dive:". Each sub-agent MUST write its full report to the session scratchpad -directory when one is listed in the system prompt (otherwise -`/tmp/aicortex-research/`) as `research_<timestamp>_<slug>.md` and return -only: the file path, a two to three sentence summary, key topics, and the -source count. +directory when one is listed in the system prompt (otherwise a +`research/` directory under the tool-state root `spec-spine.toml +[layout] state_dir` names, never committed) as +`research_<timestamp>_<slug>.md` and return only: the file path, a two to +three sentence summary, key topics, and the source count. -Example, depth-first ("how should 111 bind the identity key to the QUIC -certificate?"): +Example, hybrid ("how should the new spec bind an identity key to the +transport certificate?"): ``` -Task 1: "Deep dive: raw public key TLS (RFC 7250) support in rustls and quinn; how iroh binds a node id to the endpoint certificate" -Task 2: "Investigate: what spec 060 and spec 111 in specs/ already fix about identity binding; use spec-spine registry show" +Task 1: "Deep dive: raw public key TLS (RFC 7250) support in the libraries this stack uses" +Task 2: "Investigate: what the existing specs already fix about identity binding; use spec-spine registry show and relationships" +``` + +Example, corpus-only ("trace the spec compiler's validation pipeline"): + +``` +Task 1: "Investigate: which spec owns the validation code (spec-spine index coverage, registry show), then read it and list the diagnostics it can emit in order" ``` ## Phase 3: synthesis @@ -84,3 +91,8 @@ sub-agent report paths, and name contradictions and gaps explicitly. Prefer primary sources; cross-reference important claims; state what could not be determined; separate fact from inference; prefer recent sources and flag stale ones. Never use the em dash character in any written artifact. + +## Project layer + +Nothing here is project-specific. External topics come from the question; +the report location comes from the harness or `spec-spine.toml`. diff --git a/.claude/skills/setup/SKILL.md b/.claude/skills/setup/SKILL.md index 6ab3e43..3ddc2a5 100644 --- a/.claude/skills/setup/SKILL.md +++ b/.claude/skills/setup/SKILL.md @@ -1,58 +1,46 @@ --- name: setup -description: "One-time contributor setup for aicortex: install spec-spine 0.14.0, the pinned Rust toolchain once rust-toolchain.toml exists, the optional cargo-deny, and verify the governed loop with make spine." +description: "One-time contributor setup: install the pinned spec-spine, the stack toolchain AGENTS.md names, fetch the base ref, and verify the governed loop once, so /init can report lifecycle and structural counts." allowed-tools: Bash, Read --- # /setup Get a fresh clone operational. After this completes, `/init` can report -lifecycle and structural counts through the `spec-spine` binary, never by -ad-hoc parsing of `.derived/**/*.json` -(`.claude/rules/governed-artifact-reads.md`). +lifecycle and structural counts through `spec-spine`, never by ad-hoc +parsing of `.derived/**/*.json` (`.claude/rules/governed-artifact-reads.md`). ## Process -### 1. Install spec-spine 0.14.0 +### 1. Install the pinned spec-spine -The version is pinned in `AGENTS.md` and in `.github/workflows/govern.yml` -(`SPEC_SPINE_VERSION`); the three must agree. Either route: +`AGENTS.md` pins the version and names the invocation (a binary on `PATH`, +a package wrapper, or an in-tree build). CI runs the pinned one, so a local +pass on another version proves nothing: a different version is a halt. +Any one route: ```sh -cargo install spec-spine-cli --version 0.14.0 --locked # with a Rust toolchain -npm i -g spec-spine@0.14.0 # prebuilt binary, no toolchain +cargo install spec-spine-cli --version <pin> --locked # with a Rust toolchain +npm i -g spec-spine@<pin> # prebuilt binary, no toolchain +uvx spec-spine@<pin> --version # Python, ephemeral ``` -Verify: `spec-spine --version` prints `spec-spine 0.14.0`. A different -version is a halt: CI runs the pinned one and a local pass on another -version proves nothing. +Verify: `spec-spine --version` prints the pin. When `AGENTS.md` says the +binary is built from this checkout, build it as it says instead and use +that path for every command below. -### 2. Rust toolchain (once spec 010 has landed) +### 2. Stack toolchain -`rust-toolchain.toml` arrives with spec 010 and pins the channel and -components. When it exists: - -```sh -rustup show # installs the pinned toolchain on first run -cargo --version -``` - -Before spec 010 there is no `Cargo.toml`; every `make` target is guarded -for that, and this step is skipped with a note. - -Optional, but CI runs it and `make deny` skips without it: - -```sh -cargo install cargo-deny --locked -``` - -`jq` is used by the hooks in `.claude/settings.json`; each hook exits 0 -and says what it skipped when `jq` is absent (spec 001 FR-003), so it is -a convenience, not a prerequisite. Install it with your package manager. +Install whatever `AGENTS.md` (or `CLAUDE.md`) names for the language +gates: a pinned toolchain file, a package manager, optional linters. Report +each as present or absent; a missing optional tool is a note, a missing +required one is a halt. `jq` is a convenience the hooks in +`.claude/settings.json` use; each hook says what it skipped when `jq` is +absent. ### 3. Fetch the base ref -The coupling gate diffs against `origin/main`: +The coupling gate diffs against the default branch on the remote: ```sh git fetch origin main @@ -60,59 +48,67 @@ git fetch origin main ### 4. Verify the governed loop -`make spine` is the gate chain CI re-runs (spec 001 B-2): `spec-spine -compile`, `spec-spine index`, `spec-spine lint --fail-on-warn`, -`spec-spine index check`, `spec-spine couple --base origin/main --head -HEAD`, `scripts/spec-dag.sh`. +Run the gate exactly as `AGENTS.md` "Working the backlog" lists it under +"Run the gate before every commit". The governance floor is: ```sh -make spine +spec-spine compile +spec-spine index +spec-spine lint --fail-on-warn +spec-spine index check +spec-spine couple --base origin/main --head HEAD +spec-spine index coverage --fail-on-untraced # when [coupling] require_ownership is on ``` -On a clean checkout `compile` and `index` are deterministic no-ops. If -`git status --short -- .derived/` shows a diff afterwards the committed -shards were stale: commit the regenerated shards (`chore(derived): ...`) -before doing anything else. Halt on the first failing target and surface -its output verbatim; do not continue past it. +then the stack's own build, tests, and lints. On a clean checkout +`compile` and `index` are deterministic no-ops; if +`git status --short -- .derived/` shows a diff afterwards, the committed +shards were stale. Say so and leave the diff for the session to commit +(`chore(derived): ...`); do not hide it. Halt on the first failing step +and surface its output verbatim. -Then the lifecycle reads `/init` will use: +Then the reads `/init` will use: ```sh spec-spine registry status-report --json --nonzero-only +spec-spine registry plan spec-spine index coverage ``` ### 5. Emit summary -Report exactly: - ``` -## setup: aicortex +## setup: <project> -**spec-spine:** {0.14.0 / wrong version <v> / failed at <step>} -**Rust toolchain:** {<channel> from rust-toolchain.toml / not yet (spec 010 pending)} -**Optional tools:** cargo-deny {present/absent}, jq {present/absent} -**Governed loop (make spine):** +**spec-spine:** {<pin> / wrong version <v> / failed at <step>} +**Toolchain:** {<what AGENTS.md names>: present / absent} +**Optional tools:** {name: present/absent, ...} +**Governed loop:** - compile: {ok / failed} - - index: {ok / regenerated, commit .derived/} + - index: {ok / regenerated, shards left for the session to commit} - lint --fail-on-warn: {clean / N diagnostics} - index check: {fresh / stale} - couple: {clean / drift surfaced} - - spec-dag: {acyclic, N specs / violation} + - coverage: {N claimed, M unclaimed / not enforced} + - stack gate: {ok / failed at <command> / none declared} **Lifecycle:** {N specs across <statuses>} (from registry status-report) -**Coverage:** {N claimed, M unclaimed} (from index coverage) +**Ready:** {ids / (nothing ready)} (from registry plan) Next: run `/init` to load full session context. ``` Do not invent counts. Only report values that came back from a -`spec-spine` subcommand or a `make` target. +`spec-spine` subcommand or a gate command. ## Rules -- The loop runs through the installed `spec-spine` binary on your `PATH`. - Halt on first failure. Do not silently continue past a missing prerequisite or a failing gate. -- Never parse `.derived/**/*.json` directly in any verification step; use - the `spec-spine` subcommands. +- Never parse `.derived/**/*.json` directly; use the `spec-spine` + subcommands. - Idempotent: safe to re-run. + +## Project layer + +Read from `AGENTS.md`: the version pin, the binary invocation, the +toolchain, the gate command list. Nothing here is edited per project. diff --git a/.claude/skills/shepherd/SKILL.md b/.claude/skills/shepherd/SKILL.md index bdc7ff8..6d8cf20 100644 --- a/.claude/skills/shepherd/SKILL.md +++ b/.claude/skills/shepherd/SKILL.md @@ -1,6 +1,6 @@ --- name: shepherd -description: "Watch an open PR's checks, remediate a red required check through the governed gate (at most two rounds), merge with squash when green, and confirm the merge on disk. Never self-approves a Spec-Drift-Waiver." +description: "Watch an open PR's checks by head sha, answer review threads, remediate a red required check through the governed gate (at most two rounds), merge with squash when green, and confirm the merge on disk. Never self-approves a Spec-Drift-Waiver." allowed-tools: Bash, Read, Edit, Glob, Grep, Skill argument-hint: "[pr-number] (defaults to the PR for the current branch)" --- @@ -9,8 +9,8 @@ argument-hint: "[pr-number] (defaults to the PR for the current branch)" CI is where reality pushes back. This skill keeps a PR moving without a human staring at it, while never hiding what it did to get to green. It -mirrors claude-observatory's shepherd stage (its spec 018): watch by head -sha, remediate through the gate, bounded attempts, honest stops. Bound by +mirrors an orchestrator's shepherd stage: watch by head sha, remediate +through the gate, bounded attempts, honest stops. Bound by `.claude/rules/orchestrator-rules.md` (checkpoints are real stops) and `.claude/rules/adversarial-prompt-refusal.md` (a coupling refusal goes to a human, not around them). @@ -19,12 +19,12 @@ a human, not around them). ```sh git branch --show-current -gh pr view ${ARGUMENTS:-} --json number,url,headRefName,headRefOid,baseRefName,mergeStateStatus,isDraft +gh pr view ${ARGUMENTS:-} --json number,url,headRefName,headRefOid,baseRefName,mergeStateStatus,isDraft,reviewDecision ``` - No PR for the branch: stop and say so (run `/ship` first). Do not open one here. -- The branch must be a spec branch (`<spec-id>`), never `main`. +- The branch must be a feature branch, never the default branch. - Record `headRefOid`: every check you read is for this sha and no other. ## Step 1: watch the checks (by head sha) @@ -64,10 +64,12 @@ Then, on the branch: 2. Fix inside the spec's territory. If the failure is the coupling gate (`C-001` or `C-002`), claim the path in the spec being implemented or add the `extends` edge on its owner; never edit an owning spec to - ratify code that contradicts it (coherence guard: stop and report). -3. `make spine` then `make ci` must exit 0 locally. -4. `/commit` (conventional, spec id as scope, regenerated `.derived/` - staged), then `git push`. + ratify code that contradicts it (coherence guard: stop and report). A + `// Spec:` comment header is the other legitimate claim for a file no + frontmatter should list. +3. Run the gate exactly as `AGENTS.md` lists it; it must exit 0 locally. +4. `/commit` (conventional, spec id as scope, regenerated shards staged), + then `git push`. 5. Re-read `headRefOid`. Restart Step 1's watch on the new sha with a fresh 45 minute budget. Checks from the old sha are stale; never mix them. @@ -76,13 +78,25 @@ After two remediation rounds that still end red, stop. Report the run ids, the log tails, and what you tried; say plainly that the PR needs a human. Flapping CI (green then red on the same sha) counts as a round. +## Step 2b: review threads + +```sh +gh api "repos/{owner}/{repo}/pulls/<number>/comments" --jq '.[] | {id, path, line, body, user: .user.login}' +``` + +Address each thread that asks for a concrete change inside the spec's +territory as part of a remediation round. A thread that asks for a +design change (a different behavior than the spec describes) is a human +question: quote it in the report, do not resolve it by editing the spec. +`reviewDecision: CHANGES_REQUESTED` with no actionable thread is a stop. + ## Step 3: CHECKPOINT, merge Merging is outward-facing. Confirm with the user unless the prompt carries -the operator's standing run-start authorization (a claude-observatory -driven session), which satisfies this checkpoint. A `Spec-Drift-Waiver:` -in the PR body is never covered by standing authorization: if one is -present and was not explicitly approved by a human in this session, stop. +the operator's standing run-start authorization (an orchestrator's driven +session), which satisfies this checkpoint. A `Spec-Drift-Waiver:` in the +PR body is never covered by standing authorization: if one is present and +was not explicitly approved by a human in this session, stop. ```sh gh pr view <number> --json mergeStateStatus,reviewDecision @@ -90,7 +104,11 @@ gh pr merge <number> --squash --delete-branch ``` - `mergeStateStatus` of `DIRTY` or `BEHIND` (base moved, conflicts): do - not rebase automatically. Stop and report; a human decides. + not rebase automatically. Stop and report; a human decides. When the + only conflict is a derived shard, say so: the recipe is to merge the + base, run `spec-spine compile && spec-spine index` on the merged tree, + and commit the regenerated shard (the repository may have the derived + merge driver enabled for exactly this). - The squash commit title is the PR title (conventional, spec id as scope). No AI attribution, no session links. @@ -106,16 +124,16 @@ git log -1 --oneline git branch --contains "$(git rev-parse HEAD)" | grep -q main ``` -Report the merge sha, and that the local `main` contains it. If `git -pull --ff-only` refuses, the local `main` diverged: report it, do not +Report the merge sha, and that the local default branch contains it. If +`git pull --ff-only` refuses, the local branch diverged: report it, do not reset anything. ## Step 5: hand off The spec is shipped in the corpus sense only after its `## Verification` -block passes on the merged sha: run `/verify <spec-id>` on `main`, or say -that the orchestrator's verify stage will. Then stop: the next session -takes the next spec (`/next`). +block passes on the merged sha: run `/verify <spec-id>` on the default +branch, or say that the orchestrator's verify stage will. Then stop: the +next session takes the next spec (`/next`). ## Report @@ -123,7 +141,13 @@ takes the next spec (`/next`). ## shepherd: <spec-id> (PR #<n>) Head sha watched: <sha> (rounds: <k>) Checks: <name>: <state> ... +Review threads: <none | n addressed | n need a human> Remediation: <none | round 1: <run-id> <cause> -> <fix> | round 2: ...> Merge: <sha> squash, branch deleted | NOT merged: <reason, needs human> On disk: main contains <sha> | <divergence> ``` + +## Project layer + +Read from `AGENTS.md`: the gate command list and the default branch. +Nothing here is edited per project. diff --git a/.claude/skills/ship/SKILL.md b/.claude/skills/ship/SKILL.md index b771cb6..cde0c42 100644 --- a/.claude/skills/ship/SKILL.md +++ b/.claude/skills/ship/SKILL.md @@ -1,77 +1,84 @@ --- name: ship -description: "Governed pre-PR sequence for aicortex: run make spine and make ci, review the diff, conventional commit on the spec's feature branch, push, open the PR via gh. The coupling waiver is a human checkpoint a driven session never self-approves." +description: "Governed pre-PR sequence: run the gate locally, review the diff, conventional commit on the spec's feature branch, push, open the PR via gh. The coupling waiver is a human checkpoint a driven session never self-approves." allowed-tools: Bash, Read, Edit, Glob, Grep, Skill argument-hint: "[optional scope note or PR title]" --- # /ship: gate, review, commit, PR -Sequences the steps that turn a working tree into a PR. This is step 8 of -`AGENTS.md` "Working the backlog". Bound by +Sequences the steps that turn a working tree into a PR. This is the +"Ship" step of `AGENTS.md` "Working the backlog". Bound by `.claude/rules/orchestrator-rules.md` (checkpoints are real stops) and `.claude/rules/adversarial-prompt-refusal.md` (never edit an owning spec -to make the gate pass). The gate is the installed `spec-spine` binary; if -it is missing, run `/setup`. +to make the gate pass). The gate is the `spec-spine` invocation +`AGENTS.md` names; if it is missing, run `/setup`. ## Step 0: preflight -- `git branch --show-current`. The branch must be named after the spec id - (`017-ledger-entry-dag`). If you are on `main`, STOP and create the - branch first (`git switch -c <spec-id>`); the `PreToolUse` hook refuses - a push to `main` regardless. +- `git branch --show-current`. The branch must be a feature branch, named + after the spec id when the change implements one. On the default + branch, STOP and create the branch first (`git switch -c <spec-id>`); + the `PreToolUse` hook refuses a push to the default branch regardless. - `git status --short`. Confirm the changes are the intended set: the - spec's territory, the spec's own `spec.md` (frontmatter flipped to - `implementation: complete`, D-n decisions recorded), and the regenerated - `.derived/` shards. Surface anything unexpected before proceeding. + spec's territory, the spec's own `spec.md` (frontmatter flipped, decision + entries recorded), and the regenerated derived shards. Surface anything + unexpected before proceeding. - `git fetch origin main` so the coupling gate has a base. ## Step 1: run the gate locally +Run the gate exactly as `AGENTS.md` lists it under "Run the gate before +every commit". The governance floor: + ```sh -make spine # compile, index, lint --fail-on-warn, index check, couple, spec-dag -make ci # + index coverage --fail-on-untraced, cargo build/test/clippy/fmt/deny +spec-spine compile +spec-spine index +spec-spine lint --fail-on-warn +spec-spine index check +spec-spine couple --base origin/main --head HEAD +spec-spine index coverage --fail-on-untraced # when [coupling] require_ownership is on ``` -Stop on the first failure (orchestrator rule: halt, never continue -silently). Outcomes: +then the stack's own build, tests, and lints. Stop on the first failure +(orchestrator rule: halt, never continue silently). Outcomes: - All green: continue to Step 2. -- `index check` stale (exit 2): `spec-spine index`, stage `.derived/`, and - re-run. The shards are committed with the change they describe. +- `index check` stale (exit 2): `spec-spine index`, stage the derived + directory, and re-run. The shards are committed with the change they + describe. - `couple` drift (`C-001`): a changed path is claimed by a spec that did not change. Two legitimate paths, chosen explicitly: 1. **Fix the coupling.** The path belongs to the spec you are - implementing: add it to that spec's `establishes` (or add the - `extends` edge on the owning spec's unit). The gate enforces the - declared graph, not prose. Do NOT edit a spec to retroactively - justify code that contradicts its design: that is a coherence-guard - halt; surface the contradiction and stop. + implementing: add it to that spec's `establishes`, or declare an + `extends` edge on the owning spec's unit in your spec's frontmatter. + The gate enforces the declared graph, not prose. Do NOT edit the + other spec to retroactively justify code that contradicts its design: + that is a coherence-guard halt; surface the contradiction and stop. 2. **Waiver.** A cited `Spec-Drift-Waiver: <reason>` line in the PR body. CHECKPOINT: requires explicit human approval in this session. - Standing authorization (below) never covers it. + A waiver is a human instrument; standing authorization (below) never + covers it. - `couple` unclaimed (`C-002`): a source file no spec specifically claims. Claim it in the implementing spec's `establishes` list; the ownership ratchet has no waiver-free path by design. - `index coverage --fail-on-untraced` lists a file: same remedy as `C-002`. -- A cargo gate fails: fix it. Never disable a test, never loosen a lint, - never edit the evaluation corpus or its baseline under `eval/` to make - a retrieval delta pass (the corpus is evidence; stop and report). +- A stack gate fails: fix it. Never disable a test, never loosen a lint, + never regenerate a never-touch artefact the path-scoped rules name + (that is a human decision; stop and report). ## Step 2: review the diff -Invoke the `code-review` skill on the working diff. It checks any path -under the store, gate, embed, index, recall, or mcp crates against -`.claude/rules/memory-invariants.md`. -Apply confirmed, actionable fixes. If a fix touches any gate input (a -`spec.md`, a manifest, a workflow, `Makefile`), re-run Step 1. +Invoke the `code-review` skill on the working diff. Apply confirmed, +actionable fixes. If a fix touches any gate input (a `spec.md`, a +manifest, a workflow, the harness), re-run Step 1. ## Step 3: commit Invoke the `commit` skill: conventional, impact-focused, the spec ordinal -as scope (`feat(012): ...`), the regenerated `.derived/` staged alongside -the change. Banned in commits and PR bodies: AI attribution of any kind, +as scope (`feat(017): ...`), the regenerated shards staged alongside the +change. Banned in commits and PR bodies: AI attribution of any kind, session links, em dashes, emojis. If a waiver was chosen in Step 1, keep the `Spec-Drift-Waiver:` line with the change so the PR carries it. @@ -81,37 +88,42 @@ PR creation is outward-facing. Confirm with the user, then: ```sh git push -u origin "$(git branch --show-current)" -gh pr create --title "<type>(<spec-id ordinal>): <subject>" --body "$(cat <<'EOF' +gh pr create --title "<type>(<ordinal>): <subject>" --body "$(cat <<'EOF' ## Summary <what the spec's territory now does, two to five lines> ## Testing -<the gate: make spine, make ci; the spec's Verification block via /verify> +<the gate as run; the spec's Verification block via /verify> EOF )" ``` -- The `PreToolUse` hook refreshes the index and re-runs the coupling gate - before `gh pr create`; it blocks when the gate is red without an inline - `Spec-Drift-Waiver:` after `--body`, and when `.derived/` is left - uncommitted by the refresh. Fix, commit, and retry rather than routing - around it. -- CI (`.github/workflows/govern.yml`) re-runs the same gate. A local pass - should mean a clean CI run; if CI fails a gate the local run passed, - halt and present the divergence. +- The `PreToolUse` hook checks the index for staleness and re-runs the + coupling gate before `gh pr create`; it blocks when the index is stale, + when the derived directory has uncommitted changes, and when the gate is + red without an inline `Spec-Drift-Waiver:` after `--body`. It never + writes: fix, commit, and retry rather than routing around it. +- CI re-runs the same gate. A local pass should mean a clean CI run; if + CI fails a gate the local run passed, halt and present the divergence. ### Standing authorization -When the prompt carries the operator's run-start authorization (a -claude-observatory driven session; its ship stage says so explicitly), that -authorization satisfies this step's PR-creation checkpoint: push and open -the PR without asking. It does not extend to a drift waiver (Step 1, path -2), which still halts the session for a human. A session with no such +When the prompt carries the operator's run-start authorization (an +orchestrator's driven session says so explicitly), that authorization +satisfies this step's PR-creation checkpoint: push and open the PR +without asking. It does not extend to a drift waiver (Step 1, path 2), +which still halts the session for a human. A session with no such authorization asks. ## Step 5: after creation Hand off to `/shepherd` (watch checks, remediate, merge, confirm on disk). -After the merge, verify the on-disk default branch (`git switch main && -git pull --ff-only && git log -1`), not just the MERGED status, and run -`/verify <spec-id>` on the merged sha. +After the merge, verify the on-disk default branch +(`git switch main && git pull --ff-only && git log -1`), not just the +MERGED status, and run `/verify <spec-id>` on the merged sha. + +## Project layer + +Read from `AGENTS.md`: the gate command list, the stack gate, the default +branch. Never-touch artefacts come from the path-scoped rules. Nothing +here is edited per project. diff --git a/.claude/skills/spec/SKILL.md b/.claude/skills/spec/SKILL.md index 7e720b3..84faf37 100644 --- a/.claude/skills/spec/SKILL.md +++ b/.claude/skills/spec/SKILL.md @@ -1,8 +1,8 @@ --- name: spec -description: "Author a new spec from standards/spec/templates/spec-template.md at the next free ordinal in its wave, born status draft, validated by compiling a temporary copy and by scripts/spec-dag.sh. Approval stays a human flip." -allowed-tools: Bash, Read, Write, Edit, Glob, Grep -argument-hint: "[wave] [short title]" +description: "Author a new spec from standards/spec/templates/spec-template.md at the next free ordinal, born status draft, validated with spec-spine compile and lint before it lands. Approval stays a human flip." +allowed-tools: Bash(spec-spine:*), Bash(git:*), Bash(mkdir:*), Bash(cp:*), Bash(mktemp:*), Read, Write, Edit, Glob, Grep +argument-hint: "[short title]" --- # /spec: author a new spec @@ -11,8 +11,8 @@ A new spec is a design change, not code. It is born `status: draft`, it claims territory only in prose and typed edges, and it schedules nothing until a human flips it to `approved` (`/next` never offers a draft). The contract is `standards/spec/contract.md`; the shape is -`standards/spec/templates/spec-template.md`; the layer model and the -wave plan are `specs/002-memory-thesis/spec.md`. +`standards/spec/templates/spec-template.md`; the vocabulary of edges and +units is in the constitution and the contract. ## Step 1: gather the inputs @@ -20,37 +20,31 @@ Ask for what is not in `$ARGUMENTS`; do not invent any of these: - **title** (short, imperative) and a **slug** (kebab-case, from the title) -- **wave** 1 to 3, which fixes the ordinal range (spec 002 ยง5): - - | wave | ordinals | - |---|---| - | 1 | 010 to 019 | - | 2 | 020 to 029 | - | 3 | 030 to 039 | - -- **kind**: one of `kernel`, `feature`, `tooling`, `governance` - (`constitutional-bootstrap` and `thesis` are taken; `spec-spine.toml` - `[kind] allowed` is the closed enum) -- **domain**: `governance`, `types`, `store`, `ledger`, `kernel`, - `identity`, `edge`, `ops` - (`[domains] allowed`, also closed) -- **risk**: `low`, `medium`, `high`, `critical` (critical touches hashed - bytes or trust decisions) -- **depends_on**: existing ids only, every one lower-numbered than the - new ordinal. Check against `spec-spine registry list --ids-only`. -- **summary** (one paragraph) and the **territory** (crate, modules, - seams), enough to fill `establishes` with real paths +- **kind** and **domain**, when `spec-spine.toml` closes those enums + (`[kind] allowed`, `[domains] allowed`); read the lists from the file, + never from memory +- **risk**: `low`, `medium`, `high`, `critical` (critical touches a + hashed byte, a trust decision, or a never-touch artefact) +- **depends_on**: existing ids only, checked against + `spec-spine registry list --ids-only`. A dependency must be lower-numbered + unless the project says otherwise. +- **summary** (one paragraph) and the **territory** (the files, sections, + or symbols it will own), enough to fill `establishes` with real paths +- any extra frontmatter keys the project declares in `[frontmatter] + extra_known_keys` (a wave, a phase, a platform) ## Step 2: pick the ordinal ```sh -ls specs/ | cut -c1-3 | sort -n +spec-spine registry list --ids-only ``` -The id is the lowest unused ordinal inside the wave's range, zero-padded -to three digits, joined to the slug: `NNN-slug`. Refuse when the range is -full (say so; renumbering is a human decision). The directory name must -equal the id (spec 000, "directory name equals id"). +The id is the next free three-digit ordinal after the highest in the +registry, joined to the slug: `NNN-slug`. Never derive it from +`ls specs/`: a stale checkout collides. Projects that partition ordinals +into ranges (waves) say so in `AGENTS.md`; follow that and refuse when +the range is full (renumbering is a human decision). The directory name +must equal the id. ## Step 3: create the file @@ -61,59 +55,71 @@ cp standards/spec/templates/spec-template.md specs/<id>/spec.md Then edit `specs/<id>/spec.md`: -- Frontmatter: `id`, `title`, `status: draft`, `kind`, `domain`, - `created` (today, `YYYY-MM-DD`), `authors`, `implementation: pending`, - `risk`, `wave`, `depends_on`, `summary`. Replace the commented - typed-edge examples with real `establishes` paths (the manifest, every - source file, every test, fixtures as a subtree) and any `extends`, +- Frontmatter: `id`, `title`, `status: draft`, `kind`, `created` (today, + `YYYY-MM-DD`), `implementation: pending`, `risk`, `depends_on`, + `summary`, and the project's extra keys. Replace the commented typed-edge + examples with real `establishes` paths (the manifest, every source + file, every test, fixtures as a subtree) and any `extends`, `constrains`, or `references` edges the territory needs. Drop the template's inline comments. -- Body: the numbered sections in template order (Purpose, Territory, - Behavior as B-n with MUST/SHOULD/MAY, Functional requirements as - FR-nnn, Acceptance criteria as AC-n, Out of scope, Resolved decisions, - `## Verification`). The Verification block holds the `verify:cli` - commands that prove the AC-n; `scripts/verify-spec.sh` runs them after - merge, so each must exist once the spec is built. +- Body: the numbered sections in template order (purpose, territory, + behavior with MUST/SHOULD/MAY, functional requirements, acceptance + criteria, out of scope, resolved decisions, `## Verification`). The + Verification block holds the `verify:cli` commands that prove the + acceptance criteria; `scripts/verify-spec.sh` runs them after merge, so + each must exist once the spec is built. - No em dash anywhere in the text. +CHECKPOINT when an edge touches another spec's territory (an `extends`, +`amends`, or `supersedes`): present the frontmatter before writing the +body, since the edge is a claim about someone else's unit. + The `PostToolUse` hook recompiles the registry after a spec edit; that is expected. -## Step 4: validate in a temporary copy, then the DAG +## Step 4: validate Compile and lint a copy so nothing is judged against a half-written -working tree: +working tree, then the real tree: ```sh T=$(mktemp -d) && cp -R spec-spine.toml standards specs "$T"/ \ && spec-spine compile --repo "$T" && spec-spine lint --fail-on-warn --repo "$T" -scripts/spec-dag.sh "$T" ``` `--fail-on-warn` is what the gate runs. If `compile` complains about a -path referenced from outside `specs/` (a `references` edge into -`docs/design/`), add that directory to the copy and re-run. Fix every -diagnostic in the real file, re-copy, re-run, until all three exit 0. -`scripts/spec-dag.sh` refuses a cycle (naming it), a dependency on a -higher-numbered id, and an unknown id. +path referenced from outside `specs/` (a `references` edge into a docs +directory), add that directory to the copy and re-run. Fix every +diagnostic in the real file, re-copy, re-run, until both exit 0. A +dependency cycle is refused by `compile` itself (`V-014`). Then the real gate, which regenerates and checks the committed shards: ```sh -make spine +spec-spine compile && spec-spine index && spec-spine lint --fail-on-warn && spec-spine index check +spec-spine registry plan ``` +`plan` shows where the new id sits: blocked on its dependencies, or ready +the moment a human approves it. + ## Step 5: commit and report On a feature branch named after the new id (`git switch -c <id>`), -`/commit` as `docs(<NNN>): draft spec <id>` with `.derived/` staged +`/commit` as `docs(<NNN>): draft spec <id>` with the derived shards staged alongside, then `/ship` when the draft is ready for review. -Report the id, wave, kind, domain, risk, dependencies, and: +Report the id, kind, risk, dependencies, the units claimed, and: - `status: draft` is deliberate. Approval (`status: approved`) is a human flip made in the file after review; nothing in this skill or in a driven session performs it. -- Spec 002's sequencing plans list each wave's specs. Adding the new id - there is a change to the thesis and is a human call; say so rather than - editing spec 002. +- If the project keeps a sequencing plan in a thesis spec, adding the new + id there is a change to that spec and a human call; say so rather than + editing it. + +## Project layer + +Read from `spec-spine.toml`: the closed enums and extra keys. Read from +`AGENTS.md`: any ordinal-range convention and the thesis spec, if one +exists. Nothing here is edited per project. diff --git a/.claude/skills/validate-and-fix/SKILL.md b/.claude/skills/validate-and-fix/SKILL.md index 05a8507..989ec76 100644 --- a/.claude/skills/validate-and-fix/SKILL.md +++ b/.claude/skills/validate-and-fix/SKILL.md @@ -1,63 +1,62 @@ --- name: validate-and-fix -description: "Run make ci (the exact gate CI runs) and fix what it surfaces by severity, with coupling failures and memory-invariant changes escalated to a human." +description: "Run the local CI composite (the exact gate CI runs) and fix what it surfaces by severity, with coupling failures that need a spec decision and never-touch artefacts escalated to a human." allowed-tools: Bash, Read, Edit, Glob, Grep, Agent --- # /validate-and-fix -Run the local CI loop and fix what it surfaces. `Makefile` is the single -source of truth for what CI validates (spec 001 B-2): if `make ci` passes -locally, `.github/workflows/govern.yml` passes too. Do not rediscover -validation commands by grepping manifests. +Run the local CI loop and fix what it surfaces. The composite `AGENTS.md` +names (commonly `make ci`) is the single source of truth for what CI +validates: if it passes locally, CI passes too. Do not rediscover +validation commands by grepping manifests; read the composite's +definition. ## 1. Run the composite -```sh -make ci -``` - -which runs, in order: `spec-spine compile`, `spec-spine index`, -`spec-spine lint --fail-on-warn`, `spec-spine index check`, `spec-spine -couple --base origin/main --head HEAD`, `scripts/spec-dag.sh`, `spec-spine -index coverage --fail-on-untraced`, and, once `Cargo.toml` exists, `cargo -build --workspace --locked`, `cargo test --workspace --locked`, `cargo -clippy --workspace --all-targets --locked -- -D warnings`, `cargo fmt --all ---check`, and `cargo deny check`. Run `git fetch origin main` first if the -coupling gate cannot find its base. +Run the gate exactly as `AGENTS.md` "Working the backlog" lists it under +"Run the gate before every commit". The governance floor is +`spec-spine compile`, `spec-spine index`, `spec-spine lint --fail-on-warn`, +`spec-spine index check`, `spec-spine couple --base origin/main --head HEAD`, +and `spec-spine index coverage --fail-on-untraced` where ownership is +required; the stack's build, tests, and lints follow. Run +`git fetch origin main` first if the coupling gate cannot find its base. Capture full output (file paths, line numbers, messages) and categorize: - **CRITICAL** (human decision, do not fix silently): a coupling failure - (`C-001` drift or `C-002` unclaimed path) that would need an owning spec - edited to clear; any change that relaxes a memory invariant - (`.claude/rules/memory-invariants.md`); a `spec-dag` cycle; an edited - evaluation corpus or baseline under `eval/`; a retrieval delta that - regresses without a recorded decision. -- **HIGH**: test failures, build breaks, index staleness, a `C-002` whose - remedy is claiming the file in the spec being implemented. + (`C-001` drift, or a `C-002` whose only remedy is editing a spec you + are not implementing); any change to a never-touch artefact the + path-scoped rules name (a golden vector, a fixture chain, an evaluation + baseline); a dependency cycle; an ambient input (clock, environment, + unordered map) reaching a hashed path. +- **HIGH**: test failures, build breaks, index or registry staleness, a + `C-002` whose remedy is claiming the file in the spec being implemented + (the legitimate edit `.claude/rules/adversarial-prompt-refusal.md` + names). - **MEDIUM**: `spec-spine lint` warnings (the gate runs `--fail-on-warn`), - clippy findings, type errors, `cargo deny` advisories with a fix. -- **LOW**: `cargo fmt`, wording in prose, minor cleanups. + linter findings, type errors, dependency advisories with a fix. +- **LOW**: formatting, wording in prose, minor cleanups. -If a check is missing from the composite, add it to `Makefile` and to the -workflow in the same change; never introduce a validation as a one-off -script. +If a check is missing from the composite, add it to the composite and to +the CI workflow in the same change; never introduce a validation as a +one-off script. ## 2. Fix by phase - **Phase 1, safe quick wins**: LOW and MEDIUM findings that cannot break - anything. Verify each by re-running the narrowest target (`make fmt`, - `make lint`, `cargo test -p <crate> --locked --test <file>`). + anything. Verify each by re-running the narrowest target the stack + offers (a formatter, one lint, one test file). - **Phase 2, functionality**: HIGH findings one at a time; re-run the affected target after each. Never disable or skip a failing test; fix - the cause. + the cause. Stale shards: run `compile` and `index`, stage the derived + directory with the change that made them stale. - **Phase 3, critical**: present each CRITICAL finding with the evidence and a proposed remedy, then wait. Refusing the destructive step is - sometimes the right answer (`.claude/rules/adversarial-prompt-refusal.md`). - A retrieval regression on the evaluation corpus is a finding to report - with the delta, never a baseline to move. -- **Phase 4, verification**: re-run `make ci` end to end. + sometimes the right answer. A never-touch artefact that would change + means the encoding or the baseline changed: that is a schema or design + decision, a spec amendment, and a human decision, in that order. +- **Phase 4, verification**: re-run the composite end to end. ## 3. Error handling @@ -65,19 +64,19 @@ script. change; offer instant rollback if a fix regresses. - **Partial success**: continue past a fix that fails; separate successes from failures; give manual instructions for what you could not fix. -- **Governed reads**: read `.derived/` only through `spec-spine` +- **Governed reads**: read the derived directory only through `spec-spine` subcommands (`.claude/rules/governed-artifact-reads.md`). ## 4. Parallel execution -Launch several agents concurrently only for independent fixes in -different crates that touch non-overlapping files; keep ordered or -cross-crate changes sequential. Each agent verifies its own fix with the -narrowest target before reporting. +Launch several agents concurrently only for independent fixes that touch +non-overlapping files; keep ordered or cross-cutting changes sequential. +Each agent verifies its own fix with the narrowest target before +reporting. ## 5. Final verification -Re-run `make ci`, confirm no new findings, and summarize: +Re-run the composite, confirm no new findings, and summarize: `Fixed X/Y issues, Z require human decision. CI: {PASS|FAIL}`. ## Substrate notes @@ -85,25 +84,19 @@ Re-run `make ci`, confirm no new findings, and summarize: - `spec-spine lint` runs with `--fail-on-warn`: a warning is a failure. - The coupling gate compares `HEAD` against `origin/main`; fetch first. - The codebase index hashes more than `spec.md`: `spec-spine.toml - [index] extra_hashed_inputs` lists `.claude/**`, `AGENTS.md`, - `CLAUDE.md`, `Makefile`, `docs/design/**`, workflows, and standards. - Editing any of them without regenerating the index fails the staleness - check; the `Stop` hook regenerates it for you outside a rebase. -- `.claude/settings.json` and `.mcp.json` are hashed byte for byte: - editor reformatting trips the gate even when the JSON is unchanged. -- The ownership ratchet is on: every source file inside a crate must be + [index] extra_hashed_inputs` lists the harness, design docs, workflows, + and standards. Editing any of them without regenerating the index fails + the staleness check. The hooks only report staleness; they never + regenerate. The session runs `spec-spine index` and commits the result. +- `.claude/settings.json` and `.mcp.json` are hashed byte for byte when + listed as hashed inputs: editor reformatting trips the gate even when + the JSON is unchanged. +- With `[coupling] require_ownership` on, every source file must be claimed by a spec (`spec-spine index coverage` shows the debt, always zero on a green tree). -## The aicortex post-feature checklist - -After feature work, beyond what the gates enforce: +## Project layer -- Every new `pub` item is named or covered in its spec's Behavior section. -- No `HashMap` iteration or `std::env` read reaches the manifest hash or - a recall trace. -- New source kinds, scopes, and MCP tools appear in the owning spec's - Behavior section. -- New third-party crates are in `[workspace.dependencies]` and the spec - declares the `extends` edge on spec 010's section. -- The spec's `## Verification` block still names a command that exists. +Read from `AGENTS.md`: the composite and the stack gate. Read from +`.claude/rules/`: the never-touch artefacts and any post-feature checklist +the project keeps. Nothing here is edited per project. diff --git a/.claude/skills/verify/SKILL.md b/.claude/skills/verify/SKILL.md index 7fd936a..39649fe 100644 --- a/.claude/skills/verify/SKILL.md +++ b/.claude/skills/verify/SKILL.md @@ -1,6 +1,6 @@ --- name: verify -description: "Run one spec's verify:cli blocks locally through scripts/verify-spec.sh, the same script claude-observatory's verify stage runs after merge, and report every command's exit code." +description: "Run one spec's verify:cli blocks locally through scripts/verify-spec.sh, the same protocol an orchestrator's verify stage runs after merge, and report every command's exit code honestly." allowed-tools: Bash, Read, Grep argument-hint: "<spec-id>" --- @@ -8,26 +8,26 @@ argument-hint: "<spec-id>" # /verify <spec-id>: the spec's Verification block, locally Runs the `## Verification` section of `specs/<id>/spec.md` exactly the way -the orchestrator's verify stage will run it after merge (spec 001 FR-002): -every non-comment line inside a ```` ```verify:cli ```` fence, from the -repo root, in order, stopping at the first non-zero exit. This is the -local rehearsal of step 7 of `AGENTS.md`, "Working the backlog". +an orchestrator's verify stage will run it after merge: every non-comment +line inside a ```` ```verify:cli ```` fence, from the repo root, in order, +stopping at the first non-zero exit. This is the local rehearsal of the +"Satisfy the spec's acceptance criteria verbatim" step of `AGENTS.md`, +"Working the backlog". ## Step 0: scope -- The argument is the full spec id (`017-ledger-entry-dag`). Without one, - stop and ask; do not guess from the branch name. +- The argument is the full spec id. Without one, stop and ask; do not + guess from the branch name. - `git status --porcelain`: the post-merge run happens in a clean checkout of the merged sha, so a pass that depends on an uncommitted file is not a pass. Warn when the tree is dirty and say which files. -- Read the spec's `## 5. Acceptance criteria` and `## Verification` once, - so the report can say which criterion each command exercises. +- Read the spec's acceptance criteria and its `## Verification` section + once, so the report can say which criterion each command exercises. ## Step 1: run ```sh scripts/verify-spec.sh <spec-id> -# equivalent: make verify SPEC=<spec-id> ``` The script prints `[verify] $ <command>` before each command and @@ -41,24 +41,26 @@ The script prints `[verify] $ <command>` before each command and exit 0. - `verify: no such spec` or a usage line, exit 2. +The script reads the spec markdown, never `.derived/`; it is the kit's +`scripts/verify-spec.sh`, copied into the repository with the skill. + ## Step 2: read the result honestly - **`not-declared` is an honest zero, not a pass.** It means the spec declares nothing runnable. For a spec whose acceptance criteria are - mechanically checkable, that is a gap to report in the session summary - (the implementing session may add a `## Verification` block with the - commands that prove its AC-n; that is a legitimate mid-build edit, like - `establishes` growth). Never remove or weaken an existing block to make - it pass: that is the coherence guard + mechanically checkable, that is a gap to report in the session summary. + The implementing session may add a `## Verification` block with the + commands that prove its criteria; that is a legitimate mid-build edit, + like `establishes` growth. Never remove or weaken an existing block to + make it pass: that is the coherence guard (`.claude/rules/adversarial-prompt-refusal.md`). -- **`verify:browser` blocks** are reported as skipped. Only the - orchestrator drives those (its browser stage); nothing here can satisfy +- **`verify:browser` blocks** are reported as skipped. Only an + orchestrator with a browser stage drives those; nothing here can satisfy or fail them, so say so rather than treating the skip as coverage. -- **A failure** is either the code (fix it, then re-run `make ci` and this +- **A failure** is either the code (fix it, re-run the gate and this skill) or a criterion that cannot be satisfied here (external state, a missing sibling service): then keep `implementation: in-progress`, add a - dated Status note to the spec saying exactly what remains, and report it - (`AGENTS.md`, "Working the backlog", step 7). + dated status note to the spec saying exactly what remains, and report it. ## Step 3: report @@ -66,7 +68,7 @@ The script prints `[verify] $ <command>` before each command and ## verify: <spec-id> tree: clean | dirty (<files>) commands: N - 1. <command> exit <code> (AC-n) + 1. <command> exit <code> (<criterion label>) 2. ... result: passed | FAILED at command N | not-declared browser blocks: none | <n> skipped (orchestrator-driven) @@ -74,3 +76,8 @@ browser blocks: none | <n> skipped (orchestrator-driven) Quote the failing command's output tail when there is one. Do not paraphrase an exit code. + +## Project layer + +Nothing here is project-specific. If the corpus lives somewhere other +than `specs/`, export `SPECS_DIR` for the script. diff --git a/.derived/codebase-index/by-spec/000-aicortex-bootstrap.json b/.derived/codebase-index/by-spec/000-aicortex-bootstrap.json index db444a5..aa28451 100644 --- a/.derived/codebase-index/by-spec/000-aicortex-bootstrap.json +++ b/.derived/codebase-index/by-spec/000-aicortex-bootstrap.json @@ -5,5 +5,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "b482e59b93f165e5a810def9ba4dfb06dbae9380f8070800994a2262309ab07e" + "shardHash": "b0988444ee87a392fa28cd6df09daa8af476b0b674f3dd202a5b2f4393ad96a7" } diff --git a/.derived/codebase-index/by-spec/001-agentic-harness.json b/.derived/codebase-index/by-spec/001-agentic-harness.json index 64349e9..797afd2 100644 --- a/.derived/codebase-index/by-spec/001-agentic-harness.json +++ b/.derived/codebase-index/by-spec/001-agentic-harness.json @@ -279,5 +279,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "358a76b314c46ab78dc4bc5cc8fdbe967bcc7c380e3897d92b1227a5de93bc8c" + "shardHash": "bf9cbaba3893c3ae3ae2d6a8f159085f62d84f328a7bb7a0b7b7f6c4af7673fe" } diff --git a/.derived/codebase-index/by-spec/002-memory-thesis.json b/.derived/codebase-index/by-spec/002-memory-thesis.json index c3df9c7..31b2310 100644 --- a/.derived/codebase-index/by-spec/002-memory-thesis.json +++ b/.derived/codebase-index/by-spec/002-memory-thesis.json @@ -23,5 +23,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "4b481288c46d3ab3871a48300013173034f4b222b36f17edf117df579c5afaf7" + "shardHash": "6fed51f5510ba175d67161c7040d90583880db0b6b7d3beacd247392215afc3c" } diff --git a/.derived/codebase-index/by-spec/010-chassis-adoption-and-workspace.json b/.derived/codebase-index/by-spec/010-chassis-adoption-and-workspace.json index 92313a3..ab28db8 100644 --- a/.derived/codebase-index/by-spec/010-chassis-adoption-and-workspace.json +++ b/.derived/codebase-index/by-spec/010-chassis-adoption-and-workspace.json @@ -127,5 +127,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "880a2b494439043f09b074d2fffbcdfcbe776bdc9d76cfd92c8d5a25deeee842" + "shardHash": "6623e94c19302cc89067157913e0435b726f9e807b45122cbf7edc4aec744815" } diff --git a/.derived/codebase-index/by-spec/011-memory-model.json b/.derived/codebase-index/by-spec/011-memory-model.json index 27ef606..7ed0d0f 100644 --- a/.derived/codebase-index/by-spec/011-memory-model.json +++ b/.derived/codebase-index/by-spec/011-memory-model.json @@ -198,5 +198,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "165f222b2c9b1e34b65645c2247165c3cfb356a35409995ba8ea7c0f5c7ad329" + "shardHash": "4231b571c4db74135c9a58e0baeaaba41438b908dffd6af72401331b33d9f065" } diff --git a/.derived/codebase-index/by-spec/012-store-schema-and-repositories.json b/.derived/codebase-index/by-spec/012-store-schema-and-repositories.json index 21b4f19..a704afb 100644 --- a/.derived/codebase-index/by-spec/012-store-schema-and-repositories.json +++ b/.derived/codebase-index/by-spec/012-store-schema-and-repositories.json @@ -198,5 +198,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "8322dfecfaa500bd01d2d87a8854b3f27fdd3ccc80c00bb8e8b4db7184e98e43" + "shardHash": "2662f40ddbbb08c214aa8731575e511db5c124bacce0ab97fe0fc4f77fe84d56" } diff --git a/.derived/codebase-index/by-spec/013-write-gate-and-redaction.json b/.derived/codebase-index/by-spec/013-write-gate-and-redaction.json index 70d0ac2..a3acb30 100644 --- a/.derived/codebase-index/by-spec/013-write-gate-and-redaction.json +++ b/.derived/codebase-index/by-spec/013-write-gate-and-redaction.json @@ -156,5 +156,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "2d1f51fe1df83c092ace6a68d8cefdff7526de7856a1c94c653dcb7b285d53e7" + "shardHash": "f6c58f541838592c0a2b300afba57b9317cee3c6deaa243b5c71b29a736bef19" } diff --git a/.derived/codebase-index/by-spec/014-memory-lifecycle-and-erasure.json b/.derived/codebase-index/by-spec/014-memory-lifecycle-and-erasure.json index 8dc8424..a0f159e 100644 --- a/.derived/codebase-index/by-spec/014-memory-lifecycle-and-erasure.json +++ b/.derived/codebase-index/by-spec/014-memory-lifecycle-and-erasure.json @@ -127,5 +127,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "850ed9ef1c0d076f27551a1452be85cb87b327115e4fa40ba4da2da7c9d7038d" + "shardHash": "35a1cf861acd29ba4361ce9cd5d07ea29ef64a8828fb1fc04b25d6d00c710432" } diff --git a/.derived/codebase-index/by-spec/015-embedding-pipeline.json b/.derived/codebase-index/by-spec/015-embedding-pipeline.json index 17bf9d3..146f388 100644 --- a/.derived/codebase-index/by-spec/015-embedding-pipeline.json +++ b/.derived/codebase-index/by-spec/015-embedding-pipeline.json @@ -240,5 +240,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "90c52a7cf0c91039c9ecf04a9296892f1b6eb071685f24a404f34ad270cb8cfa" + "shardHash": "6e4e3174f1d4b83dee3e94747adad51492ba5075bcaa91adc1f9eb6ba694909a" } diff --git a/.derived/codebase-index/by-spec/016-vector-index-and-scan.json b/.derived/codebase-index/by-spec/016-vector-index-and-scan.json index 8e274b2..57e219c 100644 --- a/.derived/codebase-index/by-spec/016-vector-index-and-scan.json +++ b/.derived/codebase-index/by-spec/016-vector-index-and-scan.json @@ -183,5 +183,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "8e9eda8d02e2d13bb948688a58d7a0137d9206217ceca14f7ddc490813d444fc" + "shardHash": "acf982200b0cb3687d34715a2389bb01bddb0bcc245588b8aae0b03c98a2fd4f" } diff --git a/.derived/codebase-index/by-spec/017-entities-and-typed-edges.json b/.derived/codebase-index/by-spec/017-entities-and-typed-edges.json index b5efb09..f0202ad 100644 --- a/.derived/codebase-index/by-spec/017-entities-and-typed-edges.json +++ b/.derived/codebase-index/by-spec/017-entities-and-typed-edges.json @@ -211,5 +211,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "c156f079ae0a1a097277d91e42dc6ce849dd2f535ac53312800ad9a18c5c7ed5" + "shardHash": "ec1e79ee83b3158803e0a2f9db0af0c3a527e841b6670a9b11bc3f0db3f7e268" } diff --git a/.derived/codebase-index/by-spec/018-retrieval-and-recall-trace.json b/.derived/codebase-index/by-spec/018-retrieval-and-recall-trace.json index 20b75cf..ba4c303 100644 --- a/.derived/codebase-index/by-spec/018-retrieval-and-recall-trace.json +++ b/.derived/codebase-index/by-spec/018-retrieval-and-recall-trace.json @@ -155,5 +155,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "daebb143cbbe1cd9814cdd6456aa7fa0de528de52b9fa184145b5996ced33335" + "shardHash": "c8f60b044c9ce390800e2dc281b4dc367daa15c0e122c8c0e4cc3c6159814c59" } diff --git a/.derived/codebase-index/by-spec/019-untrusted-content-boundary.json b/.derived/codebase-index/by-spec/019-untrusted-content-boundary.json index 410436f..928ca8d 100644 --- a/.derived/codebase-index/by-spec/019-untrusted-content-boundary.json +++ b/.derived/codebase-index/by-spec/019-untrusted-content-boundary.json @@ -43,5 +43,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "a3515aaf20c3f6808c08f13914bad07d7761a84e6f01d7739cd59df91ab49b0f" + "shardHash": "572cd5498f76b0df0496ef934b9ec569fcc02a75a004f89f3b219456b784e104" } diff --git a/.derived/codebase-index/by-spec/020-http-api-and-scopes.json b/.derived/codebase-index/by-spec/020-http-api-and-scopes.json index 0904e7b..b1b206f 100644 --- a/.derived/codebase-index/by-spec/020-http-api-and-scopes.json +++ b/.derived/codebase-index/by-spec/020-http-api-and-scopes.json @@ -224,5 +224,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "7c7d48eb3a95855291c0267b70d9f4324ba3f5b01cb684defbd6551b981ea847" + "shardHash": "689c5b82e06284fb5a2b9dbcd5dbfd2f081b741b2329f8f69218b51f781a60bb" } diff --git a/.derived/codebase-index/by-spec/021-mcp-server.json b/.derived/codebase-index/by-spec/021-mcp-server.json index 2f694c2..5019af8 100644 --- a/.derived/codebase-index/by-spec/021-mcp-server.json +++ b/.derived/codebase-index/by-spec/021-mcp-server.json @@ -210,5 +210,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "426834c5b62496d29763d8316ece5e36613df4497b1bb61505568009ed4a254e" + "shardHash": "0474a112308a96ece87c0dfff1af44ebb23a3d9f4d9b2d365d86d4d01217e7de" } diff --git a/.derived/codebase-index/by-spec/022-client-adapters.json b/.derived/codebase-index/by-spec/022-client-adapters.json index 9f5c3c3..22b02a5 100644 --- a/.derived/codebase-index/by-spec/022-client-adapters.json +++ b/.derived/codebase-index/by-spec/022-client-adapters.json @@ -141,5 +141,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "079ae38bf5d5538d96a75a44d452d16edc97d5b9045f5acdc05b24c0a420bd50" + "shardHash": "4d603b7a197adb65ba95421b7d9d2b32dc56fcd79cb4bc35c7e8ebf2a2418ed0" } diff --git a/.derived/codebase-index/by-spec/023-review-and-promotion.json b/.derived/codebase-index/by-spec/023-review-and-promotion.json index 396c970..3de0889 100644 --- a/.derived/codebase-index/by-spec/023-review-and-promotion.json +++ b/.derived/codebase-index/by-spec/023-review-and-promotion.json @@ -140,5 +140,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "668caad3fd280b3ef125567d3e9598e0c32ab8189cf9630ff5a62861194642bc" + "shardHash": "b3fbd459c54041034d9b6d8f44e9f3fbef75b4a2f468fd4b6bdec30327936ae8" } diff --git a/.derived/codebase-index/by-spec/024-decision-and-audit-references.json b/.derived/codebase-index/by-spec/024-decision-and-audit-references.json index 8bf361e..a763709 100644 --- a/.derived/codebase-index/by-spec/024-decision-and-audit-references.json +++ b/.derived/codebase-index/by-spec/024-decision-and-audit-references.json @@ -99,5 +99,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "b30bad0cd0c50f3dda3fa75dc50860b7d73cbd6e75c16b837b841325a4c9ecda" + "shardHash": "5f26beea8bc5f2275e61bd9edcb6580adea6c3828e00a6c1b6eaa04d2f351ded" } diff --git a/.derived/codebase-index/by-spec/030-source-adapter-framework.json b/.derived/codebase-index/by-spec/030-source-adapter-framework.json index 3f14ce8..412f1c8 100644 --- a/.derived/codebase-index/by-spec/030-source-adapter-framework.json +++ b/.derived/codebase-index/by-spec/030-source-adapter-framework.json @@ -183,5 +183,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "cd9d93070afca756a0e15a53515335eb1c741c892779f380c6d6b606d5b601f8" + "shardHash": "dd254062802c61b2991f8a61c7868a2662ebc6efc836202ec66885fd0703dd7b" } diff --git a/.derived/codebase-index/by-spec/031-conversation-and-archive-imports.json b/.derived/codebase-index/by-spec/031-conversation-and-archive-imports.json index 9bcb2e9..4f9a930 100644 --- a/.derived/codebase-index/by-spec/031-conversation-and-archive-imports.json +++ b/.derived/codebase-index/by-spec/031-conversation-and-archive-imports.json @@ -168,5 +168,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "f3dbf58688182e8240661739129f82b8749f8f48fe9f3899f1e38f7fc595b356" + "shardHash": "a83b6a21890bd1ab19710d0f929fc2ebd4af622e5a9e281a332e7cd69a0e7749" } diff --git a/.derived/codebase-index/by-spec/032-capture-sources-and-webhooks.json b/.derived/codebase-index/by-spec/032-capture-sources-and-webhooks.json index 16cd9ad..f70371a 100644 --- a/.derived/codebase-index/by-spec/032-capture-sources-and-webhooks.json +++ b/.derived/codebase-index/by-spec/032-capture-sources-and-webhooks.json @@ -140,5 +140,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "18bc194bd0ecd9e90faffeac4b21a43ddb1bbfef865b3fc4504907ba9e0de904" + "shardHash": "65b341915630c3e589fc974e9a2506d9eb3ab85d572317b1826d76efd6fc0816" } diff --git a/.derived/codebase-index/by-spec/033-observatory-and-agent-ingestion.json b/.derived/codebase-index/by-spec/033-observatory-and-agent-ingestion.json index bfdffe4..d841e01 100644 --- a/.derived/codebase-index/by-spec/033-observatory-and-agent-ingestion.json +++ b/.derived/codebase-index/by-spec/033-observatory-and-agent-ingestion.json @@ -98,5 +98,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "23bfb903b9fb0592f8cc4dc9dac64b3540681d5d2bbd34915c429f5469ca6b87" + "shardHash": "be52d4f7adb712b9d6572033adb3ebe42156924a76805bd683d3492b8cf428d7" } diff --git a/.derived/codebase-index/by-spec/034-curation-workers.json b/.derived/codebase-index/by-spec/034-curation-workers.json index 103ff39..8c653aa 100644 --- a/.derived/codebase-index/by-spec/034-curation-workers.json +++ b/.derived/codebase-index/by-spec/034-curation-workers.json @@ -168,5 +168,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "e1dfaf57a849b7a619205b6b8b0f3805022f9ac9a9fc741c9538c9cd491d65c0" + "shardHash": "6ed5c3b1edc5b240cfc71af56b54a4745cee50f9c631f22e7a8deb6fb4498a68" } diff --git a/.derived/codebase-index/by-spec/035-agent-coordination.json b/.derived/codebase-index/by-spec/035-agent-coordination.json index 6e03c28..aac0f99 100644 --- a/.derived/codebase-index/by-spec/035-agent-coordination.json +++ b/.derived/codebase-index/by-spec/035-agent-coordination.json @@ -99,5 +99,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "d50f54414bc7aacc7d8bdf4adac2bda8bc26bc5b90db246c3cdb0b6641cc0e94" + "shardHash": "71e7ee038e91dcf9e7d0604624502de3581b82b2b6867e02379ca03a4bea2f5e" } diff --git a/.derived/codebase-index/by-spec/040-retrieval-evaluation.json b/.derived/codebase-index/by-spec/040-retrieval-evaluation.json index ff666e1..fe0e333 100644 --- a/.derived/codebase-index/by-spec/040-retrieval-evaluation.json +++ b/.derived/codebase-index/by-spec/040-retrieval-evaluation.json @@ -183,5 +183,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "c6c29d332d9328e3ec234bd0a003158037059842f255f568c15ad3134052b431" + "shardHash": "9c61eb58fbb94a21824f6c260c2a2c1456fad7b432130fd1daf2568feed1bfb2" } diff --git a/.derived/codebase-index/by-spec/041-privacy-and-egress-boundary.json b/.derived/codebase-index/by-spec/041-privacy-and-egress-boundary.json index 01d27cf..33611ee 100644 --- a/.derived/codebase-index/by-spec/041-privacy-and-egress-boundary.json +++ b/.derived/codebase-index/by-spec/041-privacy-and-egress-boundary.json @@ -43,5 +43,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "fedf8b699838cceacc5a441ee127d11d1842c4341437214e7cb914725dcdef95" + "shardHash": "a2570c1f0228ded2623d7aec5f394b9f28ab011f077542c213724eae7da1befb" } diff --git a/.derived/codebase-index/by-spec/042-portability-and-migration.json b/.derived/codebase-index/by-spec/042-portability-and-migration.json index 342c449..a2a0bb0 100644 --- a/.derived/codebase-index/by-spec/042-portability-and-migration.json +++ b/.derived/codebase-index/by-spec/042-portability-and-migration.json @@ -127,5 +127,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "dd26aea40346fcb7258eb7fe8e95893e0641d3924d9d587e147148a35d6c11f6" + "shardHash": "ebb5228ffff30ecd56b15dd81ee6b03519802a86f2c72cbc1fc82d038f731e25" } diff --git a/.derived/codebase-index/by-spec/043-packaging-and-operations.json b/.derived/codebase-index/by-spec/043-packaging-and-operations.json index 1496c3a..59296b4 100644 --- a/.derived/codebase-index/by-spec/043-packaging-and-operations.json +++ b/.derived/codebase-index/by-spec/043-packaging-and-operations.json @@ -113,5 +113,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "00f0dcf854cd93a331f0b6ea61e17baa93971c47ea4eb927d2b89e1943973aba" + "shardHash": "a5b0ff1b4a835927c8f01cd5d591aa930c8db4b971a2440ad187fdd619b6ced1" } diff --git a/.derived/codebase-index/by-spec/044-reference-deployment.json b/.derived/codebase-index/by-spec/044-reference-deployment.json index 81625bb..50c8a9f 100644 --- a/.derived/codebase-index/by-spec/044-reference-deployment.json +++ b/.derived/codebase-index/by-spec/044-reference-deployment.json @@ -183,5 +183,5 @@ "specStatus": "approved" }, "schemaVersion": "1.1.0", - "shardHash": "12913e00cb29a64a48f7f654479eddf13b2429ffe9c829faf27884594302148f" + "shardHash": "8c89b235518bdac1d26e2d942cc6419bfeb177f6e1f8ba16d676430f07322ceb" } diff --git a/.derived/codebase-index/slices.json b/.derived/codebase-index/slices.json index 1d362d2..5114467 100644 --- a/.derived/codebase-index/slices.json +++ b/.derived/codebase-index/slices.json @@ -1,5 +1,5 @@ { "deployment": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "evaluation": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "governance": "c56962c13bedf5dbd2aeccdc6afc2eea401a0b577c881bb0ec0a6a4d465f0564" + "governance": "febbcaca2b6b32de104d845e038220b3e23b81cb10c5257a5badf5e5ebddb115" } diff --git a/.derived/spec-registry/by-spec/001-agentic-harness.json b/.derived/spec-registry/by-spec/001-agentic-harness.json index 50f9252..68a9a10 100644 --- a/.derived/spec-registry/by-spec/001-agentic-harness.json +++ b/.derived/spec-registry/by-spec/001-agentic-harness.json @@ -102,6 +102,6 @@ "summary": "The machinery that turns the corpus into work: AGENTS.md as the cross-agent session protocol and backlog discipline, CLAUDE.md as the Claude Code overlay, five behavioral rules of which two are path-scoped, four subagents, fifteen skills, settings hooks that recompile and refuse, one Makefile that is the single definition of the gate, and a CI workflow that runs the same targets. This spec is complete on arrival: the harness exists before the first line of product code.\n", "title": "Agentic engineering harness: session protocol, skills, agents, hooks, and the gate" }, - "shardHash": "c4f9a73446c54f73474eda25fc8e65fb854306d7657efde78a40c688f0935b63", + "shardHash": "6ca13892c3f457ef032a164cc320c75ad61ca07838cf4b921fc735f6eac16aa8", "specVersion": "1.1.0" } diff --git a/AGENTS.md b/AGENTS.md index d558bcf..7108426 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,6 +48,7 @@ to derive its plan; anything added here is picked up on the next init. - `spec-spine index check`: index staleness (non-fatal) - `spec-spine registry status-report --json --nonzero-only`: lifecycle counts - `spec-spine registry list --ids-only`: the spec inventory + - `spec-spine registry plan`: the ready set (spec-spine 038): which specs can be worked on now and what blocks the rest; `/next` applies the approval and in-flight rules on top of it - `spec-spine index coverage`: which source files no spec claims (exit 2 if stale) - `scripts/spec-dag.sh`: the DAG is acyclic and every dependency is lower-numbered - `ls crates/ apps/ eval/ docker/ deploy/ 2>/dev/null`: what has been built so far (absent directories are expected before their spec lands) @@ -160,7 +161,7 @@ Skills live in `.claude/skills/`: - `/init`: this protocol. - `/setup`: install spec-spine and the Rust toolchain; verify the loop. -- `/next`: the next ready spec, with honest blockers when none is ready. +- `/next`: the next ready spec from `registry plan`, minus drafts, with in-flight specs and honest blockers. - `/build <id>`: one spec start to finish per "Working the backlog". - `/verify <id>`: run a spec's `verify:cli` blocks locally. - `/spec`: author a new spec from the template; next ordinal; DAG check. @@ -172,6 +173,12 @@ Skills live in `.claude/skills/`: - `/validate-and-fix`: run `make ci` and fix what it surfaces. - `/cleanup`, `/implement-plan`, `/research`, `/refactor-claude-md`. +The fifteen are the spec-spine kit's, byte for byte (spec-spine spec 048). +The project layer the skills read lives in this file (the pin, the binary, +`make spine` and `make ci` as the gate, the default branch) and in the +path-scoped rules (the memory invariants, the evaluation corpus); do not +edit a skill to add a project fact, add it here. + ## Conventions - Rust 2024, toolchain pinned in `rust-toolchain.toml` (spec 010); always diff --git a/scripts/verify-spec.sh b/scripts/verify-spec.sh index 6bf1c34..6799f3a 100755 --- a/scripts/verify-spec.sh +++ b/scripts/verify-spec.sh @@ -1,11 +1,22 @@ #!/usr/bin/env bash -# verify-spec.sh <spec-id>: run a spec's `verify:cli` blocks locally (spec 001 -# FR-002). This is what claude-observatory's verify stage runs after merge, in -# a clean checkout of the merged sha: every non-comment, non-blank line inside -# a ```verify:cli fence, from the repo root, in order, stopping at the first -# non-zero exit. A spec with no `## Verification` section prints -# `not-declared` and exits 0. `verify:browser` blocks are reported and skipped; -# only the orchestrator drives those. +# verify-spec.sh <spec-id>: run a spec's `verify:cli` blocks locally. +# +# This is what an orchestrator's verify stage runs after merge, in a clean +# checkout of the merged sha: every non-comment, non-blank line inside a +# ```verify:cli fence under the spec's `## Verification` heading (a numbered +# `## 5. Verification` also counts), from the +# repository root, in order, stopping at the first non-zero exit. +# +# passed exit 0 every command exited 0 +# FAILED at N exit c command N exited c; later commands did not run +# not-declared exit 0 no `## Verification` section, or no verify:cli +# commands in it (an honest zero, not a pass) +# no such spec exit 2 (also: usage error) +# +# `verify:browser` blocks are counted and skipped: only an orchestrator with a +# browser stage drives those. The script reads the spec markdown, never +# `.derived/`, and runs commands through `sh -c` from the repo root, so a +# command may reference `spec-spine`, `make`, `cargo`, or anything on PATH. set -u id="${1:-}" @@ -15,15 +26,16 @@ if [ -z "$id" ]; then fi root="$(cd "$(dirname "$0")/.." && pwd)" -spec="$root/specs/$id/spec.md" +specs_dir="${SPECS_DIR:-specs}" +spec="$root/$specs_dir/$id/spec.md" if [ ! -f "$spec" ]; then echo "verify: no such spec: $spec" >&2 exit 2 fi -# The Verification section: from the exact heading to the next H2. +# The Verification section: from the heading (numbered or not) to the next H2. section="$(awk ' - /^## Verification[[:space:]]*$/ { on = 1; next } + /^## ([0-9]+\. )?Verification[[:space:]]*$/ { on = 1; next } on && /^## / { exit } on { print } ' "$spec")" @@ -36,10 +48,8 @@ fi # Fenced blocks: tag on the opening line, body until a bare closing fence. commands="$(printf '%s\n' "$section" | awk ' /^```verify:cli[[:space:]]*$/ { inblock = 1; next } - /^```verify:browser[[:space:]]*$/ { browser = 1; next } - /^```[[:space:]]*$/ { inblock = 0; browser = 0; next } + /^```/ { inblock = 0; next } inblock { print } - END { if (browser_seen) {} } ')" browser_count="$(printf '%s\n' "$section" | grep -c '^```verify:browser' || true)" diff --git a/specs/001-agentic-harness/spec.md b/specs/001-agentic-harness/spec.md index 9db1312..c03dcbf 100644 --- a/specs/001-agentic-harness/spec.md +++ b/specs/001-agentic-harness/spec.md @@ -155,6 +155,26 @@ consumes this repository as a registered target. expected state, not a defect. Follow-ons (`registry plan` in the init reads, `state_dir`, retiring the Python in `/next`) are their own change. +- **D-5 (2026-09-06, kit adoption).** The fifteen skills under + `.claude/skills/` are the spec-spine kit's own (spec-spine spec 048), + taken byte for byte, and the three standing rules are the kit's spec 047 + text. The kit moved every project fact out of the skills into + `AGENTS.md` and the path-scoped rules, which this repository already + held (`make spine`, `make ci`, the 0.14.0 pin, `memory-invariants`), so + nothing was lost in the swap and a future kit update is a copy. This + also retires the copy residue the audit found: the `build` skill's + dangling `ledger-invariants` and `trust-invariants` references, the + `spec` skill's domain enum copied from another corpus (the skill now + reads `[domains] allowed` from `spec-spine.toml`), and the + `code-review` heading mismatch. In substance: `/next` wraps + `spec-spine registry plan` and drops the Python readiness script (the + D-4 follow-on), `/code-review` uses `compile --check` so a review never + writes, `/commit` carries the session-link and em-dash bans, and + `scripts/verify-spec.sh` is the kit's copy. B-5's hooks are unchanged: + the kit's hooks now read and never write (spec-spine spec 046), and + porting them changes what B-5 requires of the `PreToolUse` and `Stop` + hooks, which is an amendment for a human to file, not a mid-build edit. + ## Verification ```verify:cli