A public presentation of source authority
The specification
catalog.
Read what is declared. Follow the pinned source. Lifecycle and implementation are shown independently; verification and deployment qualification remain unknown here.
328 specifications · 9 repositories
action-gate ↗
Deterministic decisions · 3 shown
action-gate bootstrap (pure decision gate over a check registry)
Bootstrap spec for the action-gate repository: a two-crate pure, deterministic decision gate that evaluates an ActionContext against an ordered, pluggable Check registry and returns Allow/Deny/Degrade. Extracted (relicensed Apache-2.0 by the sole copyright holder) from the Open Agentic Platform's crates/policy-kernel/lib.rs (the Case-B extraction: OAP's hardcoded six-gate evaluate becomes a Check registry over a domain-neutral context). Depends on canonical-keysort-json for reproducible decision hashing. This spec establishes the workspace skeleton and seeds this repo's own spec corpus, governed by the pinned spec-spine library.
Secret detector parity: a regex-free detector registry with ids and offsets, and an opt-in deny-by-default terminal check
Converges the family's secret detection upward into action-gate-core. aicortex-gate's detectors (aicortex spec 013 B-4: 24 published-prefix rules, 7 private-key armour markers including the PGP block, a URL credential detector, a JWT detector, and a Q16 fixed-point entropy detector) move here with their stable detector ids and byte offsets, joined by action-gate's own `api_key =` style assignment rule, as one pure, regex-free registry. A new `SecretScanCheck` reports which detector fired and where without echoing the value. The published `SecretsCheck` and the gate's allow-when-silent default are unchanged; a documented, opt-in `DenyByDefault` terminal check lets a consumer close the gate. Golden vectors ported from aicortex's corpus ship with the crate so aicortex spec 047 can assert parity by vector id. Semver-minor (0.2.0).
Adopt Statecraft profile revision 14 with the ordinary Rust code gate
Authorizes the narrow migration from Statecraft github-actions-rust revision 13 to revision 14 after the spec-spine 0.28.0 bridge and pin sequence. Action Gate keeps its exact engine pin, ordinary Rust code gate and all-features job. The managed authority files are regenerated by the revision 14 producer using the retained setup parameters.
aicortex ↗
Persistent memory · 38 shown
Bootstrap spec system for aicortex (specify first, build by spec, clean of the system it replaces)
The founding spec. It fixes the authoring boundary (markdown in, compiler-owned JSON out), the identity rule (directory name equals id), the typed authority graph, the closed taxonomies, the lifecycle that makes a spec a work order, and the gate chain. It also fixes the two rules that are specific to this product: the coherence guard that forbids amending a spec to ratify code, and the clean-room rule that separates what may be carried from the system this product replaces (observations) from what may not (its expression).
Agentic engineering harness: session protocol, skills, agents, hooks, and the gate
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, six behavioral rules of which three are path-scoped, four subagents, ten skills, settings hooks that report and refuse, one Makefile that is the single definition of the gate and splits its reading half from its writing half, 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.
Memory thesis: nine responsibilities, one governed cell, provider agnostic by protocol
What aicortex is, what it refuses, and the order it is built in. It is persistent memory for AI clients, delivered as a governed cell on the rahi chassis, reached by any client that speaks MCP over streamable HTTP. Nine responsibilities across eight domains, thirteen crates and one binary, four build waves. The refusals are as load-bearing as the claims: no second store, no static key, no fork-per-feature, no local stdio server, and no memory that a model may treat as an instruction unless a human promoted it.
The cell: a Cargo workspace, pinned rahi crates, a declared manifest, and one line of main
The workspace and the seam to the chassis. rahi crates are pinned to one exact version in the workspace manifest and are never vendored or patched. The application is a `Cell`: it returns a manifest, a migration list, a router, and an operator router, and `main` hands it to rahi's runner, which owns argument parsing, boot, the verbs, and the exit codes. The manifest is the capability ceiling, and it starts closed: no egress host is declared until a spec needs one.
The memory record: scope, trust class, actor, provenance, and the states a memory can be in
What a memory is, as types, before anything can store one. A memory is a claim by a named actor, from a named source, at a known time, inside a scope owned by a rauthy subject, carrying a trust class that decides whether a client may act on it and a status that decides whether it is still true. The type system enforces the parts that matter: there is no way to construct a memory without provenance, and no way to promote one to instruction grade without an authority token that only a human decision produces.
Schema and repositories: versioned migrations, scope in the predicate, counters instead of scans
The tables and the only code allowed to touch them. Schema arrives as an ordered list of rahi migrations with versions, never as SQL a user pastes from a guide. Every repository call takes a scope and puts it in the SQL, so an unscoped read is not expressible. Aggregates are maintained counters updated in the writing transaction, because the predecessor's statistics call read every row's metadata into memory. Listing is keyset-paged; there is no offset pagination and no unbounded select.
The write gate: refuse secrets, cap size, establish origin, quarantine the rest
Everything that enters the store passes one gate, and the gate runs before the transaction opens. It refuses credentials rather than redacting them, because a redacted secret is still a secret that was transmitted, stored in a log, and possibly embedded. It caps size, requires an establishable origin, and quarantines rather than admits when origin is doubtful. Every refusal is a ledgered Decision with a reason code and never the offending content. The rule set is data, the corpus of fixtures is committed, and the gate is a pure function so its verdicts are reproducible in review.
Lifecycle: supersession, correction, expiry, deduplication, and erasure that reaches the vectors
A memory store that can only append becomes wrong over time and then confidently recalls the wrong thing. This spec gives a memory the rest of its life: an exact fingerprint that merges a repeated capture instead of duplicating it, supersession that keeps the old row readable but unretrievable, correction that records who said the old value was wrong, expiry for memories that were true until a date, and erasure that removes content, chunks, embeddings, and index entries in one transaction while the ledger records only that it happened.
Embeddings: a local default, a governed provider trait, an outbox worker that cannot lose a memory
The predecessor captured a memory in two non-atomic calls: insert the row, then update the embedding. A failure between them produced a memory that search could never reach and nothing ever retried. This spec makes the second half a durable job: capture stages outbox work in the same transaction as the row, a worker drains it idempotently with backoff and a dead letter state that preflight reports, and every stored vector names the model, dimension, and normalization it was produced under so a model change is a migration rather than a silent reinterpretation.
The index: a paged cosine scan, an application-owned text index, and no SQLite extension
Similarity is computed in this process. Vectors are BLOBs read through rahi's paged read, scored by dot product against a normalized query, and reduced by a bounded top-k heap, with the scope predicate in the SQL so a scan never leaves its scope. The text channel is an application-owned inverted index rather than FTS5, because FTS5 is not compiled into hiqlite's SQLite build and a loadable extension is refused by the chassis. A trait boundary exists so an approximate index can replace the scan when the evaluation corpus says it must, and not before.
Entities and typed edges: a closed predicate vocabulary, derived facts that never outrank their source
Similarity alone answers "what looks like this", not "what do I know about this person". Entities give recall a second axis: people, projects, organizations, places, and artifacts, resolved to canonical records with aliases, connected by typed edges from a closed vocabulary. Every entity and every edge names the memory it was derived from and the extractor version that produced it, and a derived assertion never outranks the memory it came from. Extraction is a job on the same outbox as embedding, so it inherits the retry and dead-letter discipline rather than inventing its own.
Recall: four channels fused into one order, every result explainable, scope in the SQL
One query, four candidate channels (vector, text, recency, graph), one fused order, and a trace that explains it. Fusion is reciprocal rank with declared weights and an importance prior, chosen because it needs no score calibration between channels that do not share a scale. Every result set carries the query, the per-channel candidates and scores, the fusion arithmetic, and the reason each returned memory made the cut. A ranking that cannot be explained is not shippable, and an evaluation corpus (040) is the only way a weight changes.
Recalled content is data: delimited, labelled, never in an instruction position
A memory store feeds text written by other people, other agents, and web pages into a model's context, which makes it an indirect prompt-injection surface by construction. This constraint spec owns no code and freezes one property across every path that returns content: memory bodies leave this system only inside an envelope that delimits them, labels their trust class and origin, and states that instructions inside them are not to be followed. Instruction grade, the only class a client is told to act on, requires a human promotion recorded in the ledger. An agent never promotes its own memory.
The HTTP API: bearer-authorized, scope-gated, streaming where it earns it
The product's own HTTP surface, mounted by the cell into rahi's edge. Every route is authorized by a rauthy-issued bearer token or a browser session, both resolved to the same principal, and gated by an OAuth scope that names what it permits. Scopes are coarse enough to be understandable and fine enough to be useful: read, write, delete, admin, a separate scope for promotion, which no agent client is ever granted, and a read and a write scope for coordination, which no memory scope implies. Long operations report progress over server-sent events through the chassis's declared streaming routes. Every response carrying content obeys the untrusted content boundary.
The MCP server: streamable HTTP, a small tool surface, OAuth discovery that bootstraps itself
One remote MCP server over streamable HTTP, stateless per request, serving every client that speaks the protocol. Seven tools, no more: capture, search, fetch, list, update, forget, and relate. Authorization is the chassis's resource server, so an unauthenticated initialize answers the challenge that lets a client discover the authorization server and register itself without the user pasting anything. Tool results carry envelopes and the framing statement, and the tool descriptions themselves never contain recalled content.
Client adapters: one capability matrix, four install paths, zero client code in the core
Connecting is one URL, and everything a client needs beyond that is generated rather than written by hand. This spec owns the capability matrix: which clients support remote MCP, which support OAuth against it, and what each offers for automatic capture, which is the only place they genuinely differ. It ships per-client configuration snippets and the hook or notification payloads that turn a session into captured memories, and a route that serves the correct snippet for a client so setup is a copy rather than a document to follow.
Review: the queue where quarantine, contradiction, and promotion wait for a human
Three situations need a person: content whose origin could not be established and was quarantined, two edges that contradict each other, and a memory that someone wants a client to act on. This spec is the one queue where all three wait, the decisions that empty it, and the ledger records those decisions produce. Promotion is the important one: it is the only path to instruction grade, it requires the promote scope and an interactive principal, and it writes a rahi Decision whose id becomes part of the memory's trust.
One chain, referenced: decisions live in rahi's ledger and memories point at them
There is exactly one hash chain in a deployment and it belongs to the chassis. This product appends Decisions to it and stores references to them; it never builds a second chain, and it never puts memory content into the one that exists. The audit view is a join: a memory's history is its lifecycle rows plus the decisions that reference it, rendered in one timeline. This also fixes the vocabulary, because the sibling product in this family has its own object called a ledger and two things called ledger in one deployment would be a permanent source of confusion.
Source adapters: one contract, incremental and resumable, so a capability is never a fork
The predecessor's ecosystem grew by forking the server: fifty-odd capabilities, each a copy of the same six hundred lines, so a fix in one never reached the others. This spec is the alternative. A source is an implementation of one trait, registered by name, driven by one runner that owns fetching pages, mapping to candidates, passing the gate, staging the transaction, recording a resumable cursor, and reporting progress. Adding a source is a module and a fixture set, and it inherits dedup, retries, provenance, egress governance, and the untrusted content boundary without restating any of them.
Bringing a history in: chat exports, note vaults, and read-later archives
A memory system is worth little on day one and a great deal once it knows what you already said. This spec brings a user's existing history in: chat exports from the major assistants, a note vault on disk, a read-later archive, and mail, each as an adapter over the framework. The interesting work is not fetching, it is turning a transcript into memories worth keeping rather than a wall of every message, and doing it without sending the archive anywhere.
Live capture: signed webhooks from chat systems and the browser, quarantined by default
Capture that happens while you work: a message forwarded from a chat system, a page clipped from the browser. Both arrive as webhooks, which means both are unauthenticated in the OAuth sense and must be treated accordingly. Signatures are verified per platform, the external identity is bound to a local subject by an explicit linking step, and content from a channel other people can write to is quarantined by default rather than trusted because it arrived over a verified transport.
Machine intake: session decisions from the orchestrator and memories written by agents
The most valuable memories in a software team are the decisions made inside driven sessions and then forgotten when the session ends. This spec accepts them: a generic session-event ingestion contract that any orchestrator or agent runtime can post to, with claude-observatory as its first producer, and a decision feed that turns sealed session decisions into memories. It is deliberately not Claude-specific, and it is deliberately a push into a documented schema rather than a reader of any particular tool's private files.
Curation: atomize, enrich, consolidate, expire, digest, all bounded and reversible
Without curation a memory store becomes a landfill: long notes that match nothing, near-duplicates from three sources, facts that stopped being true, and no way to see what a week contained. Five workers address that, all sharing one discipline: they run under a lease in bounded batches, every change they make is a lifecycle transition that is visible and reversible, they never delete, and anything they produce inherits the lowest trust class of its inputs. A curator can propose; only a human promotes.
Agent coordination: per-agent identity, work claims, and a shared working memory
When several agents work in one scope they need to know who did what and to avoid doing the same thing twice. Per-agent identity already exists, because each client is its own OAuth client; this spec adds the two things that build on it. A work claim is a renewable, application-owned hold on a named unit of work with its own fencing token, so two agents cannot both take it. A working-memory scope is a short-lived, high-churn area where agents post what they are doing, visible to their siblings and expired automatically, so coordination does not pollute durable memory.
Evaluation: a golden corpus, recall and precision at k, and a gate that refuses a silent regression
Retrieval quality is the product, and it degrades invisibly. This spec builds the instrument: a committed golden corpus of memories and queries with judged relevance, a runner that loads it into a temporary instance and executes every query, metrics that are the standard ones rather than invented, a baseline file per configuration, and a gate that fails a change which moves a metric down by more than its tolerance without an explicit, justified baseline update. Constitution XIV becomes a command with an exit code.
The privacy boundary: what leaves the container, proved by the manifest rather than promised
The product's claim is that the user's memory stays theirs. This spec turns that from a sentence in a README into a checkable property. In the default configuration the manifest declares no egress host at all, so the process can make no outbound call and a test proves it. Every feature that would need one names it, justifies it, and is off by default. Telemetry does not exist. Backups go where the operator configured and nowhere else. The boundary is stated once here and inherited by every spec that could cross it.
Portability: a documented export anyone can read, and an import path off the system this replaces
A memory store that cannot be left is a hostage situation, and a product whose pitch is ownership has to prove it. Export writes a documented, self-describing archive of a scope: memories, provenance, entities, edges, lifecycle history, and optionally embeddings, in a format specified here and readable without this software. Import reads it back. The same machinery carries the migration path off the system this product replaces, as an ordinary source adapter reading a user's own data at their request.
Packaging: one image on the chassis, the operator surface, the runbook
The chassis already owns the image shape, the volume, the supervision, the four verbs, the probes, and the metrics. This spec adds only what is this product's: the build that produces the binary and fetches or embeds the local model, the operator routes the cell mounts, the configuration reference, and the runbook for the handful of things that can go wrong. It is deliberately thin, because a packaging spec that grows is a sign the chassis is being reimplemented.
The reference deployment: three nodes on Kubernetes, object storage behind it, evaluation green
The wave 4 exit condition and the product's own proof. A three-node StatefulSet on Kubernetes with object storage for backups and archived ledger segments, one origin behind an ingress, metrics scraped, backups on a schedule, and an end-to-end harness that drives the whole product through a real client path: sign in, connect, capture, import, curate, recall, promote, export, and erase. When this is green the specification has been demonstrated rather than argued.
Coordinate repository work through durable requests, replies, and reconciled evidence
Generalize repository handoff packets and handbacks into a durable, scoped coordination protocol. Requests, acknowledgment, replies, evidence observations, explicit resolutions and corrections retain their identity and revision. Inbox and outbox are projections, not another broker or hash chain. The same hiqlite store holds records, recipient state and consumer cursors transactionally; chassis events only wake consumers. Statecraft-cli supplies local session and Git/provider observations and keeps execution authority. A recalled request cannot authorize an action, a session report cannot approve its own specification, and an interrupted or disconnected consumer can reconcile what it missed.
Move every chassis pin to rahi 0.2.0 together, and declare which migrations are additive
The nine rahi crates are pinned at `=0.1.0`. rahi 0.2.0 is published on crates.io for all nine and is a minor release that changes the consumer contract: migrations gain an `additive` declaration and the store records a checksum per applied version, restore and first-boot export change their Rust API, backups authenticate with a dedicated passkey, and the ledger gains lifetime decision identity. This spec moves all nine pins in one change, keeps the exact-pin rule of spec 010, decides for each shipped migration whether it is additive, and re-runs spec 010's supply-chain revisit against the new tree. It must land before any claim-history migration (050 to 052) is written, so those migrations are authored against the 0.2.0 contract.
Converge secret detection upward: aicortex's detectors move into action-gate, and aicortex consumes them
aicortex-gate carries its own credential detectors (013 B-4, `rules.rs`, `secrets.rs`), and action-gate-core, the family's pure action gate, ships a weaker `SecretsCheck` with an overlapping pattern set. The owner chose to converge upward: aicortex's stronger detectors, with their detector ids, byte offsets, and full private-key marker set, move into action-gate under a separate upstream spec, and aicortex-gate then consumes action-gate's table at an exact pin instead of keeping its own. The consumption is admissible only at parity with 013's committed corpus, and aicortex keeps its own verdict, normalization, and keyed-digest ledger record. Until the upstream release exists this spec is blocked, not mocked.
One spec per pull request; a session may land several specs in build order
The owner's decision of 2026-09-25 replaces the repository rule that code lands one spec per session with one spec per pull request: a session may land several specs, each on its own branch and in its own pull request, in build order. This spec amends spec 001 B-6 and carries the new sentence into the harness text that repeated the old one (AGENTS.md, CLAUDE.md, README.md, the orchestrator rule, the architect agent, and constitution principle III).
Typed claims: a subject, a registered predicate, a typed value, an epistemic status, and the source span it came from
aicortex becomes the family's canonical owner of claim history with provenance. This spec defines what one claim says, as types, before any claim is admitted or stored: a subject reference, a predicate from a namespaced, versioned registry that a domain supplies, a typed value (text, integer, fixed-point decimal with a unit or currency, date, datetime with zone, interval, entity reference, enumeration), an epistemic status taken from the source's own stance, typed claim-to-claim relations, and source span references that extend 011's Provenance with a part, a byte or character span, and a content digest. aicortex stays domain agnostic: travel, the first consumer, registers its own predicates. Who may admit a claim is 051; when it holds and how history is kept is 052.
Claim admission: an observation is not a proposal, a proposal is not a claim, and a score is never authority
Three things that ingest pipelines routinely blur are kept apart: an observation (the source as received, stored as a memory through 013's gate), a proposal (a typed claim some actor suggests, with its evidence), and an admitted claim (a proposal the gate accepted under a named policy, with a decision record). Admission reuses the existing gate crate and its purity, adds a claim verdict whose `AdmittedClaim` only the gate can mint, and records every admission. Model-produced scores are stored as evidence and never serve as authority. A user's correction is first-class evidence carrying an authority level, and it is appended, never an edit.
Bitemporal claim history: valid time apart from record time, supersession per slot, and a current view that is a pure function
A claim has three times: when it holds in the world (valid time, with precision, zone, open ends, and uncertainty), when the source said so (source-declared time), and when this store learned it (transaction time). History is append-only on the transaction axis; retraction, correction, and supersession are appended records, and only erasure removes content, leaving a tombstone. Supersession is scoped to one subject, predicate, and slot, so a schedule change to one segment supersedes that attribute alone. The current view is a deterministic projection of history under an explicit as-of on both axes and a policy version, ordered by the source's own time and the claim's authority, never by arrival, so reordered email delivery yields the same answer.
Host-library mode: named migrations, operator-authorized vocabulary, and one caller-owned transaction
Expose Aicortex as a linked library inside another Rahi cell without giving up migration identity, operator authority, or transaction ownership. The library exports the immutable named migration set `aicortex`, makes predicate registration an explicit operator-configured act, and stages observations, admission records, claim history, erasure records, and outbox work into the caller's `rahi_store::TxnBuilder`. A separate host fixture proves that Rahi ingress state, Aicortex history, a host-owned projection revision, and outbox work share one commit and one rollback.
Adopt Statecraft profile revision 14 with the ordinary Rust code gate
Authorizes the narrow migration from Statecraft github-actions-rust revision 13 to revision 14 after the spec-spine 0.28.0 bridge and pin sequence. Aicortex keeps its exact engine pin and ordinary Rust code gate, corpus attestation, supply chain and spec DAG jobs. Managed authority files are regenerated by revision 14 using the retained setup parameters.
attest-ledger ↗
Hash-linked records · 2 shown
attest-ledger bootstrap (tamper-evident record ledger)
Bootstrap spec for the attest-ledger repository: a three-crate workspace vending a tamper-evident record ledger (append-only, hash-linked, Ed25519-signed) with an independent verifier. Extracted (relicensed Apache-2.0 by the sole copyright holder) from the Open Agentic Platform's crates/policy-kernel (proof_chain.rs and the pure subset of audit.rs). It depends on canonical-keysort-json for reproducible hashing. This spec establishes the workspace skeleton and seeds this repo's own spec corpus, which is governed by the pinned spec-spine library.
Tail completeness and trusted head commitments
Amend spec 000's unqualified deletion claim. Existing verifiers prove the integrity of the sequence presented to them, not that it is the latest or complete sequence. Add a versioned, domain-neutral head commitment and opt-in record-chain and closed-audit-segment verifiers for callers that obtain the expected head through a separately trusted channel. Preserve all 0.1.0 APIs and release the additive contract as 0.1.1.
canonical-keysort-json ↗
Canonical serialization · 2 shown
canonical-keysort-json bootstrap (deterministic JSON key-ordering library)
Bootstrap spec for the canonical-keysort-json repository: a single-crate library that recursively lexicographically sorts object keys at the JSON serialization boundary, so values serialize byte-identically regardless of serde_json's preserve_order feature. Extracted (relicensed Apache-2.0 by the sole copyright holder) from the Open Agentic Platform's crates/canonical-json. It is the leaf dependency of attest-ledger and action-gate. This spec establishes the crate skeleton and seeds this repo's own spec corpus, which is governed by the pinned spec-spine library.
The governed harness (spec-spine self-governance and the session kit)
This repository's own governance surface: the spec-spine gate chain, the committed .derived shard trees it compares against, the merge driver that keeps those shards mergeable, and the Claude Code session kit that constrains what an agent may do here. Spec 000 governs what the crate emits; this spec governs what the repository does to keep 000 honest. It exists because 000 section 4 became a normative cross-language contract while nothing mechanically checked that the corpus stating it stayed coherent with the code implementing it.
rahi ↗
Rust application chassis · 36 shown
Bootstrap spec system for rahi (specify first, build by spec)
Foundational contract for the rahi corpus. Authored truth lives only in markdown with YAML frontmatter; machine-consumable truth about the corpus is compiler-emitted JSON read only through spec-spine; every artifact is a deterministic function of (config, file contents); and a typed authority graph governs who owns what. rahi is specified in full before a line of it is built: the corpus is the design, spec numbers are the build order, and an orchestrator drives one spec per fresh session through build, ship, shepherd, and verify. This spec also freezes the seven chassis invariants (constitution VI through XI and XIV) that no later spec may amend.
Agentic engineering harness: session protocol, skills, agents, hooks, gate
The governed-development loop every human and every driven session runs inside: the cross-agent New Sessions protocol and the Working the backlog protocol in AGENTS.md, the Claude Code skills (the eight that sequence the backlog protocol, prime, setup, next, build, verify, spec, ship, shepherd, and the two it calls, commit and code-review), the four pipeline agents, the standing and path-scoped rules (including the chassis invariants checklist), the hooks that report derived-artifact freshness and block an ungated PR, the Makefile that is the one source of truth for what CI validates, and the CI workflow that re-runs the same gate. The harness is what makes the corpus buildable by claude-observatory.
Chassis thesis: seven responsibilities, one deployment unit, a library not a template
The architectural record every ordinary spec operates inside. rahi is a set of Rust library crates and one binary composer that give an application exactly seven things: identity, state, ledger, kernel, edge, operations, and a dev substrate. It ships as one container with one volume and one origin, with rauthy co-deployed and never shared. It is a library that apps compose, not a template that apps are stamped from. This spec fixes the responsibilities, the crate topology, the dependency direction, and the three-wave build order. It owns no code.
Cargo workspace and the core types: Error, Principal, Revision, FenceToken, Config
The first build: a virtual Cargo workspace with the toolchain pinned, unsafe denied, one shared dependency table, and a supply-chain policy; plus rahi-types, the plain-data substrate every other crate depends on and that depends on nothing in the workspace. It fixes the Error enum with the four exit codes, the Principal as the IdP's subject plus claims, the Revision and FenceToken newtypes the store's invariants are written against, the Config tree derived from one public URL, and the schema version constants.
The store: hiqlite in-process with txn, two read calls, migrations, and backup
The application store: hiqlite linked as a library, opened from the Config with its own data directory and ports (never rauthy's), exposing exactly the surface the interface contract decided: execute, txn as the write path for anything with an invariant, query on the local replica and query_consistent through the leader as two named calls, migrations as versioned DDL run by a verb and never at boot, and the leader-only encrypted backup surface. Carries enrahitu://032 §3.1, §3.3, §3.6, §3.8.
Store coordination: fenced leases, local notify, the outbox, and the revision watermark
The coordination plane over the cache Raft group: distributed leases with the Raft log id as a mandatory fencing token and an explicit release, key-only local notify whose envelope is the routing key, the outbox row that commits in the same txn as the resource it describes, and the monotonic revision watermark every consumer polls so that notify is only ever a latency hint. Carries enrahitu://032 §3.2, §3.4, §3.5, §3.10.
The decision chain: hash-linked records, CAS append, verification at boot
The governance ledger: a linear chain of signed, hash-linked Decision records stored in the app's hiqlite, appended by a compare-and-swap that a unique index on the parent hash enforces inside one txn, retried after a bounded wait on a miss and then a typed integrity error, and verified in full at every boot with an integrity failure being process-fatal. Records use the attest-ledger envelope and canonical-keysort-json bytes so an independent verifier can check the chain without this code. Carries enrahitu://024 and enrahitu://032 §3.7.
Ledger sealing: a resident hot window, immutable segments archived to object storage
hiqlite replicates the whole database to every node, so an unbounded audit history cannot live in it; the head and a hot window do. When the window exceeds its bound the oldest contiguous run is sealed into an immutable segment (first id, last id, count, segment hash, previous segment hash), written to an object store behind a trait with an S3 implementation, and deleted from the hot table. Segments link to their predecessor and the head links to the last segment, so the chain verifies end to end without archived history resident. Archive is never conflated with backup. Carries enrahitu://032 §3.7 and §3.9.
The kernel: a declared capability ceiling, verified at build, enforced at runtime, denials ledgered
The governance seam. An app declares a manifest (TOML, embedded at compile time): identity, services, resources by name, a capability catalog, per service grants, the gate roster, ledger and observability policy. The kernel canonicalizes and hashes it, verifies at build that every governed facade the app links is covered by a grant, adjudicates every store, lock, notify, secret, and egress operation at runtime through the action-gate primitive, denies anything not granted, and appends a Decision for every denial without the request path awaiting the ledger. Carries enrahitu://020 and enrahitu://021 with the parser-based extractor replaced by a declared manifest.
Binary values and the extension policy: BLOBs are first class, loadable extensions are refused
Applications above the chassis store vectors, thumbnails, and signatures, so the store must round-trip binary values with a declared ceiling and a paged read that never materializes a table. It must also answer, once and in writing, whether a loadable SQLite extension may be used: it may not. hiqlite replicates statements rather than pages, so a node whose engine differs diverges silently, and a virtual table's behavior under snapshot, backup, and restore is unproven. The escape hatch is named here so a later spec can open it with evidence rather than by assumption.
The edge: axum router, middleware chain, probes, static slot, error mapping
The one public listener of a cell: an axum Router builder that an app mounts its services on, a fixed middleware order (observation outermost, then security headers, CSRF double-submit, and a per-node rate limiter on the cache group), liveness and readiness probes that answer different questions, a static file slot for the app's SPA, and one mapping from rahi_types::Error to HTTP status. Everything an app serves goes through this router; there is no second listener.
rauthy behind a same-origin proxy: the raw /auth/* route, discovery, JWKS, client bootstrap
One public origin for app and IdP: rauthy binds loopback, the app reverse-proxies the /auth/* subtree to it raw and unfiltered with bodies streamed, the issuer and every OIDC endpoint live under the app's URL, the driver trusts rauthy's discovery document fetched through the loopback base and caches its JWKS with rotation, and the app's OIDC client is bootstrapped on first boot with authorization-code plus refresh, S256 PKCE, RS256, and redirect URIs derived from the public URL. rauthy is a release binary in the same container and is never forked. Carries enrahitu://005.
Sessions and the principal: the signed envelope, renewal as a round-trip, the IdP subject as the only id
The app keeps the shell and gives up the authority. Login is the authorization-code flow with PKCE through the proxy; the session cookie is a signed envelope holding rauthy's refresh token and the pinned subject, httpOnly and same-origin, that grants nothing on presentation; the access assertion is short-lived and minted from the IdP's claims; renewal forwards the refresh token to rauthy and re-reads roles and email_verified from userinfo every time, so a role removed at the IdP takes effect within one access lifetime and a refused grant ends the session. No local account row is ever written. Carries enrahitu://004.
Observability: /metrics always on, an in-process OTel tracer with a ring buffer, decisions on spans
The substrate contract every cell exposes: Prometheus text on /metrics from an in-process registry (process metrics, HTTP counters and duration histograms labeled by route pattern and status class, store operation counters, kernel decisions and ledger failures), and an OTel tracer with a span per request and per store operation that exports OTLP only when an endpoint is configured and always retains a bounded ring buffer of recent traces queryable in-process. Kernel decisions attach their id to the active span through the observe hook. Carries enrahitu://022.
Hardening: trusted client identity, the operator role gate, exposure review of every route
X-Forwarded-For is evidence, not identity: it becomes identity only when the operator states how many hops in front of the cell are theirs. This spec adds the client identity resolver the rate limiter keys on, the operator role gate that internal surfaces (the trace ring, ledger inspection, backup triggers) sit behind with no rate limiter on top, and an exposure table that every mounted route must appear in with its authentication class, asserted by a test so an unreviewed public route cannot land. Carries enrahitu://025.
The resource server: bearer tokens rauthy issued, audience bound, scope gated, never app minted
A browser is not the only client. Command line tools, agent runtimes, and MCP clients hold no cookie and cannot follow a redirect into a browser session, and the chassis has so far had nothing to say to them. This spec makes the app an OAuth 2.1 resource server over the same IdP: it publishes protected resource metadata so a client can discover the authorization server, validates rauthy-issued JWT access tokens locally against the cached JWKS with a mandatory audience check, gates routes on scope, and answers an unauthenticated request with the challenge that bootstraps discovery. Registration and the grants themselves stay rauthy's: the app issues no API key, ever.
Streaming responses: server-sent events as the one mechanism, declared per route, bounded and cancellable
A response that is produced over seconds rather than milliseconds breaks every default the edge holds: compression buffers it, the request timeout kills it, the rate limiter counts it once and forgets it, and graceful shutdown drops it mid-sentence. This spec adds one streaming mechanism, server-sent events, and makes a streaming route declare itself so those four defaults are turned off deliberately rather than discovered in production. Streams are bounded by a per-stream channel and a per-client concurrency limit, are cancellation-aware, close cleanly on shutdown, and are one span each in the tracer.
The verbs and the binary composer: preflight, migrate, backup, restore, ledger verify
Operations that exist. rahi-ops implements preflight (config, keys, both stores' health, disk), migrate (spec 011's migrations as a deploy step, leader-only), backup (one encrypted archive holding the app's hiqlite snapshot taken through the addon, rauthy's snapshot taken through rauthy's own API, and the key material that decrypts both), restore (a cluster reset that refuses partial input and is single-shot by a marker), and ledger verify (spec 013 over an exported chain). rahi-cli is the binary composer: an app implements the Cell trait and its main is rahi_cli::run(cell), which yields serve plus every verb with the four exit codes. Carries enrahitu://027.
Single-container packaging: one image, one volume, first boot, keys, die-together supervision
One container plus one volume is a complete authenticated application. The image carries the app binary and the rauthy release binary. First boot generates every key (ledger, session, backup, rauthy ENC_KEYS, the admin token), writes rauthy's environment derived from one public URL, and bootstraps the OIDC client; a restart regenerates nothing. The app is the supervisor: it starts rauthy on loopback, waits for its health, starts serve, and when either exits the other is stopped and the container exits. The volume layout is /data/hiqlite, /data/rauthy, /data/keys, and /data/backups. Carries enrahitu://007.
Cluster topology: N=1 primary, N=3 StatefulSet on Kubernetes, one key set, S3 backups
What multiplies and what does not. At N=1 nothing changes. At N=3 every replica is identical: a StatefulSet pod running the supervisor, with its own volume, two Raft clusters per replica (rauthy's and the app's), peers discovered by stable pod DNS, one key set injected as a Secret and custodied once, the public origin on an Ingress that terminates TLS and keeps /metrics off it, a CronJob that runs backup --to s3:// against the leader, and a ServiceMonitor example. Rate limiting is per node and says so. A shared volume between Raft members is refused by the manifests themselves. Carries enrahitu://030.
The dev substrate and the harness: compose for N=1, boot the real binary, wait on /readyz, a cookie jar
Two things a contributor needs: a compose file that runs the N=1 topology (the image with a bind-mounted volume, watched rebuilds) and a harness crate that boots a built cell binary under a throwaway data directory with fresh keys and OS-allocated ports, starts a rauthy on loopback when one is available, waits on /readyz and never on /healthz, and hands back an HTTP client with a cookie jar so authenticated flows are testable. One instance per test file, never per test. Carries enrahitu://033.
hello-cell: the reference app that composes every crate and proves the chassis end to end
The smallest complete governed cell and the thing consumers copy from: a manifest declaring one table, one kv prefix, and the grants a notes service needs; a Cell implementation whose main is one line; a notes resource with create, list, and delete behind login, each write staged with an outbox row and stamped with a revision, each denial ledgered; a static page that logs in through the proxy and lists notes; an operator route exposing the trace ring; and an end-to-end test through the harness that logs in, writes, reads, is denied an ungranted operation, verifies the ledger, takes a backup, and restores it into a fresh directory.
Denials survive a graceful shutdown, and every lost denial is counted by its cause
Constitution X says the kernel ledgers every denial. Spec 015 answers a denial before its record is durable and drains the records through a bounded queue, and serve never drains that queue on shutdown, so a graceful stop silently loses the denials still queued (reproduced: 50 denials answered 403 with decision ids, 33 and 40 of them in the chain after SIGTERM, nothing counted). A second loss is structural: a decision id is the chain head at boot plus a counter, so replicas of spec 032's StatefulSet that boot on the same head mint the same ids, and the second append of an id is refused as a conflict (reproduced in process: two kernels on one chain both answered with the same id and one record landed). This spec drains the queue within a bound when serve stops, counts what the bound abandons, separates a dropped record from a failed append on /metrics, and names the replica in every decision id, so the only uncounted loss left is a process that is killed.
Manifest and schema evolution: a ledgered manifest transition, a checked migration history, and a restore that refuses what it cannot serve
Today a cell's manifest is frozen at its first boot: the manifest hash is the chain's genesis parent, Ledger::open checks the first record against the booted manifest on every open, and any change to the manifest (one added grant, otel switched on) makes serve exit 1 with an integrity error on the existing volume. Spec 015 B-8 names the missing step, "a deploy genesis record", and nothing defines it. Migrations have the mirror problems: serve accepts a store ahead of the binary, a changed migration under an applied version is skipped silently, and restore checks no compatibility. This spec makes a manifest change a signed transition record appended at deploy, checks the booted manifest against the chain's current manifest, records a checksum per migration, lets an older binary serve a newer schema only across migrations declared additive, and makes restore refuse an archive it cannot serve. It carries the worked consumer example for an upgrade, a rollback, and a restore.
Identity recovery and the live proof: a backup a real rauthy answers, a restore that brings the users back, and CI that runs against the pinned release
Recovery of a cell is two state systems, and only one of them recovers today. Against a real rauthy the backup verb fails outright, because rauthy's backup routes accept only an admin session and the verb presents the admin API key (030 D-3, observed against 0.36.2). The app half races hiqlite's background backup and can report no file for a backup that lands a moment later (observed on two of four runs). A restore places rauthy's snapshot and nothing hands it to rauthy, so a restored cell comes up with no users, and every sub in its rows is unreachable. CI never runs a real rauthy. This spec resolves D-3 by the mechanism a human chooses, waits for the app backup to exist, applies rauthy's snapshot exactly once on the next start, and adds a CI job that runs every rauthy-gated test, and an end-to-end test through recovery with identity, against the pinned rauthy release.
Native clients and bearer revocation: the CSRF exemption wired, a public client for a CLI declared in the manifest, token lifetimes set, and a token that can be revoked
A hosted control plane built as a cell and a command-line client that logs in to it need four things the chassis does not yet give. The CSRF layer exempts only /auth/*, so a POST with a bearer token is refused 403 csrf (025 B-11 promised the exemption; D-6 left the wiring). No public client exists: the one client first boot creates is confidential, and a rauthy client defaults to EdDSA with no audience binding, so no CLI token passes the resource server without an admin's hand. Token lifetime is rauthy's default, 1800 seconds, not the fifteen minutes 025 B-5 states. And nothing writes the jti deny-list, so a bearer token cannot be revoked before it expires. This spec wires the exemption, declares native clients in the manifest and provisions them at boot bound to RS256 and the cell's audience, sets lifetimes explicitly, adds revocation by token and by subject, reports the bound in preflight, and gives hello-cell a scope-gated bearer route and a device-code login in its end-to-end test.
Releases and out-of-tree packaging: a tagged version a consumer pins, images that exist under the names the manifests use, and a runtime image a cell in another repository builds on
The thesis says consumers depend on published crates and pin a version (002 D5), and nothing is published: no tag, no release, no crate, no image. The one route that works is a git dependency pinned to a sha. cargo package fails on rahi-ops because it includes a template from outside the crate, which also blocks rahi-cli, the crate that carries Cell. The deploy manifests name ghcr.io/bartekus/rahi:latest, a namespace and a tag no workflow produces. The image recipe builds only a package of this workspace, and a cell's static directory named under CARGO_MANIFEST_DIR does not exist in the image (hello-cell's page answers 404 there). This spec defines the version and the release, publishes the images a tag builds under the names the manifests use, adds a runtime image an out-of-tree cell builds on, makes static assets exist in the image, makes the crates packageable, and proves the consumer stanza on every release.
Runtime identity: a replica states which bits it runs, which ceiling it enforces, and which instance it is, with every value's basis named, on bounded surfaces
A consumer that correlates an immutable artifact, an authority snapshot, a deployment, and a running replica needs the replica to say what it is. Today it says almost nothing: rahi version prints the crate version, the manifest hash is visible only inside a ledger export or a backup, the OTel resource carries service.name alone, the image has no labels, and the deploy manifests pin no digest. This spec makes a replica report its measured executable digest, its booted manifest hash, its declared image and build revision, and a per-boot instance id, each with its basis (measured, declared, or absent), on one unguarded document at /binding kept off the ingress like /metrics, on the OTel resource, and in one build-info metric whose labels are versions only. It states what the telemetry does not observe or count. It writes nothing to the chain; deployment epochs are spec 041.
Deployment epochs: the chain records each deployment it serves under, appended at the deploy step, linked to external build and deployment records it never rewrites
The chain commits to the first manifest (genesis) and, with spec 036, to every manifest adopted since. It records nothing about which build a replica runs or which deployment put it there, so a consumer cannot join a decision to an artifact, an authority snapshot, or a rollout, and an upgrade, a rollback, and a restore look the same in the chain. This spec appends one epoch record at each deploy step that changes what is deployed: the manifest current after the step, the artifact (the binary measured by the deploy step, the image digest the deployer declares), and typed opaque references to the external build provenance, the deployment record, and the authority snapshot. The genesis and every existing record stay byte for byte; no history is rehashed; nothing is embedded in a built artifact; every reference points from the later record to the earlier one, so no hash cycle is possible. Replicas bind to the current epoch at boot and report a mismatch without refusing, every decision names its epoch and instance, and the chain becomes readable from a running cell.
Lifetime identity: one id names one decision for the life of the chain, and absence is proven rather than assumed
The chain can say what happened but not whether a given decision ever happened, because spec 013 D-5 tells a lost compare-and-swap from a duplicate id by asking whether the id is resident and spec 014 deletes the row when its segment is sealed. Past the hot window a retried append therefore writes a second record under the same id, and full verification passes because a duplicate id is not a broken link. This spec gives every decision a narrow resident identity row that survives sealing, makes that row's primary key the arbitration inside the append transaction rather than a read before it, stamps the row in the seal transaction, and answers presence, proven absence, unproven history and proven ambiguity as four different answers. A chain sealed before this spec has no rows for its archived ids, so a cell refuses to serve until `rahi ledger reindex` has rebuilt them: the upgrade costs a stop, and the alternative is a cell that serves while its audit proof is silently wrong. The refusal, the reindex's behavior on damaged or ambiguous history, the cutover from a binary that does not stamp, and the permanent resident cost are all stated here rather than discovered in production.
Patched dependency adoption: the N=1 cell runs the published, provenance-pinned hiqlite and Rauthy builds, crosses their cache boundary only through a fenced, resumable, backed-up transition, keeps every accepted revocation through it, and stops within a validated budget with an honest outcome
rahi's application store runs hiqlite 0.14.0 through a root git patch that registry consumers never inherit, and its co-deployed identity provider runs upstream Rauthy 0.36.2. Published downstream builds exist for both, hiqlite-patched 0.15.0-patched.1 and rauthy-patched 0.36.2-patched.2, each qualified by its producer at N=1. This spec adopts exactly those two artifacts for the N=1 cell under a narrow recorded exception to 011's no-fork rule. It crosses the one incompatible on-disk boundary (the cache raft log) by relocating the app store behind a permanent fence that every pre-043 binary refuses at its old path, installed whole and verified quiescent before anything moves, and a second fence at rahi's rendered Rauthy environment that makes every published pre-043 supervisor that reads it afterwards exit before it spawns Rauthy; it names what neither fence can stop (an old process already past its read or its lock-free start phase, and the two destructive hiqlite variables in an old process's environment) as preconditions the operator establishes by stopping and removing every old process first. Every new-binary entry point takes its locks before it reads the state file; Rauthy moves its own cache under consent, and the resumable upgrade is released only on a Rauthy build whose interrupted move is repaired. It keeps every revocation accepted before the upgrade refused by a permanent issued-at floor that needs no historical lifetime, bounds every admitted token by an enforced lifetime ceiling and a hard 86,400-second maximum, keeps durable revocations against a backward clock step after any prune (retained, or pruned under a persisted watermark, by owner choice), states the restore boundary, including what a stale Rauthy restore revives, turns a proven terminal storage failure into a bounded exit, and reports every stop's observed outcome with a non-zero exit when completion is not confirmed. It changes no topology and claims no N=3 support.
N=3 split composition: two StatefulSets, rahi and rauthy, one hiqlite node per pod, rahi reaching rauthy only through an encrypted internal Service and losing readiness, never liveness, without it
The owner decided on 2026-09-23 (hiqlite proposal D-14, recorded at bartekus/hiqlite@da4c910) that an N=3 cell is one namespace holding two StatefulSets, rahi and rauthy, three replicas each, one hiqlite node per pod, and that the single-container supervised composition of spec 031 stays the N=1 profile. This spec is rahi's share of that decision. It amends 031 and 032 for N=3 only: rahi starts without rauthy, reaches it only through the internal ClusterIP Service rauthy-internal.<namespace>.svc.cluster.local over Rauthy's TLS or a mesh's mTLS, fails readiness and never liveness while rauthy is unavailable, and keeps peer discovery independent of readiness. It carries the proposal's routing, NetworkPolicy, per-StatefulSet disruption and storage requirements, and backup obligations. It changes no dependency and claims no N=3 support: that follows only from recorded qualification on a named release.
Receipts and work claims: an inbound item is recorded once per content revision, processed once per processor revision, and never silently dropped
Every product that ingests from an outside source rebuilds the same table: a key, a body digest, the first outcome, and a rule for a repeat (statecraft-platform's `sc_idempotency`; aicortex's work-claim rows over the Lease and the Outbox). This spec puts one primitive in rahi-store. A receipt names an inbound item by tenant, source namespace, and external key (or a synthetic key when the source has none) and records a digest of its content; classification distinguishes first seen, exact redelivery, changed content under the same key (recorded as a new revision or a collision, never dropped), and erased. Receipt rows stage into the caller's TxnBuilder beside the domain writes and Outbox::stage. Processing is identified separately (receipt revision plus processor and its policy revision) so a correction or a new policy reprocesses. Work is reserved under the existing Lease into a fenced claim row with attempt history, bounded retry, a visible dead-letter state, and reclaim after expiry; queue counts reach /metrics redacted. Retention compacts to a tombstone, erasure keeps only the key digest and is never undone, and a crash-point table states recovery at every boundary. Owner decisions of 2026-09-24 are recorded.
Named migration sets: a linked library brings its own versioned, checksummed migration sequence, and never renumbers into the host's
A store today has one migration sequence, owned by the cell. A library linked into a cell (aicortex inside travel-memory) numbers its own migrations from 1, and so do the chassis's own tables (coordination_migration, 045's receipts), so the host must renumber everyone into its list and every renumbering is a checksum nobody else can reproduce. This spec gives each linked library a named migration set: the store records version, checksum, and the additive declaration per (set, version); migrate plans across sets with declared minimum-version requirements between them; serve, backup, and restore apply 036's checksum-first and additive rules per set. A store written before this spec keeps its schema_version rows byte for byte as the implicit set app. 045's receipt_migration becomes the chassis set rahi.receipts.
Manage application background services through the cell lifecycle
A Cell can declare routes and migrations, but it cannot give the chassis an application worker whose lifetime is tied to serve. Applications therefore either omit durable background work or detach tasks that Rahi cannot cancel, observe, or join before the application store shuts down. This spec adds a named managed-service declaration to Cell, starts each service once after successful composition, treats an unexpected return, error, or panic as a process failure, broadcasts cancellation for every stop cause, and joins or aborts every service before draining its final denials and shutting down the store. The service bound overlaps the existing HTTP drains so the published stop grace does not grow.
Make every exit of a program that opened the app node pass through its shutdown
hiqlite creates an unclean-stop marker, state_machine/lock, when a node starts and removes it only in a graceful shutdown; without the auto-heal feature the next start refuses until someone repairs the directory by hand. The travel-memory service hit this with a ? between Store::open and Store::shutdown and a Ctrl-C-only handler. This spec gives rahi-store one scope for the node's lifetime, Store::run, which arms SIGTERM and SIGINT before the node starts and shuts it down on every way out of the body; it closes the three paths in rahi's own verbs that returned with the node open and the signal window in serve's boot; it documents the single-voter recovery and verifies it; and it records why auto-heal stays off after a probe showed its rebuild complete.
rustev ↗
Decision proposals · 18 shown
Bootstrap spec system
Foundational contract: authored truth lives only in markdown (+ YAML frontmatter); machine-consumable truth is compiler-emitted JSON only; every artifact is a deterministic function of (config, file contents); a typed authority graph governs who-owns-what.
Ecosystem boundaries, proposals versus authority, and product principles
What Rustev owns and refuses to own within the ecosystem (spec-spine, Aicortex, Rahi, statecraft-cli, applications), the rule that Rustev produces proposals and never grants, the provenance classes of Rustev outputs, the dependency layout that keeps contracts transport-neutral and the core free of integrations, and the product principles that are the constitution's principles VI onward. Owns the workspace manifest, the Makefile and the boundary check that enforces the dependency rules.
Decision contract and pure core
Increment 1: the versioned wire contract (`rustev-contract`) and the pure core (`rustev-core`). Bounded parsing, canonical form and identities, value kinds without silent conversion, fixed-point decimals and millisecond time, input validation with provenance, freshness and conflict, a closed versioned exact-operator registry, decision definitions entered through a typed builder or canonical JSON, the plan compiler with capability matching, declared fallbacks and typed refusals, calibration binding, deterministic selection policy with explicit unresolved handling, and derivation classes with lineage. No I/O, no clock, no async executor, no model.
Runtime execution and evidence emission
Increment 2: `rustev-runtime` drives the pure core's staged evaluation through backend seams on Tokio (R-03) under explicit bounds: injectable monotonic time and one end-to-end deadline, bounded admission and concurrency, cost budgets reserved before dispatch with honest settlement, bounded retries and runtime fallback declared in an identified execution policy that is part of plan identity, cancellation that never claims more than the backend acknowledged, and evidence delivery under a declared sink policy. Amends spec 002: plan schema 2, the execution-policy and run-record documents, a pre-supply output check, and the backend and sink seams. Batching, duplicate suppression and caches are deferred by name.
Evaluation and replay
Increment 2: offline reproduction from bounded, host-retained replay bundles; configurable tenant-only or principal-scoped equivalence for candidate comparison; explicit availability, coverage and abstention; named dataset splits and temperature fitting. Amends 002 with replay and request documents and typed loading diagnostics, and 003 with opt-in bounded capture of supplied values. Live re-execution is deferred. Synthetic fixtures establish mechanics only, never empirical quality.
Deterministic rules backend
Increment 2: `rustev-backend-rules`, a deterministic, in-process backend that implements the backend seam of spec 003 by evaluating a bounded, versioned rules program (`rustev.rules/1`): typed field access into the canonical projection, ordered rules with explicit precedence and no-match behavior, constant, linear and lookup contributions to authored logits in exact fixed-point arithmetic, and a descriptor derived from the program so the program's digest is the artifact identity plans bind. Metered logical cost units, bounded work, local cancellation observed at declared points. Narrowed by owner decision R-13; the semantic backend is spec 011.
CLI surface
Increment 2: `rustev`, a command-line host over the library. Plan check, compile and show against descriptor, rules-program, calibration and execution-policy files; run one snapshot through `rustev-runtime` on rules-program backends with a file evidence sink and optional bounded capture into a replay bundle; offline replay under an explicitly supplied trusted scope; evaluation reports with their detail and configuration companions, regression gates, and temperature fitting with its lineage record, through `rustev-eval`. Bounded file access, stable JSON output and distinct exit codes. No network, no hosting, no live inference beyond the deterministic rules backend.
Reference package: support routing
The first domain package of increment 3 (design section 16.1, roadmap ordinal 007): `packages/rustev-pkg-support-routing`, a library that publishes the support-routing decision definition, its golden canonical documents, a declarative task adapter and evaluator configuration, and a synthetic labeled dataset, depending only on `rustev-contract` and `rustev-core`. It demonstrates that a domain is a package and not a core change, and that swapping the semantic backend (the rules backend's synthetic head against the Jev integration, spec 012) produces comparable reports with no change in any authority path. Approved (A-12, 2026-09-25) and amended by 016 (A-13, 2026-09-26); implemented.
Reference package: lodging recommendation
The second reference domain package reserved by the architecture roadmap: `packages/rustev-pkg-lodging`, containing a bounded lodging-recommendation definition, canonical documents, synthetic mechanics fixtures, and evaluation material. The package preserves exact eligibility, explicit trust treatment, bounded semantic fan-out, abstention, lineage, offline replay, partial adoption, and backend-swap comparison without claiming semantic quality, external readiness, or authority to book lodging.
Remote adapter protocol
Increment 3 (design roadmap 17.1, ordinal 009): what every decision backend reached across a network must guarantee, and a versioned, transport-neutral protocol (`rustev.remote/1`) for backends served by another process. Covers capability description and negotiation at setup, attempt identity and idempotency, one total time budget per attempt, cancellation that never claims more than the remote side confirmed, validation of responses into `RawOutput` without inventing mass, a closed error taxonomy mapped onto spec 003's failure classes, explicit served-model identity (unknown is a value, never an omission), cost reporting, retained exchange records for audit and replay, and privacy options passed through and recorded. Protocol messages live in `rustev-contract`; every network implementation lives under `integrations/`. Amends no approved behavior. Approved (A-08); implemented.
First semantic backend (deferred)
Deferred roadmap item split out of spec 005 by owner decision R-13: the first semantic backend candidate of R-01, frozen embeddings plus a task-specific linear head, with ONNX Runtime through `ort` preferred only after a bounded compatibility, licensing, operator, linking, packaging and resource check. Claims no code and adds no dependency until that check is recorded and this spec is made concrete and approved.
Jev integration (remote decision backend)
A remote decision backend under `integrations/` that answers Rustev's `proposition`, `classify` and `rubric` steps by calling TypeSafe's Jev model, through the Vercel AI Gateway by default or TypeSafe's own API as an alternative transport, as an ordinary integration. It conforms to Part A of spec 009: explicit served identity (unknown on the Gateway), one total budget, unconfirmed cancellation once sent, closed error codes, estimated cost, requested privacy options, and exchange records. Many Rustev questions over one state go in one request. Provider confidence is recorded, never treated as calibrated. `rank` is not declared. No Jev wire-compatible surface is exposed. Paid inference for smoke and qualification is authorized by R-27 (zero data retention, synthetic and independently labeled data only); testing is capped at USD 5 in total and production at USD 25 per month (R-29), and production requires a pinned model version (R-28). Approved (A-09); implemented.
Remote state on attempts that end without a stop (amends 003)
A separately reviewable amendment of approved spec 003 (R-16), chosen by the owner in R-28 for the gap spec 009 5.1 records: the runtime derives `possibly_continuing` only from cancellation, so an attempt whose request left the process and whose connection then failed is recorded as `finished` although remote work and charges may continue. This adds one field to the attempt report through which an adapter states that remote work may continue, and one derivation rule in the runtime. The `rustev.run/1` schema and every existing record's bytes are unchanged. Approved (A-07) and implemented.
Task adapter rules bound into evaluation identity (amends 004)
An amendment of approved spec 004 (R-16). The evaluator configuration names its task adapter only by name and version, so the configuration identity a report carries does not change when the adapter's correctness rules change under the same version, and a gate can compare two reports that judged correctness differently. Spec 006 closes this for the CLI alone, by writing the adapter document beside each report and comparing bytes. This amendment binds a digest of the adapter's rules into a new evaluator configuration version, so the report identity itself covers correctness, and makes a gate unknown whenever either side's rules are unbound. Approved (A-10, 2026-09-25) and implemented.
Per-case reporting of unusable replay bundles (amends 004)
An amendment of approved spec 004 (R-16). Evaluation takes parsed replay bundles only, so a retained bundle whose bytes cannot be read, exceed their limit or do not parse cannot be reported as one incomparable case: a host must either stop the whole evaluation (the CLI's choice, spec 006 E-31) or leave the case out, which the library then reports as `bundle-missing`, misstating corruption as absence. This amendment lets the caller supply a typed bundle-load failure per case, reported as incomparable with its own reason, counted in coverage and excluded from quality. Approved (A-11, 2026-09-25) and implemented.
Declared uncalibrated threshold in support-routing swap test (amends 007)
A proposed amendment of approved spec 007 (R-16). Spec 007 3.4.3 and section 5 require the backend swap test to assert identical compiled plans' policy sections across the rules backend and Jev backend. Under owner decision R-31 (approving Q-1), Jev returns an uncalibrated distribution and declares `uncalibrated_threshold` on `topic`'s `top_mass_below` condition with a margin rule. The reference definition keeps `uncalibrated_threshold: not_declared` to match the golden document byte-for-byte (3.1.3). Because `uncalibrated_threshold` is part of the condition inside the policy section, the compiled plans' policy sections cannot be strictly identical under equality. This amendment amends 3.4.3 and section 5 so that the swap test asserts identical policy rules, conditions, thresholds, adjustments and output declarations, accounting for Jev's declared uncalibrated threshold on `topic`. It also resolves two further conflicts found while preparing 007's delivery: where the swap test can live given spec 001 3.4.4, and how priority can be a separately labeled field given the declarative task adapter's one-label-per-case shape (spec 006, 3.8). Approved (A-13, 2026-09-26) with both open questions settled as recommended (R-33); implementation complete, delivered with spec 007.
General runtime batching, duplicate suppression, and caches
A separately reviewable amendment to runtime execution for general runtime-planned batching, concurrent duplicate suppression, bounded memory caches, and opt-in persistent caches. Optimized execution must be judgment equivalent to independent uncached execution, isolate authorized scopes, preserve each waiter's deadline and cancellation, attribute one charge and every liability exactly once, invalidate on correction, revocation and erasure, and record every shared-call or cache effect. Existing adapter-scoped batching remains a special case, not evidence that this general contract is implemented or qualified.
Adopt Statecraft profile revision 14 with the ordinary Rust code gate
Authorizes the narrow migration from Statecraft github-actions-rust revision 13 to revision 14 after the spec-spine 0.28.0 bridge and pin sequence. Rustev keeps its exact engine pin and ordinary Rust code gate, boundaries and verify jobs. The managed authority files are regenerated by the revision 14 producer using the retained setup parameters.
spec-spine ↗
Specification authority · 182 shown
Bootstrap spec system (markdown → compiled JSON authority ledger)
Foundational contract for spec-spine. Authored truth lives only in markdown (with YAML frontmatter); machine-consumable truth is compiler-emitted JSON only; the corpus compiles in full from day one; every artifact-producing step is a pure, deterministic function of (config, file contents); and a typed authority graph, not a flat pile of claims, governs who may change what. This spec defines what a spec IS. It is tier-1: its `unamendable` anchors are non-overridable, and it bootstraps the corpus by declaring authority it has held since before the graph existed (`origin.retroactive`).
Compile the spec corpus into a deterministic registry
The compile capability: read every specs/NNN-slug/spec.md, validate its frontmatter, and emit a deterministic spec-as-source registry.json plus a non-deterministic build-meta.json. Establishes the spec-spine-core engine scaffolding (content hashing, canonical JSON, markdown heading extraction) and the `spec-spine compile` CLI subcommand.
Typed read-only query over the registry
The query capability: typed, read-only access over a loaded registry: list, show, status-report, and relationships; plus load_registry (which rejects an unknown MAJOR schema version). Establishes the query module and the `spec-spine registry` CLI subcommands; extends 001's public surface additively.
Corpus conformance lint
The conformance lint: corpus well-formedness checks beyond compile's structural validation, emitted as L-codes with error/warning/info tiers. Error-tier always fails; warning-tier fails under --fail-on-warn; info-tier under --fail-on-info. Establishes the lint engine and the `spec-spine lint` CLI subcommand.
Codebase index and the file/section/symbol unit grammar
The codebase indexer (code-as-source view): discover compilation units via the configurable manifest namespace, link code to specs three ways, resolve the file/section/symbol unit grammar to physical (file, line-span) locations (tree-sitter for symbols, Rust + TypeScript in v1), emit a deterministic index.json with a content-hash staleness mechanism, and answer authorities(unit). Establishes the manifest/section/symbol resolution layer and the `spec-spine index` CLI subcommand.
The coupling gate: join the two views and refuse drift
The PR-time coupling gate (spec-spine.md guardrail 2): cross-reference every changed code path against the authority graph and refuse the merge when code drifts from the spec that owns it. A pure function of (config, registry, index, diff, waiver); the CLI parses `git diff --no-color -U0 --no-renames base...head` (quotepath off, end-of-options) into a typed DiffInput so git stays out of core. Matches a diff hunk to its owning unit at file, section, and symbol granularity; is amends-aware with the FR-005 strict-expansion guard; transfers authority across supersedes; honors PR-body waivers and an additive bypass list. Establishes the couple engine and the `spec-spine couple` subcommand.
Distribution: npm binary shim + the release pipeline
How the spec-spine CLI reaches users who do not have a Rust toolchain. Two channels already existed but were unspecced: the tag-gated GitHub release pipeline (release.yml) that builds the five per-triple archives, and install.sh (curl | sh) that consumes them. This spec adopts both as governed territory and adds a third channel: an npm binary-distribution shim under npm/ so a TS/JS repo can `npm i -D spec-spine` and get the CLI with no Rust. The shim is a launcher, not a native addon: the main package's bin resolves a per-triple platform package (optionalDependencies, os/cpu-gated) and exec's the prebuilt binary, forwarding argv and exit code. No postinstall, no network at install, no archive extraction; it works under `npm ci --ignore-scripts` and offline. Version-locked to the binary release tag (npm 0.1.0 ships the v0.1.0 assets).
Distribution: uvx/PyPI wheel shim
The Python parallel of 006's npm shim: a uvx/PyPI channel so a Python or polyglot team can `uvx spec-spine ...` (or `uv tool install spec-spine`) and get the CLI with no Rust toolchain. Where npm uses a main launcher package + five os/cpu-gated platform packages, Python collapses the same idea into one PyPI project + five per-platform wheels (+ one sdist): the wheel platform tag IS the selector, and the prebuilt binary ships in the wheel's data/scripts dir so pip/uv place it directly on PATH. There is no Python in the run path, no postinstall, no network at install, and no archive extraction at install: it works offline and under --no-build-isolation. Unsupported hosts (musl/Alpine, win-arm64, 32-bit) match no wheel and fall to the sdist, whose only artifact is a `spec-spine` console entry point that names the host and points at `cargo install spec-spine-cli` -- exact parity with 006 §3.4. Wheels are assembled from the same release archives the build job already produces (no second Rust build), are byte-reproducible, and are version-locked to the tag.
Coupling gate: explicit unit claims take precedence over the bypass floor
Amends 005 §3.5: a path covered by an explicit unit claim (a file, section, or symbol declared in some spec's ownership-bearing edge and resolved into the index) is NEVER bypassed, even when it matches the hardcoded floor or an adopter's additive bypass list. Implicit path-level ownership (manifest floors, comment headers) keeps deferring to bypass. Closes the silent- weakening hole where an adopter who governs workflow YAML or specific docs by explicit claim would have those claims ignored because `.github/` and `docs/` sit on the immutable floor.
Registry query: --ids-only and --nonzero-only projection flags
Two additive projection flags on the spec 002 query verbs: `registry list --ids-only` (newline-delimited spec ids; a JSON string array under --json) and `registry status-report --nonzero-only` (omit zero-count statuses). Both are load-bearing in adopter session-init protocols (OAP's /init and /setup read exactly these projections); neither changes any existing output shape when absent.
Index: implement the reserved render and orphans subcommands
Implements the two subcommands spec 004's CLI reserved but stubbed: `spec-spine index render` (a deterministic human-shaped markdown projection of the committed index.json -- package inventory, traceability, diagnostics) and `spec-spine index orphans` (the orphanedSpecs list, newline ids or a --json array). Both are pure read-side projections of the committed artifact; neither recomputes the index. Establishes the render module; extends the index CLI dispatch.
Index: named hash slices and `index check --slice <name>`
Named sub-hashes of the index's input set: an adopter declares glob groups under `[index.slices]`, the indexer emits a per-slice content hash into `build.sliceHashes` (additive index-schema MINOR), and `spec-spine index check --slice <name>` gates on exactly that slice -- fresh (0), stale (2), config/IO error (3). Generalizes the narrow high-value staleness gate OAP runs over its agent-config files (settings/MCP JSON) without hardcoding any adopter's file list into the core.
Frontmatter: declared extra keys carry arbitrary YAML, verbatim
Widens the value domain of DECLARED extra-frontmatter keys (`config.frontmatter.extra_known_keys`) from scalars + string-lists to any JSON-representable YAML value (nested maps, mixed lists), carried verbatim into SpecRecord.extra_frontmatter under canonical-JSON normalization. Undeclared keys keep the existing scalar/string-list restriction and the V-007 cap -- the anti-bulk-YAML guard is untouched. Registry schema MINOR. This is the load-bearing half of the overlay contract: overlays cannot consume domain frontmatter the compiler drops.
Edge grammar: `paths:` list sugar on extends/refines items
Authoring sugar for the predecessor dialect: an `extends:` or `refines:` item may declare `paths: [<file>, ...]` instead of a single `unit:`; the compiler expands each path to a file unit at parse time, emitting N normalized single-unit edges into the registry. Consumers see exactly one edge shape -- `paths:` never reaches registry.json. `unit:` and `paths:` on the same item is malformed (V-002). Unblocks adopting a corpus authored in the OAP dialect (140+ extends-bearing specs) without a mass frontmatter migration.
Frontmatter sugar: `unit:`-wrapped establishes items and the `n/a` implementation alias
Two pieces of authoring sugar for the predecessor dialect, both normalized away at parse so nothing new reaches registry.json. (1) An authority unit may be written as a single-key `{ unit: <unit> }` wrapper -- a third 1:1 representation alongside the existing bare-string and tagged-map forms -- which the Unit deserializer unwraps to its inner unit. The corpus uses this on `establishes` items (one wrapper per item, `unit` the only field). (2) `implementation: n/a` is accepted as a deserialize-only alias for the canonical `n-a`, which stays the only spelling ever emitted. Neither changes the registry schema or any consumer; both unblock adopting a corpus authored in the OAP dialect (389 wrapped establishes items across ~97 specs; 5 specs using `n/a`) without a mass frontmatter migration.
Compile: resolve short spec ids on `depends_on` and `superseded_by`
Lets a `depends_on` or `superseded_by` reference name its target by the leading spec number alone (`109`) and have the compiler resolve it to the full id (`109-slug`) when exactly one spec matches that number, rewriting the reference before validation and emission. An exact id, an ambiguous number, or a number matching nothing is left unchanged, so V-008 (superseded_by must resolve) and V-010 (dangling depends_on) still fire on a genuinely broken reference. The resolution policy is the indexer's existing `resolve_id` (spec 004), applied at compile time so the registry and the index agree. Unblocks adopting a corpus that references specs by number (106 specs in the OAP dry-run) without a mass frontmatter migration; a no-op on any corpus that already uses full ids (such as this repo's own).
Authority units: resolve the reserved directory / crate / module kinds
Lifts the v1 unit-grammar limitation (`docs/design/00-architecture.md` §2.2, Q5): the three reserved kinds (`directory`, `crate`, and `module`) are now parsed and resolved alongside `file`/`section`/`symbol`. A `directory` unit resolves to its subtree (existence-checked, I-007 on miss); a `crate` unit resolves a manifest name against the discovered package inventory to that package's directory subtree (I-003 on miss, hyphen/underscore interchangeable); a `module` unit resolves a `::`-qualified Rust module path via a new module index; file-modules whole-file, top-level inline `mod` blocks to their span (I-008 on miss). The registry/index schemas are permissive on the unit payload, so this is an additive MINOR (`INDEX_SCHEMA_VERSION` 0.2.0 → 0.3.0) with no schema-file edit. Unblocks adopting the OAP corpus, which authors these three kinds across ~192 unit declarations (93 `directory`, 99 `crate`, 1 `module`) with no mass frontmatter migration.
Constrains grammar: classification discriminator and optional unit
Widens the `constrains` item grammar to the two shapes the predecessor dialect authors: a **path-scoped** constraint (`unit:`, the canonical `invariant-freeze` over a file or schema) and a **spec-scoped** constraint (`target_specs:`, a sequencing/ordering plan over other specs with no code unit). Two changes: `unit` becomes optional, and an optional documentary discriminator is accepted under either spelling `flavor:` or `kind:` (synonyms, not gate-load-bearing). A new V-011 requires every item to scope at least one of `unit` / `target_specs`. A spec-scoped item contributes no resolved unit to the index. Unblocks the OAP corpus, which authors `{ flavor: invariant-freeze, unit }` (spec 130) and `{ kind: <plan>, target_specs }` (specs 093, 089), both rejected by the unit-required, discriminator-less v1 grammar.
Supersedes grammar: structured items and partial (unit-scoped) transfer
Implements Option A from `docs/design/01-partial-supersession.md`: a `supersedes` item may be a bare predecessor id (full) or a structured `{ spec, scope, unit?, note?, rationale? }`. A full item (bare id or `scope: full`) normalizes to the bare-string form, so a full-only corpus emits a byte-identical registry; the wire is unchanged for every existing adopter; only a partial item serializes as an object. `scope: partial` with a `unit` transfers authority over that unit alone (additive, the predecessor keeps it and everything else), threaded through the index as a `supersedes` resolved unit so the gate already treats the successor as an owner of that unit's paths; `build_superseders` therefore contributes only **full** supersession to the whole-spec transfer. A partial item with no unit (a documentary lifecycle marker) transfers nothing. Short ids on the predecessor resolve like `depends_on` (spec 015). Unblocks the OAP corpus shapes 073 (`scope: full`), 108 (bare short id), 114 (`partial` + unit), and 199 (`partial` + note).
Release supply-chain artifacts: per-target SBOM and build provenance
Every prebuilt binary spec-spine ships should carry a machine-readable bill of materials and a verifiable provenance trail. This spec adds, to the tag-gated release pipeline (006's release.yml), a per-target CycloneDX SBOM generated by syft for each of the five release archives, plus a SLSA build-provenance attestation over each archive. The SBOM names the dependency graph baked into that binary (read from the committed Cargo.lock, so it is as deterministic as the build); a fail-closed check refuses to ship an SBOM with zero components. SBOMs are attached to the GitHub Release as separate assets named to match their archive; the provenance attestation is recorded in GitHub's attestation store so any archive verifies with `gh attestation verify`. This is a port of OAP spec 117, scaled to spec-spine's single-CLI, five-triple release. The channels themselves (crates.io, npm, PyPI) are unchanged; this is purely additive supply-chain metadata on the binary release.
Bounded keypath section anchors for first-party structured config
Widens the section-anchor grammar (`sections.rs`, spec 004 §3.3) so a `{ kind: section, file, anchor }` unit can name a bounded mapping keypath inside a first-party structured-config file: a workflow YAML (`on`, `permissions`, `env`, `jobs.<name>`, `jobs.<name>.permissions`), a `Cargo.toml` table path (`workspace`, `package.metadata.oap`), or a `package.json` member (`scripts`, `dependencies`). The anchor field is already a free string (`Unit::Section`), so this is a permissive-payload additive change with no frontmatter or JSON-schema edit, mirroring spec 016. The motivating cases are ASI security boundaries: `permissions:` (GITHUB_TOKEN scope) and `on`/`on.merge_group` (the trigger and merge-queue contract). Section ownership of those keys means any token-scope escalation or trigger change forces the owning spec's review. A hard file-eligibility predicate keeps the grammar pointed at surfaces spec-spine has a first-party reason to parse and off foreign tool schemas (deny.toml, Helm values, k8s manifests), which their owners rev independently. This retires the `# region:`-in-structured-config escape hatch for the eligible surfaces: a structural keypath is computed from the document tree and cannot silently drift from the block it claims, the way a hand-placed region marker can.
Ledger Seal (signed, reproducible attestation over the spec corpus)
spec-spine self-describes as a typed, hash-verifiable authority ledger over a markdown spec corpus. A hash-verifiable ledger is an attestation missing only two things: a signature and a verify. This spec completes that sentence. It adds a CorpusAttestation, a deterministic, pure-function payload over the inputs manifest, the registry hash, and the compile/lint (and optionally coupling) verdicts, plus a detached Ed25519 seal over its hash, and a two-mode verify-attestation (recompute, which any third party can run with no key, and signature, which is opt-in). The reproducible recompute mode is the property a run certificate can never have, because runs are side-effecting; this object is the signed, archival, independently-verifiable form of a verdict spec-spine already computes and then throws away into a CI log. It is deliberately NOT called a certificate: that word stays with run-provenance (signed testimony of a non-reproducible event). This is an attestation (recomputable truth).
Per-unit shard storage (conflict-free committed registry + index)
The two committed artifacts (the spec registry and the codebase index) were each emitted as one file behind one global content-hash line with id-sorted arrays. Because every spec or package PR rewrites that one hash line, two PRs touching different units collide textually, and GitHub's server-side merge (the web button and the merge queue's speculative build) does not run the spec-020 merge driver, so the conflict surfaces there and the merge queue ejects sibling PRs. This spec shards both artifacts by authority unit: per-spec registry shards under spec-registry/by-spec/<id>.json, per-spec traceability shards under codebase-index/by-spec/<id>.json, and per-package inventory shards under codebase-index/by-package/<slug>.json, each carrying its own input hash. The global view (validation, orphans, untraced code, the aggregate content hash) is computed ON READ from the shard set rather than committed, so there is no shared file to conflict on. Two PRs touching different specs then write disjoint files and never conflict, so the merge queue forms clean speculative stacks and auto-merge stops ejecting in-flight PRs. Staleness becomes per-shard (sharper than the broad hash), the committed shards stay present-on-clone, and a MAJOR schema bump (registry + index -> 1.0.0) marks the new on-disk shape. This generalizes spec 011 (index hash slices) from scoped staleness CHECKING to per-unit STORAGE, and it removes the textual-conflict cause spec 094's merge driver only papers over locally.
Severity tiers for unresolved units: lifecycle- and edge-aware (warn, count, never skip)
Today the indexer hard-errors (I-003..I-009) on every declared unit that does not resolve, regardless of whether the edge is authoritative or whether the owning spec is even built yet. Two of those errors are not governance failures: an unresolved unit on a non-owning `references` edge is a stale provenance pointer, and an unresolved owning unit on a `draft` / `implementation: pending` spec is legitimate work-in-progress. This spec adds two orthogonal severity tiers over the same resolver, keyed on (a) edge authority and (b) owning-spec lifecycle, so those two cases become counted warnings (a fresh `W-001` / `W-002` band) instead of blocking errors, while an unresolved owning unit on an approved + implemented spec stays a hard error. The downgrade never skips: every such unit is still surfaced and counted, never silently dropped, so the authority-laundering / invisible-drift vector stays closed. The index never fabricates a code location for an unresolved path; authority remains a property of the registry relationship graph, which the coupling gate consults independently of index resolution. Additive: the `warnings` tier and the free-form diagnostic `code` already exist, so this is an `INDEX_SCHEMA_VERSION` MINOR (1.0.0 -> 1.1.0) with no schema-file edit and no registry change.
Indexer resolution + discovery fixes: foreign-YAML regions, Makefile tags, nested-workspace globs
Three mechanical correctness fixes surfaced by the OAP spec-217 Phase-0 dry run of 0.5.0, all in the indexer's resolution and discovery paths, none changing any schema or DTO shape. (1) Non-workflow YAML (Helm values, infra manifests) is dispatched to the CI-jobs parser, so its `# region:` markers never resolve and a declared section unit on one raises a spurious I-006; route foreign YAML to the region parser, which is exactly the whole-file / region ownership spec 020 §3.2 already promised foreign structured configs (retiring §4's deliberately-deferred D4). (2) A Makefile `## tag:` is silently overwritten when a second `## tag:` precedes any consuming target, so a tagged region is never emitted; make the pending tag deterministic so it is never silently lost. (3) Workspace-member globs declared in a NON-root workspace file (a nested `pnpm-workspace.yaml`) are resolved against the repo root instead of the declaration file's directory, so they match nothing; pre-join such globs with the declaration file's parent. A related lower-priority guard covers a `standalone_rust_workspaces` entry that ends in `Cargo.toml`. Pairs with spec 023 (the severity policy); these are the mechanical half of the 0.6.0 indexer work.
Feature-gate tree-sitter symbol/module resolution for dependency-free embedding
Adds a default-enabled `symbol-resolution` cargo feature to `spec-spine-core` that gates the tree-sitter dependency tree (tree-sitter + the Rust/TypeScript grammars) and the symbol/module resolution code. The CLI and `spec-spine index` keep default features, so adopter and self-governance behavior is byte-for-byte unchanged. Library consumers that read only the committed shards (`load_committed_registry` / `load_committed_index`) can build with `default-features = false` to drop tree-sitter entirely, which removes a hard blocker: tree-sitter declares `links = "tree-sitter"`, so a host workspace that pins a different tree-sitter (e.g. an analysis crate on `^0.26` while this crate pins `=0.25.10`) cannot otherwise resolve a single lockfile, and adding `spec-spine-core` anywhere in that workspace fails resolution before compilation. The seam is minimal: the `SymbolIndex` / `ModuleIndex` types and their `resolve` lookups are plain `BTreeMap` wrappers (no tree-sitter) and stay compiled, so the index resolver, its readers, and `compile()` build with the feature off; only the `build_*` functions and their parse helpers are gated. No DTO, JSON Schema, or `INDEX_SCHEMA_VERSION` change: the on-disk index shape is identical. Filed off the OAP spec-217 engine-swap work, which is blocked workspace-wide by exactly this `links` clash across its eight planned read-path consumers.
References provenance: optional derived_at timestamp
Adds an optional `derived_at` field to the `Provenance` struct carried on a `references` edge: `{ kind, ref, derived_at? }`. `derived_at` is a generic, optional ISO-8601 timestamp recording when the reference was derived, preserved verbatim with no format validation in the type (an adopter that wants format enforcement adds a lint). The field is additive and optional, so a corpus that declares none is unaffected and `deny_unknown_fields` is preserved (the field becomes known, not a hole in the schema). The registry format gains an emittable field, so `REGISTRY_SCHEMA_VERSION` bumps an additive minor 1.0.0 -> 1.1.0 (the permissive shard schema file is unchanged; the bump only restamps the `specVersion` carried by each committed registry shard, leaving every `shardHash` and `record` body byte-identical, because a shard hash is taken over the `spec.md` source bytes). Filed off the OAP spec-217 engine-swap, whose decomposition synthesizer (OAP spec 165) emits this shape because OAP spec 161 FR-007 requires `provenance.derived_at`; the published library rejected it at parse with `V-002: unknown field derived_at`, producing zero shards. This is the second small, broadly-useful additive concession from the 217 swap (the first was the spec-027 symbol-resolution feature gate, shipped as 0.7.0).
Cargo and workflow dependabot bumps self-clear: freshness projection + auto-waiver
Amends 004 §3.5 and 005 §3.5, extending the paired 2026-06-11 npm dependency-only mechanism to the cargo and GitHub Actions ecosystems so a dependabot-class bump self-clears without a human waiver. Freshness half: Cargo.toml folds into the index content hash as a governance projection (its dependency tables stripped), mirroring npm, so a version bump leaves the committed shards fresh. Coupling half: the mechanical auto-waiver recognizes a Cargo.toml whose only change is dependency version specifiers, and a `.github/workflows/*.yml` whose only change is the `@ref` of `uses:` action references. Every non-version edit (a new or removed dependency, a feature / git / path change, a `run:` / `with:` edit, an added step or table, an unpinned action, a spec-metadata edit) still refuses the waiver, fail-closed. A claimed workflow file (007/008/019's release.yml) and a crate whose direct dependencies dependabot bumps are the live acceptance tests.
Registry freshness: `spec-spine compile --check`
The committed spec-registry shard tree had no freshness gate. `index check` (spec 004) covers only the codebase-index tree, and `compile` is a validation gate that rewrites the shards without ever asserting the committed copies matched, so editing a spec.md and forgetting to recompile lands stale shardHash values with CI green (this is what PR #61 did to specs 016 and 021). This spec adds `spec-spine compile --check`: a non-writing gate that compiles in memory and compares the result byte-for-byte against the committed shards, exiting 2 on staleness exactly as `index check` does. It closes the registry/index asymmetry with a first-class command rather than a repo-local git tree check, so every adopter that commits its registry gets the same guarantee spec-spine gets.
Ownership coverage: `spec-spine index coverage` and the `C-002` ratchet
spec-spine refuses drift but has never required coverage. The coupling gate skips a changed path that no spec claims ("not a coupling concern"), and the only coverage-shaped signal, `traceability.untracedCode`, is package-granular and read by nothing, so a crate with one governed file and two hundred ungoverned ones reports as traced. "This application is fully specified" is therefore not a state the tool can assert. This spec adds two additive pieces that share one definition of ownership. `spec-spine index coverage` reports, per source file, whether a spec *specifically* claims it (a resolved unit or a comment header), whether only a package's manifest floor covers it, or whether nothing does; `--fail-on-untraced` turns that into the whole-tree assertion. `[coupling] require_ownership` (default false) makes the gate ask the same question of every changed source file and refuse a floor-only or unowned one as `C-002`, the PR-time ratchet. Coverage is a read verb over the tree and the committed ledger, like `index check`: nothing new is committed, no schema version moves, and the report predicts the gate exactly because both call one classifier over one universe.
Dependency cycle refusal: the `V-014` corpus-wide check on `depends_on`
`depends_on` is the one edge whose graph shape spec-spine never checked. A dangling target is a `V-010` warning, but a cycle passes: two specs that depend on each other compile with zero warnings and lint clean at every severity, so a corpus can be green and structurally unexecutable. This spec adds `V-014`, an error-tier, corpus-wide check that reports a cycle in the resolved `depends_on` graph and names the path. It is the sibling of `V-010` and lives beside it in `compile.rs`, the first step of the gate chain and the step `compile --check` verifies, so a cycle fails the build at the earliest point and no new verb is introduced. `V-014` joins `CROSS_SPEC_CODES`, so it is never stored in a registry shard and is recomputed from the assembled records on read; both the fresh-compile and committed-shard paths reach one detector, so the two views cannot disagree. Scheduling semantics stay out: readiness, contract pinning and downstream invalidation depend on merge history and a run journal, which are not a pure function of the corpus and therefore not spec-spine's to compute.
`references` seeds no implementing path: closing the C-001 ownership leak
The contract says `references` is the only non-owning edge and that the coupling gate ignores it. The indexer did not implement that. It seeded a spec's `implementingPaths` from every resolved unit location, owning or not, so a file a spec merely cited became a file it claimed. Every consumer reads that field as a claim: `couple.rs` treats a listed path as whole-file ownership, the indexer's own `owners_of_unit` does the same, and `orphaned_specs` and coverage read a non-empty list as "this spec claims something". One wrong seed made all of them wrong together. The observable defect is that a spec could not edit its own `spec.md` without a waiver once any other spec referenced it: on this corpus, changing `specs/094-one-gate-and-the-boundaries-it-holds/spec.md` was refused as `C-001` with `022-index-sharding`, which only cites it, named as the sole owner. This spec filters the seed by the `ownership` flag the loop already holds, at the single source rather than at each consumer. The citation is not lost: it stays in `resolvedUnits` with `ownership: false`, so provenance is preserved and only the ownership view changes. No DTO or schema shape changes.
A closed reader is not an error: stdout writes stop panicking the CLI
Exit codes are a stable contract: 0 ok, 1 validation failure, 2 stale, 3 I/O. The CLI could exit 101. `println!` unwraps its write, so a reader that stops early made the process panic: `spec-spine registry list --json | head` printed a Rust backtrace and exited 101, and under `set -o pipefail` that failed the surrounding script. Piping into `head`, `less` or `grep -q` is ordinary use, not an error condition. This spec routes every CLI stdout write through one helper that classifies the write instead of unwrapping it: a closed reader ends the process 0, because the consumer got what it asked for, and a genuine I/O failure reports and exits 3, which is the code the contract already assigns to I/O. Stderr keeps `eprintln!`: diagnostics are small, and a broken stderr has nowhere left to report itself. The fix is not `unsafe` SIGPIPE restoration, which `forbid(unsafe_code)` rules out workspace-wide, and not a panic hook, which would match on a message string the standard library is free to change.
The coupling gate honors `layout.specs_dir`
`layout.specs_dir` is configuration, and every other consumer reads it: `compile` scans it, `index` builds each spec's `spec.md` path from it, and `init` scaffolds into it. The coupling gate did not. Two functions in `couple.rs` hardcoded the literal `specs/`, and both are load-bearing. The primary-owner heuristic asked whether `specs/<id>/spec.md` was in the diff, so for an adopter whose corpus lives anywhere else the answer was permanently no: no edit to an owning spec could clear `C-001`, and every governed change was refused with the owner named and no reachable way to satisfy it, short of a waiver on every PR. The amends-awareness parser had the same literal, so amendment expansion silently never fired. Both now take the configured root, and the builder is shared with the indexer (`spec_md_rel`, promoted to `pub(crate)`) so a path constructor and its parser cannot drift apart again. A trailing slash on the configured value is trimmed, making `specs` and `specs/` name one corpus root instead of the second yielding `specs//<id>/spec.md`. No DTO, schema, or config shape changes; a default-layout repo is byte-identical.
Machine-readable verdicts: `--json` on the adjudicating verbs
The read verbs speak JSON; the verbs that render a verdict do not. `registry` (four subcommands), `index orphans` and `index coverage` have `--json`, while `couple`, `lint`, `compile --check`, `index check`, `attest` and `verify-attestation` emit prose to stdout and nothing else. A programmatic consumer of the gate chain is therefore forced to string-match sentences like `index is fresh` and to infer the reasons for a refusal from formatted text, which is precisely the ad-hoc parsing constitution §II forbids and the governed-artifact-reads rule was written against; the CLI currently leaves no alternative for exactly the commands whose output is a decision. The reports already exist: every one of these verbs is backed by a facade function returning a typed structure, and only the CLI surface is missing. This spec adds `--json` to each of them, behind one versioned envelope (`schemaVersion`, `verb`, `ok`, `exitCode`, and either `report` or `error`) whose `report` member is byte-identical to the corresponding facade output, so a consumer parses one header shape across the whole chain and one payload shape per verb, never two spellings of the same verdict. `--json` changes what is written, never what is decided: the exit code for every outcome is unchanged, and a failure is reported inside the envelope rather than as bare prose on stderr, so the machine consumer's happy path and error path have the same shape.
`registry plan`: the ready set in dependency order
`depends_on` records the order specs must be built in, spec 030 guarantees that graph is acyclic, and `implementation` records how far each one has got, but nothing joins the three: there is no query that answers "which specs can be worked on now, and in what order". Every consumer that needs it (a contributor picking up the corpus, a release note, an autonomous driver taking one spec per session) recomputes it by reading frontmatter by hand, which is both tedious and exactly the ad-hoc traversal the typed read verbs exist to replace. This spec adds `registry plan`: a read-only projection that partitions the corpus into a `ready` set (nothing unfinished blocks it) emitted in deterministic topological order, and a `blocked` set, each entry naming the unfinished dependencies that block it, so the output explains itself rather than merely listing. It is a scheduling projection and nothing more: `implementation` is a self-declared field that no gate currently verifies, so `plan` treats it as a hint about intent and never as evidence about code, and this spec deliberately does not let a scheduling answer imply that anything is done.
`layout.state_dir`: a declared, ungoverned tool-state root
A repo governed by spec-spine increasingly hosts tooling that keeps state inside it: a build daemon's journal, a local database, captured transcripts, attestation bundles. There is nowhere agreed to put any of it. Every such tool invents a directory, and every invented directory is then a surprise to the gates: `couple` sees changed files no spec claims and refuses the merge under `require_ownership`, `index coverage` counts them as untraced debt, and the resolver walks them. The adopter's only recourse is to keep appending private paths to `bypass_prefixes` and `resolver_exclusions`, one list per concern, none of which states what the directory *is*. This spec adds `layout.state_dir`: a single declared root, unset by default, that spec-spine agrees to know about and refuses to govern. Declared means the gates recognize it (bypassed by `couple`, excluded from coverage classification and from the resolver scan, and never hashed); ungoverned means spec-spine never reads its contents, never writes into it, and refuses a spec that tries to claim a unit inside it, which `lint` reports as `L-006`, a contradiction rather than a precedence question resolved silently in either direction. Unset by default, so no existing repo changes behavior.
An amendment is declared once, in the amending spec
The corpus has always authored amendments one way: the `amends` edge is declared in the amending spec's frontmatter, and the amended spec's `spec.md` is left alone. Spec 023 amends 004 without editing it; spec 033 amends 005 without editing it. The rule is enforced by nothing, stated by nothing, and visible only as a pattern in the history, so a reader looking at a diff cannot tell a deliberate convention from an omission. That is not hypothetical: the review of PR #89 asked six separate times for spec 030 to be edited to record that 038 had amended it, withdrew the request once on its own analysis, then raised it again, because nothing in the corpus says which answer is right. This spec writes it down, in the one-page contract where a reader checking the edge vocabulary will find it. It adds no mechanism, because the two mechanisms already exist and work: the gate's amends-awareness (005 §3.3) widens the owner set so editing the amender clears a violation on the amended spec's territory, and `registry relationships` reports `amended_by (incoming)` so the amendment is discoverable from the amended spec's side without that spec being touched.
`implementation: complete` defeats draft leniency
`index.rs::in_flight` is `status == "draft" || implementation == Pending`, and spec 023 uses it to downgrade an unresolved owning unit from a blocking `I-0xx` error to a counted `W-001` warning. The `Complete` arm of `implementation` is never consulted, so `status: draft` alone buys the leniency: a spec that states in its own frontmatter that its work is finished may claim units that do not exist, and the indexer counts a warning instead of refusing. That window is not an edge case in this corpus, it is the normal one. A spec is filed as `draft` with `implementation: complete` when its code lands (spec 033 did exactly that at 46474a3) and stays that way until a later ratification PR flips it to `approved`, so every spec passes through a period where its strongest claim about code is the one claim nothing checks. This spec makes `implementation: complete` decisive: a spec asserting completion is never in flight, whatever its `status`, and its unresolved owning units are hard errors. `status` and `implementation` are orthogonal axes (design ratification versus code existence), and where they disagree the more specific claim about code wins. It verifies existence, not behavior, and 3.3 is explicit that no gate can do more than that.
`attest --spec <id>`: a signed record scoped to one spec
Spec 021 attests the corpus: one payload, one hash, one optional Ed25519 seal over the whole ledger. The unit of work in a governed build is one spec, and a corpus-scoped attestation cannot answer "was this spec's territory sound when its work was declared done" without being reduced by hand. This spec adds `attest --spec <id>`, emitting a `SpecAttestation` scoped to a single spec: its own source hash, the resolved location and content hash of every owning unit it claims, and the verdicts restricted to it. It reuses 021's split exactly, the payload pure and reproducible with the wall clock and the signer identity in the detached seal, so the attested fact stays a function of `(config, file contents)` while the act of attesting is dated and attributed. It stays on-demand and gitignored like 021's, deliberately: the design note wanted a committed per-spec bundle, and committing one would restale on every edit to any claimed unit and would need its own freshness gate, which buys churn rather than assurance. A consumer that wants durable evidence receives the bundle; it does not need the repository to store it. One constraint is load-bearing and stated normatively: no lint rule may consume a `SpecAttestation`, because the payload records the lint verdict and a lint that read it would grade itself.
Three governance statements the first adopter could not act on
hqgit is the first repository outside this one to adopt spec-spine, and what its author had to write by hand is a measurement of what the governance documents fail to supply. Three findings. First, the constitution's own Amendment clause names a mechanism that cannot be written: it says the constitution may be amended by "an ordinary spec that `amends` it", but `amends` resolves to spec ids and the constitution is not a spec, so the sentence has no implementation, and hqgit copied it verbatim into a corpus where it is equally unwritable. Spec 037 4 named this as an open question and deliberately did not answer it; this spec answers it. Second, spec 000 3 files `depends_on` and `implementation` under "optional descriptive keys", which was true when it was written and is now false: 033 refuses a cycle in one, and 038 and 041 make the other decide scheduling and diagnostic severity. hqgit had to write its own "lifecycle as scheduling" section because tier 1 reads as though the field were inert. Third, constitution V says pre-existing code is evidence but never says what to write about code adopted with defects; hqgit supplied the missing half. The scaffold learns all three, because the scaffolded constitution is four bullets with no tier statement, no normative hierarchy and no amendment clause, and the evidence is that the first adopter did not use it.
`implementation: in-progress` is in flight
Spec 023 grants an in-flight spec leniency: an unresolved owning unit is a counted `W-001` warning rather than a blocking error, because work under way may legitimately claim territory that does not exist yet. Its predicate names `status: draft` and `implementation: pending` and does not name `in-progress`, so a spec whose implementation is openly underway gets hard errors for every file it has not written. Spec 038 3.1 wrote that cell down and 4 called it very likely a defect, then left it alone on the grounds that one spec should not both tighten and loosen the same predicate. This is the spec 4 deferred. `in-progress` and `pending` are the same claim about the filesystem, that the work is not finished, and only one of them buys the leniency designed for exactly that state. The asymmetry has no argument behind it: 025 never discussed `in-progress`, which is what makes the omission look accidental rather than chosen. It bites a corpus that ratifies a design before building it, which lives in `approved` + `in-progress` for the whole build; the rahi corpus is in that state today.
An absent `implementation` key takes its answer from `status`
`implementation` is optional, and two verbs read its absence in opposite directions. `registry plan` (spec 035) reads an absent key as `pending` and offers the spec as ready; the index's in-flight predicate (specs 038 and 041) reads `approved` + absent as settled and holds the spec to its claims. The one spec that routinely lacks the key is the bootstrap spec `spec-spine init` scaffolds, so every scaffolded corpus reports its bootstrap spec as the single ready item in an otherwise finished corpus, forever; one adopter does today, and this repository did until its own spec 000 was patched by hand. This spec fixes the rule rather than the instance: an absent key takes its answer from `status`. On a `draft` it reads as `pending`, as 038 intended; on anything ratified it reads as settled, as the gate already concludes. The scaffold's bootstrap spec declares `implementation: n-a` and the spec template states the key instead of commenting it out, so the absent case becomes rare as well as consistent.
`spec-spine verify <id>`: run a spec's declared acceptance
A spec's `## Verification` section states what would make its claims true, and nothing runs it. Three adopters independently wrote the same 78-line `scripts/verify-spec.sh` to close that gap, and spec 093 added a fourth copy to this repository, so the kit now ships the workaround it exists to retire. Spec 040 4 named this "the most substantial thing the adoption invented and the one most worth having" and deferred filing it. This is that filing. `spec-spine verify <id>` reads the spec's `## Verification` section, runs each line of its `verify:cli` fences from the repository root in order, and stops at the first failure. Two things make it a spec-spine verb rather than a script: the parse becomes a typed, tested read of authored markdown that the library owns, and the outcome becomes a spec 034 verdict envelope instead of a sentence a caller must string-match. The engine never spawns a process. Core returns a `VerifyPlan`, the commands it found and the fence tags it declined, and the CLI executes it, which is the same seam `couple` already uses for `git`. Two behaviours diverge deliberately from the ported script, both to keep an older contract intact: a missing spec exits 1, not the script's 2, because 2 means stale; and a failing command exits 1 with its own code carried in the payload, rather than propagating a code the exit contract does not define. `not-declared` stays an honest zero and is distinguishable from a pass in the payload, never by parsing prose.
Index diagnostics reach a gate
Spec 023 downgrades an unresolved unit to a counted `W-001` (owning unit on an in-flight spec) or `W-002` (non-owning `references` unit) instead of failing the index, because work under way legitimately claims territory that does not exist yet. That downgrade is right and this spec does not touch it. What 025 never gave the warning is a reader. The codes are deliberately absent from `BLOCKING_CODES`, `lint` counts a different set entirely (`L-001`..`L-006`), and `index check` prints one word, `fresh`. The only way to see them is to run the *writing* command, which mutates the tree, or to read `index render`'s markdown, which is the ad-hoc parsing constitution II forbids. So a corpus accumulates them without limit while every gate stays green: aicortex carries 248 that nothing reports. The asymmetry is exact, because error-tier diagnostics are gated: `I-003`..`I-009` mark their shard stale and `index check` exits 2. This spec gives the warning tier the same reachability without giving it the same force. `index check` reports the counts it already holds, `--fail-on-unresolved` turns them into exit 1 for a corpus past the specify-first stage, and `index diagnostics` lists them as typed output. All three read the committed shards and recompute nothing, so the answer always describes the ledger the corpus compiled to.
The coupling gate names the crossing
The coupling gate has always named the specs that own a drifted path, and has never named the mechanism for crossing into their territory. Its resolution footer offers two doors: edit an owning spec, or waive. For an author who has legitimately touched a unit somebody else owns, both are shut. Editing another spec to clear a refusal is the illegitimate edit the corpus rules forbid, and the waiver is a human instrument that no adopter has ever used and that an unattended session is forbidden on every path. The third door, an `extends` edge declared in the author's own spec, is the corpus's actual answer and the gate never mentions it. This spec makes the refusal name it: three doors in author order, and when the diff edits exactly one spec.md, the concrete `extends` block to paste into that file. It also promotes the owner set from prose into an optional `owners` field on the violation, so an orchestrator reading `couple --json` learns the owner as data instead of by regexing English. No committed artifact and no schema version moves.
A dependency points backward, and the corpus can say so
Three adopters independently wrote the same ninety-line `scripts/spec-dag.sh` to assert one thing the tool does not: that a `depends_on` entry names a lower ordinal than the spec declaring it. Spec 030 already ships the harder half of that graph check, refusing a cycle at compile time, and ordinal monotonicity is the cheap total order that makes a cycle impossible by construction rather than detectable after the fact. This spec adds `L-007`, an opt-in conformance lint gated on a new `[lint] require_ordinal_monotonic_depends_on` knob, defaulting off so no existing corpus changes verdict. It is deliberately a lint and not a `V-` code: a forward dependency is a corpus convention an adopter may reasonably not hold, not a structural defect that makes the registry unreadable.
The effective configuration is a governed read
`spec-spine.toml` is only half the configuration a coupling decision is made from. The other half is `DEFAULT_BYPASS_PREFIXES`, a thirteen-entry constant compiled into the binary, additive to the adopter's list and impossible to read from outside the process. claude-observatory, which judges other repositories, hand-parses each target's TOML and documents in its spec 015 D-3 that the built-in floor is simply unknowable to it. That is a consumer reimplementing half a contract and knowing it has the other half wrong. This spec adds `spec-spine config show [--json]`: the effective configuration, every default resolved, with the bypass floor rendered as the merged list the gate actually matches against and each entry attributed to the built-in floor or to the adopter's file. It reads and reports; it decides nothing.
The ledger answers what consumers rebuild by hand
Two facts the ledger computes and then keeps to itself, each of which an adopter has reimplemented. Ownership of a path is resolved inside `couple.rs::owners_for_path`, a private function, so a consumer that wants to know which spec governs a file rebuilds a path-to-spec map from raw edges and gets the subtree, supersession and comment-header rules wrong. A spec's content hash is written into every registry shard as `shardHash` and is absent from every read verb, so claude-observatory reimplemented `hash.rs` normalization in TypeScript in order to pin against it. This spec adds `spec-spine index owner <path>` and puts `contentHash` on `registry show --json`'s output object. Neither touches a committed artifact, a DTO, or a schema version: both facts already exist and are merely unreachable.
Validate one draft without a temporary repository
Writing a spec means validating it before it lands, and the tool offers no way to do that. `compile` writes the whole shard tree, which an author with an unfinished draft must not do; `compile --check` refuses the whole corpus because the draft has no committed shard, reporting the author's work in progress as staleness. So two adopters wrote down a `mktemp -d` ritual: copy the corpus, copy the draft in, compile there, read the violations, delete the copy. It has a documented gotcha, because a `references` edge into `docs/` does not resolve in a corpus copy that has no `docs/`. This spec adds `compile --spec <id>`: validate one spec against the committed registry, report only that spec's violations, and write nothing.
A claim no hash witnesses
A `file` unit carries no span, and only span-backing source files are folded into a per-spec index shard hash. So a spec can claim a file, `index check` can report fresh, `index coverage` can report it claimed, and the file's contents can be rewritten from end to end with no gate noticing. This is not hypothetical in this repository: `AGENTS.md` is claimed by three specs, and appending a line to it leaves both `index check` and `compile --check` reporting fresh. Twenty-four claimed paths here are in that state, and spec 021 signs an attestation over a ledger that never hashed any of them. This spec adds `L-008`, a warning naming each claimed path that contributes to no content hash, and one line of `index check` output reporting the count. It does not fold claimed files into the hash: that would change what staleness means, and the honest first move is to say how large the gap is.
The defects heading has one spelling
Constitution V requires code adopted from outside the corpus to be specced as found, with the behavior the adopting spec would not have chosen recorded under a `## Known defects` heading. It does not say what counts as that heading, and the first consumer to check mechanically got it wrong: claude-observatory's `defects.ts` matches the string `## Known defects` exactly and therefore rejects the numbered headings of the very specs it cites as precedent. This spec settles the spelling as an anchor rather than a string: any heading whose computed slug is or ends with `known-defects`, at any level, under the same slug rule the indexer already uses for section units. It adds `L-009`, an info-tier lint for a near-miss spelling, and it declines to lint `origin.retroactive` into implying the heading, because spec 040 established that the two are orthogonal.
Two read verbs that mislead a specify-first corpus
Three of the four governed repositories are specify-first: the whole corpus is ratified before a line of code exists, and specs live at approved plus pending for months. Two read verbs give that corpus an answer that is technically correct and practically a lie. `index orphans` reports sixty-four of hqgit's sixty-eight specs, because an orphan is a spec claiming nothing that resolves, and a spec whose code is not written yet claims nothing that resolves; the list is the corpus, so it says nothing. `index coverage --fail-on-untraced` on a tree with no discovered package enumerates zero source files, computes zero untraced, and exits 0 from inside a CI step named "the whole-tree ownership assertion". This spec makes the first partition by the in-flight predicate the index already computes, and makes the second refuse an empty universe instead of passing it.
The plan answers the whole question, and names one pick
`registry plan` prints spec ids and a count. Every consumer needs more than that, so every consumer adds it back: adopters kept about forty lines of Python to join the ready ids against titles and to explain why each blocked spec is blocked, and the corpus's own `/next` skill has to make a second call to turn an id into something a human can read. The data is all there. `Plan` already carries each blocked spec's blockers with the state of each, and the registry it was computed from carries every title. This spec makes the prose form render what the structure already holds, and adds `--next`, the single pick that the one-spec-per-session loop actually asks for. The JSON shape gains title fields and is otherwise unchanged.
The scaffold ships what every adopter wrote by hand
Four scaffold defects the audit measured, all in one generator. `init` writes no `.gitignore`, so every adopter independently learns that `.derived/**/build-meta.json` carries a wall clock and dirties the tree, and spec 036's `state_dir` has the same missing half: the live failure it fixed was a permanently dirty tree, not a classification. The scaffolded `spec-spine.toml` emits five knobs; adopters needed thirteen, and aicortex annotated each of its own with the diagnostic code that knob drives, which is the template to copy. And `CONSTITUTION_TEMPLATE` in `scaffold.rs` is still the two-bullet stub spec 040 complained about, while the real thirty-four-line document sits in `standards/spec/templates/constitution-template.md`: adopters get the stub, and two of them deleted it. This spec fixes all four in `scaffold_init`, which stays a pure function of `Config` performing no IO.
A version pin the CLI can check
A repository is governed by whichever spec-spine binary happens to be on the path, and nothing anywhere says which one it should be. rahi states 0.11.0 in three files that must agree by hand. All four governed repositories are two to four releases behind the fixes their own experience motivated, and rahi cannot see spec 041, which was written for rahi's exact state, because it is pinned below it. This spec adds `[meta] required_version`, a semver requirement the CLI checks on every run, refusing with a message that names both the requirement and the running version. The schema versions already tell a loader whether it can read an artifact; this tells an operator whether they are running the tool the corpus was governed by, which is a different question and currently has no answer at all.
The contract records the lifecycle table and the extra keys
`status` and `implementation` are the two keys every adopter reasons about daily, and the contract mentions `status` once, in a list of required frontmatter, and `implementation` not at all. The mechanical consequences are spread across four specs: 041 makes a `complete` claim checkable, 044 defines the in-flight window, 045 says an absent key takes its answer from `status`, and 038 reads both to build the ready set. Every specify-first adopter wrote the resulting table into its own contract by hand, and hqgit had to invent a section called "Lifecycle as scheduling" because ours has none. This spec adds that section and an "Extra keys" section telling adopters to document their `extra_known_keys`, and claims both as section units of the contract.
The docs name what adopters derived by experiment
Three documentation gaps the audit measured, each with a named victim. There is no page for specify-first adoption, which is how three of the four governed repositories work, so each of them independently derived the guarded Makefile, the CI manifest probe, `implementation: n-a` for record specs, and the fact that two hundred and forty-eight W-001 warnings is the defined state rather than a backlog. Two interactions are documented nowhere and were found by experiment: a path can be on the coupling bypass floor and simultaneously a hashed input, and a trailing-slash directory unit in `establishes` satisfies ownership recursively. And spec 034 changed `attest`'s stdout, which claude-observatory regexes in two places, with no migration note. This spec writes the page and the three paragraphs.
The shipped default hashes what it names
`IndexConfig::default().extra_hashed_inputs` ships as `["standards/**", ".github/workflows/**"]`. In the glob crate `dir/**` enumerates directories, and the hasher keeps only entries that are files, so the shipped default matches nothing: every adopter who has not overridden it folds zero extra files into the content hash and is told nothing. Spec 050 found the form in this repository's own config and fixed it there; spec 054 found it in the default behind it, emitted the key at its real value with a comment naming the trap, and deferred the fix to its own spec on the grounds that changing it restales every adopter's committed index. This is that spec. The default becomes `["standards/**/*", ".github/workflows/**/*"]`, the working form 054's comment already tells adopters to write, so an adopter who followed that advice sees no change and one who did not gets a one-time restale and a governance surface that is finally in the ledger. A regression test pins the property that was never asserted: that the shipped default matches files.
A malformed id is refused, not a panic
`detect_duplicates` reads a spec id's V-004 prefix as `&id[..id.len().min(3)]`, a byte slice. An id whose fourth byte falls inside a multi-byte character panics the process instead of diagnosing it, so `a日本-spec` exits 101 with a backtrace where the corpus should have been refused at exit 1 by V-012, the code that exists to name exactly that malformation. Spec 046 §4 found the defect, declined to fix it from inside a lint change, and asked for this spec. The fix reads the prefix the way spec 001 §3.2 already describes it, as a numeric prefix (`NNN`), using the character-safe leading-digit-run rule spec 046 established in `lint.rs`.
A workflow bump is not a governed change
A GitHub Actions workflow enters the index content hash as raw bytes, so a Dependabot `uses:` bump moves the global-inputs scalar and stales every shard in the repository. The bot can neither re-index nor add a waiver to its own PR body, so the PR walls on exit 2 and needs a maintainer to push a re-index commit. Spec 027 built the coupling half of this for workflows and read the freshness half as already fine; it was not, and spec 058 made that visible by fixing the default glob, then deferred the real fix as a hashing change with a migration of its own. This is that spec. A workflow folds as its governance projection, the parsed document with the pinned ref of each `uses:` reference removed and the action path kept, so a version or SHA bump is invisible to the ledger while a changed action, a `run:` or `with:` edit, an added step or an unpin all still stale it. The projection and 027's waiver classifier are required to state one rule, and the upgrade restales every workflow-bearing repository once.
Shipped is not the same as working
Two adopters installed spec-spine on the same day and each found a shipped artifact that does not do what it says. A glob form that matches no files reached one by hand and the other through a pre-069 scaffold, where the fix to the default can never arrive and 058's migration note explicitly does not look. `init --with-kit` writes the merge-driver hooks without an executable bit and without the binding that makes them fire. It installs a script the tool absorbed two releases ago, and it will write a second spec at an ordinal the corpus already uses. The session protocol calls a version read a precondition and never schedules it, while the skill beside it still prescribes the stderr ritual that protocol retired. Every one of these shipped for the same reason: the acceptance asserted that a thing exists rather than that it works. This spec closes them together and adds the refusal that would have caught the first one in an adopter's own config.
One name for the protocol, one verb for freshness
The session protocol is ambiguous in two places and both are collisions with its own neighbours. Its entry point is named `/init`, which is also a Claude Code built-in meaning the opposite thing (one-time, repository-level, and it writes) and also `spec-spine init`, which means a third thing again. And it asks one question, whether the committed state is current, in two spellings: `compile --check` is a flag and `index check` is a subcommand, so the protocol, the hook, the skills and CI each have to spell both. This spec renames the skill to `/prime` with no alias, and adds `spec-spine check`, an additive top-level verb that runs both freshness reads, reports each tree separately, and composes one exit code. Nothing is removed: both primitives keep their flags, their tests and their contracts, and CI may still call one when it regenerated only one tree.
Planned territory is declared, not inferred
A spec cannot say what it intends to own. Ownership is only ever inferred from a unit resolving on disk, so a corpus that opts into spec 044's `--fail-on-unresolved` gate forecloses declaring territory before writing it: every claim must arrive with its file. The cost is that `registry plan` and `index coverage` are blind to planned ownership, a draft's risk understates the work, and two specs can plan the same file with no edge to collide on until one of them ships. This spec adds an optional `planned` flag to the unit payload. A planned unit is a declared state rather than an unresolved one, so it produces no `W-001` and the gate keeps refusing every claim that is merely broken. The flag is bound to the lifecycle field that ends it: a spec at `implementation: complete` carrying one is refused, and a planned unit that has resolved is reported so the flag cannot be left on.
Compile warnings reach the gate
`V-010`, a `depends_on` naming a spec that does not exist, is the only warning-tier code the compiler emits, and no verb in the gate chain can be told to refuse it. `lint --fail-on-warn` cannot reach it: lint's codes are disjoint from compile's by design, and `lint()` keeps only the registry from the compile it runs. So a dangling dependency compiles clean, lints clean at every severity, and reaches the default branch unread. This spec adds `--fail-on-warn` to `compile` and forwards it through `check` into the compile half, the shape spec 044's `--fail-on-unresolved` already uses for the index half. The flag gates the CLI's exit code only; `validation.passed` keeps the meaning spec 001 fixed for it, and `V-010` keeps its warning tier, because a corpus that files specs forward must be able to name a dependency it has not written yet. Zero `V-010`s exist today: the gap is structural, and this spec closes it before it costs something.
A dead glob is dead in both tables
`L-010` refuses a pattern ending in `/**` in `[index] extra_hashed_inputs`, where it enumerates directories and can contribute no bytes. `[index.slices]` carries pattern lists with the same documented semantics and the same file-only walk, and the lint is silent about it. An adopter audited on 2026-09-09 carried seventeen dead patterns, nine in the table the lint reads and eight in the table it does not. A fix pass corrected all nine and six of the eight; the two survivors reached an already-reviewed pull request and were caught by a second human reading, since no tool could report them. Both pointed at directories that do not exist yet, so they would have stayed silently empty on the day those directories arrived. This spec extends the rule to the second table, and requires the slice message to speak about the slice's own hash rather than repeating a sentence about `contentHash` that is false for slices.
An attestation covers the territory it claims
`attest_spec` hashes a unit by opening each resolved location with `read_to_string`. Several unit spellings resolve to a directory rather than to a file, so the read fails with `Is a directory` and the verb exits 3: fourteen of this corpus's eighty-three specs cannot be attested at all, and the failure arrives as an I/O error rather than as a verdict. The dominant spelling is not the obvious one: thirteen of the fourteen are plain `file` units whose path is a subtree (`npm/`, `.claude/skills/`), which spec 000 4.2 defines as resolving identically to an explicit `directory` unit. Spec 039 3.1 already states what should happen, that `units` carries "the content hash of what it resolved to" for every owning unit, without qualifying by kind, so this spec changes nothing 042 requires and supplies the implementation that sentence never had. A directory-resolved location is walked instead of opened: every file beneath it, pruned by `index.resolver_exclusions` and `layout.state_dir` through the indexer's existing walker, folded into the same path-sorted `hash::content_hash` construction the file units already use. The walk is deliberately not extension-filtered, because the coverage universe's source extensions would hash zero files for a markdown-only directory such as `.claude/agents/` and emit a confident hash over nothing. A directory that walks to no files at all hashes its own path as a single piece, so an empty claim cannot attest as SHA-256 of the empty input, a constant every empty claim would otherwise share. No previously emitted payload changes value, because every spec this reaches could not produce one.
A short id names the same spec at every verb
Six arguments take a spec id, and they disagree about what one is. `verify` and `compile --spec` accept the short form spec 015 defines; `registry show`, `registry relationships` and `attest --spec` refuse it at exit 1; and `verify-attestation --spec` fails at exit 3 reading a file named after the raw argument. Specs 043 3.2 and 056 3.1 each state the cross-verb rule as already true elsewhere, and nothing enforces it. The policy itself exists in four private copies, two strict ones for arguments and two lenient ones for frontmatter references, and the two strict ones test the exact case by joining the argument onto a path, so `verify ../specs/<id>` resolves and `compile --spec ./<id>` reports a false `V-001` on a valid spec. This spec puts the policy in one module that every argument and both reference resolvers call. Each verb resolves against the ids it already reads, and every value derived from the id after resolution uses the resolved id, which is what keeps `relationships` from computing incoming edges against the raw argument and `attest` from naming its output file after it. A census over the CLI's argument tree fails when a seventh id argument appears outside the matrix. It amends 016 2, whose mirror becomes a shared call. The implementation changes no committed shard: the lenient adapters return what the mirrors returned.
A verifier checks the bytes it was given
`verify-attestation` decides on a different object from the one it was handed. It deserializes the attestation without refusing members it does not know, and it checks the seal against a hash of its own re-serialization of what it parsed, never against the file's bytes. Measured at 75181a5: a sealed attestation with an injected `"prCouple": {"ok": true}` verifies as `match` and `valid` at exit 0, in both the corpus and the per-spec scope, and so does a reformatted copy. Separately, the corpus recompute never compares `schemaVersion`, so an attestation claiming `9.0.0` recomputes as `match` at exit 0, while the per-spec path reports an unnamed content mismatch. Spec 021 AC-4 already requires a single tampered payload byte to fail the signature, and the attestation module documents that loaders reject an unknown MAJOR, so this spec extends rather than amends: the verifier reads the file once, both modes decide on those bytes, every attestation and seal DTO refuses unknown members at exit 3, an unknown schema MAJOR is refused at exit 3 in the loaders' own words, and the corpus recompute compares the version. Nothing `attest` emits changes, and every attestation and seal it has ever written still verifies, provided the bytes were kept.
The committed index is compared, not trusted
`index check` does not compare the committed index with what the corpus indexes to. It reads each committed shard's own mapping, derives from that mapping which span files to hash, and compares the resulting hash with the shard's `shardHash` field. The mapping body itself (which units resolved where, which paths implement which spec, the ownership flags) is never compared with a fresh resolution, and for a `file`, `directory` or `crate` unit, which carries no span, nothing in the hash constrains it. Measured on a scratch repository: rewriting a committed shard so its spec owns nothing, with `shardHash` left alone, leaves `index check` and `check` reporting fresh, and `couple` then derives ownership from the edited body and passes a change to code the spec owned, without its `spec.md`. `.derived/` is in the bypass floor, so the edit itself trips nothing. AGENTS.md tells every session that a fresh `check` means the committed shards are exactly what the corpus compiles to; for the index that is not what is checked. Spec 028 3.1 recorded the weaker comparison as a cost trade; a full re-index of this repository takes 0.04 s against 0.03 s for the check. This spec makes `index check` compare bytes and set membership exactly as `compile --check` does, so every reader of the committed index (the coupling gate, `index owner`, coverage, the projections) reads a body equal to the recompute. It amends 024 5, whose bounded trade let a sibling-caused resolution flip go unreported.
An authority snapshot says what it read
A consumer that binds spec-spine's authority results to a revision needs five separate outputs today and still cannot answer four questions from them: whether the committed ledger matched the corpus (attestations recompute and never read the committed shards, and `check --json` reports booleans without the digests it compared), which configuration and governance files were read, what every spec's territory hashed to, and how much source no spec claims. This spec adds a third attestation scope, `attest --snapshot`, emitting an `AuthoritySnapshot`: the tool and schema versions, a digest of `spec-spine.toml`, the existing corpus hashes, the committed registry and index trees with a digest each and whether each matches the recompute, the governance inputs by path and digest, the gate verdicts, the ownership counts, the exclusions in force, and each spec's lifecycle with a framed digest of its resolved territory (plus, for joining records issued earlier, the hash of the `SpecAttestation` spec 039 would emit for it, or a stated reason where that construction has no answer). New members use a framed digest that binds normalized text rather than exact bytes, a contract 3.3 states explicitly and distinguishes from spec 068's, because the existing fold does not frame content and two trees can share one hash; existing members and payloads keep their constructions. The payload is pure, on demand, gitignored, sealable and verified under spec 068's rules. It commits to no git revision: the consumer binds the tree.
A change is classified under the base's rules
The coupling gate clears C-001 when any owning spec's `spec.md` is in the diff, and reads the owners from the candidate's own index. Measured on a scratch repository, a candidate that deletes a command from its own `## Verification` block while changing the code that command checked passes `couple` at exit 0, and so does a candidate that files a new draft spec with an `extends` edge on another spec's unit and rewrites that unit. Both are changes a reviewer must weigh under the rules that held before the change, and nothing reports them as such. Design note 02 G7 says no refusal should be asserted before it is designed, because the naive rule refuses legitimate refactors. This spec therefore adds a report, not a gate: `spec-spine delta --base B --head H` classifies every changed path into implementation, requirement, verification, authority, lifecycle, constitutional, policy, derived, bypassed, unowned or unknown, computed under the merge base's own configuration and index so a candidate cannot reclassify its change by editing `spec-spine.toml`. It records the base and head plan digests for every spec whose declared acceptance changed, the owner sets on both sides of every path whose ownership moved, and a `priorPolicy` block naming the classes a consumer must judge under the base's policy. It interprets no prose, infers no moves, and decides nothing.
Two ready specs can collide
`registry plan` answers "what can be worked on now", and a scheduler fans that answer out. Readiness is computed from `depends_on` alone, so two specs with no edge between them are both offered even when they claim the same files. The corpus has the case standing today: drafts 084 and 085 both extend `crates/spec-spine-cli/src/verify_attestation.rs`, `attest.rs` and `lib.rs`, neither depends on the other, and `plan` lists both as ready with no way to say so. This spec adds the overlap report: for every pair on the ready set, the units both claim. It is a report and never a refusal, and it is a lower bound rather than a safety verdict: disjoint territory does not prove independence, because a shared lockfile, a regenerated shard or a consumed API are collisions no frontmatter declares. `plan` keeps exiting 0.
A mode-only or binary change is a change
`couple` builds its `DiffInput` by parsing `git diff --no-color -U0 --no-renames`, and registers a path only when it meets a `---` or `+++` header. Git prints neither for a mode-only change (`old mode` / `new mode`) nor for a binary change (`Binary files a/x and b/x differ`), so both arrive at the gate as no change at all: an owned binary can change without its spec and `C-001` stays silent, and a mode flip on an unclaimed source file is invisible to `C-002`. Spec 071 met the same blindness while building `delta`, worked around it with a `--name-only` helper, and recorded in D-9 that `couple` is spec 005's territory. This spec unions the name list into the parsed diff: a path git reports and the hunk parser did not register enters `DiffInput` as a whole-file change with no spans, deleted when the status letter says so, and is then judged by the same clearance rules as every other path.
A governed read names its version
`docs/api.md` states that every JSON this tool emits is pretty-printed with sorted keys, and spec 034 gave every verdict verb a versioned envelope. The read verbs got neither. Measured at 0.19.0: thirteen read documents across ten verbs, of which twelve carry no version member at all and seven emit keys in struct declaration order, because they serialize with `serde_json::to_string_pretty` while every committed artifact goes through the canonical writer. A consumer therefore pins a document whose shape it cannot dispatch on and whose key order is an implementation detail of a Rust struct. This spec routes every read document through one emitter that sorts keys and stamps a new `READ_SCHEMA_VERSION`, and decides note 04's D7: the version is an additive member on an object document, and the documents that are not objects today become objects so they can carry one. Four documents across three verbs change shape breakingly, and two of the four are contracts approved specs state, so this spec carries `amends` edges to 010 and 060 rather than editing them.
A claim below the header window is not silent
A `// Spec: specs/<id>/spec.md` comment header claims the file it sits in, and `index.rs::scan_comment_headers` looks for it in `content.lines().take(16)`. That bound is claim eligibility, and no spec, standard or adopter-facing document records it: a header on line 17 claims nothing, the file then reads as unclaimed, and under `[coupling] require_ownership` a change to it is a `C-002` refusal for a reason nothing states. Two neighbouring failures are equally silent: a header whose reference names no spec in the corpus, and the `//!` doc-comment form, which the scanner deliberately does not accept. This spec declares the window (16, unchanged) and the recognizer exactly as it ships, and makes `index coverage` report the near misses, so a file that tried to claim itself and failed is distinguishable from a file that never tried. The recognizer is looser than the documented form in three ways, and 3.2 records them rather than correcting them: declaring a rule is not the place to change it.
A stray shard is orphaned at the verbs
Spec 069 §3.1 says `orphaned` covers "a stray file in either shard directory", and `compare_shard_dir` classifies by name without parsing, so the library holds to it. The verbs do not. `check_report` and `check_freshness_json` compute the freshness verdict and then call `diagnostics::committed_counts(config, repo_root)?`, which parses every file in the shard directories; an unparseable stray makes that `?` throw the computed verdict away, and `index check` and `check` exit 3 with a parse error where `couple` and `index coverage` exit 2 and name the orphan. Measured at 0.19.0, and the same at 0.18.0, so it is not a regression and not a bypass. 069's acceptance asserted the classification on the library function only, which is exactly how it passed. This spec makes the tally best-effort and the verdict authoritative, and asserts it at the verbs.
One hash, one construction, one name
`registry show` prints `contentHash: <h> (sha256 of this spec.md)`, and approved spec 048 §3.4 says the same thing in prose: "`contentHash` is SHA-256 over that spec's `spec.md` alone". It is neither. The value is the shard hash, which is SHA-256 over the repo-relative POSIX path, a NUL, and the normalized bytes, so for `specs/069-.../spec.md` the printed value is `a4a0098235e9...` while the digest of the file's bytes is `ae0144ca7e4a...`, the value `attest --spec` reports as `specSourceHash`. 048 §3.3 exists for consumers who reimplement the normalization to pin against the ledger, and one adopter does exactly that, so a gloss naming the wrong construction is the whole defect. This spec amends 048 §3.4 to state the construction the code has always used, makes the printed line say it, and adds the test that pins both digests so the two cannot drift again.
Governed scope is declared, not inferred
The ownership ratchet sees a file only if all four conjuncts of `coverage.rs::in_coverage_universe` hold: the extension is in `SOURCE_EXTS`, no path component is a resolver exclusion, the file lies inside a discovered package, and no bypass prefix matches. Measured on 2026-09-15: this repository tracks 572 files and the ratchet can see 89. Nineteen tracked files carry a `SOURCE_EXTS` extension and are invisible on the package conjunct alone, and eleven of those nineteen are files `[index] extra_hashed_inputs` already names, so a change to one restales all 101 shards while `C-002` cannot ask who owns it. Seven of the nineteen already carry a valid `// Spec:` claim header that nothing ever reads. The two lists disagree about what this repository governs. This spec adds an opt-in declared scope: named path patterns that join the coverage universe whatever their extension and wherever they sit, with the tracked-file enumeration done by the CLI and passed in, because the core has no git. Empty by default, so no adopter's verdict changes on upgrade. The claim scanner is untouched: a scoped file is claimed from spec frontmatter, so those seven headers stay inert and this spec says so rather than implying otherwise.
A blocking claim is not a stale shard
`check_index_freshness` folds two different refusals into one verdict: a committed shard whose bytes moved, and a spec whose claimed unit does not resolve. Both arrive as `Freshness::Stale`, so `check` prints "codebase-index: STALE (run `spec-spine index`)" and `index check` prints "index is STALE (run `spec-spine index` to refresh)" for a corpus whose shards are byte-exact. The named remedy provably does not work: reproduced at 0.19.0, `index` exits 0, writes the shard, and `check` still exits 2, because re-indexing cannot create a file a spec claims. An adopter documenting exit 2 as staleness follows that advice indefinitely. This spec keeps the refusal and the exit code exactly as they are and changes what the two freshness verbs say: a blocking resolution diagnostic is reported as its own class, names the owning spec and unit, and says regeneration will not clear it. The mixed case reports both and says which half regeneration addresses.
An unresolved claim exits as a validation failure
Spec 079 stopped `check` calling an unresolved claim "stale" and stopped it prescribing `spec-spine index`, a remedy that provably does not work. It deliberately left the exit code alone, because 069 3.1 pins "the exit code is unchanged: 2 when anything drifted" and adopters branch on it. The result is a verb whose prose says "this is not staleness" while its exit code says staleness, and exit 2 is the code an adopter's CLAUDE.md maps to "run spec-spine index". This spec moves an unresolved claim to exit 1, the validation code, by an `amends` edge on 086. A spec claiming a unit that does not resolve is a corpus that does not describe its tree, which is a validation failure and not a ledger that has fallen behind. No message changes: 098 already made every report line correct.
The coupling gate can see the change being committed
`couple --base X --head HEAD` builds a `git diff X...HEAD`, so every path in the working tree and the index is invisible to it. The documented pre-commit coupling check therefore cannot see the change it is being run to judge: on a branch whose work is not yet committed it reports `0 path(s) checked, no drift` and exits 0, which reads as a pass. Spec 094 §4 placed the defect out of scope and said it "is filed separately"; spec 073 §4 placed it out of scope and pointed back at 090. Nothing filed it, and the phrase in 094 §4 has been untrue since it was written. This spec files it: `--include-uncommitted` unions the committed range with `git diff HEAD`, so the gate judges what a commit would contain. The library stays pure and still shells out to nothing; the CLI does the git work, as it already does for the range.
An amended acceptance is the one that runs
Spec 074's `## Verification` line 13 asserts `d["next"]["id"]` on `registry plan --next`, which is a claim about the corpus rather than about the code: it holds only while something is ready to build. The corpus finished, `next` became `null`, and `spec-spine verify 093` has been red ever since, failing an assertion that contradicts 074 §3.3's own requirement that an absent answer be a present `null` member. The line is wrong and 093 is approved, so correcting it is an amendment. Spec 037 forbids editing the amended file and `verify` executes that file, so until now an amendment could not reach an acceptance block at all. This spec gives it a route: a spec that `amends` another may declare that its own `## Verification` block replaces the amended spec's, and `verify` runs the replacement and says whose it ran.
An amendment carries the acceptance it replaces
Spec 080 amended spec 079 so that an unresolved claim exits 1 rather than 2, and migrated the four assertions that live in Rust. Spec 079's own `## Verification` block was left as it was ratified, and five of its assertions still require the pre-amendment code, so `spec-spine verify 098` has been red since 101 merged. A sixth line is red for an unrelated reason: 079's AC-9 compares two `settings.json` files against a fixed commit to prove 098 did not edit them, which specs 093 and 104 then legitimately did. Spec 082 built the route an amendment needs to reach an acceptance block and landed after 101, naming the audit of the remaining blocks as its own work. This is that work for 098: this spec declares 079's acceptance replaced and carries the corrected block, without editing 079's file.
An acceptance outlives the output it was written against
Spec 044's `## Verification` block has three assertions that no longer hold, and none of the three describes a defect. It compares the whole of `index check`'s stdout against one line, which spec 050 legitimately added a second line to; it takes `len()` of `index diagnostics --json` as a bare array, which spec 074 legitimately wrapped in an `items` envelope; and it pins `VERDICT_SCHEMA_VERSION` at the literal `0.2.0`, which spec 071 legitimately moved to `0.4.0`. The third is the sharpest: 044 3.6 rules that the envelope version MUST NOT move for a payload addition, and a literal pin cannot assert that, because it also fails for every move the rule permits. None of the three amending specs declared `amends_verification`, and all three predate the spec that built it. This spec declares 044's acceptance replaced and carries the corrected block, without editing 044's file.
A version pin is not a contract
Spec 049's `## Verification` block pins the verdict envelope at the literal `0.3.0`, which spec 071 legitimately moved to `0.4.0`, so `spec-spine verify 056` has been red since 088 merged. The pin was never what 056 required: 056 3.4 states that `--spec` emits the spec 034 envelope with a `verb` token distinguishing it from `compile --check`, and records the bump it performed as a fact about that bump, not as a standing ceiling. A literal pin cannot assert either half, because it goes red for every move the rule permits and stays green for a verb token that collides. This spec declares 049's acceptance replaced and carries the corrected block, without editing 049's file. It is the first of the four follow-ons spec 084 4 named.
An exact key set refuses what the rule allows
Spec 052's `## Verification` block asserts that `index orphans --json` has exactly the keys `orphaned` and `inFlight`, and spec 074 legitimately added a `schemaVersion` member to every governed read, so `spec-spine verify 059` has been red since 093 merged. 052 3.1 requires the two named arrays to be present and always emitted; it never required them to be the only members, and an equality over the key set asserts a closed document the rule does not state. The same block is silent about the two decisions 059 actually recorded for that verb: that `--json` emits both arrays even when empty, and that the prose form stays silent on a corpus with nothing to report. This spec declares 052's acceptance replaced and carries the corrected block, without editing 052's file. It is the second of the four follow-ons spec 084 4 named.
The answer is a member, not the document
Spec 053's `## Verification` block compares the whole of `registry plan --next --json` against one spec object, and spec 074 moved that object under a `next` member so the empty ready set could be a value rather than a missing document, so `spec-spine verify 060` has been red since 093 merged. 093 declared an `amends` edge to 060 for exactly this change and carried the replacement text for 053's rule; what it could not reach was 053's acceptance, because spec 037 forbids editing the amended file and the block lives inside it. The same block leaves 053 3.2's strongest requirement unasserted: that `--next` is a projection of `plan` and does not reimplement selection, which a one-element fixture cannot show. This spec declares 053's acceptance replaced and carries the corrected block, without editing 053's file. It is the third of the four follow-ons spec 084 4 named.
The template teaches the whole grammar
`standards/spec/templates/spec-template.md` is the file `/spec` copies to start every spec, and it is the only place in the corpus where the frontmatter grammar is written down for an author. It has fallen behind the parser. Spec 082 added `amends_verification` and the template never gained it, a gap five specs have now recorded in their own section 4 with nobody able to close it, because no spec claims the file. Measured against `KNOWN_KEYS`, four more keys are missing for the same reason, and the `extends` example omits `nature`, which 96 of the 111 specs in this corpus write. This spec claims the template, completes it, and gives it an acceptance that reads the parser's own key list, so the next key added to the grammar cannot reach `main` without the document that teaches it.
Nothing reruns a merged acceptance
A `## Verification` block is run once, by the session that writes it, and then never again. `verify` is outside the gate chain on purpose, so a block that a later approved spec legitimately invalidates goes red silently and stays red: specs 084 through 110 each exist to repair one such block, and each was found by hand. Spec 083 §4 named the remedy, "a periodic sweep a maintainer runs, not a gate step", and left naming its home as its own work. This spec is that home. It establishes `scripts/verify-sweep.sh`, a maintainer-invoked sweep that runs the whole corpus's declared acceptance against one already-merged revision in an isolated worktree, accounts for every spec under five outcomes of which only two are success, and closes the 000-047 population as a ledger that can only shrink and that no future spec can enter.
The verdict is the only thing on stdout
Spec 034 3.1 requires that under `--json` a verb write exactly one JSON object to stdout, and `verify --json` does not: it spawns each acceptance command with stdout inherited, so every byte the command prints lands on the parent's stdout ahead of the envelope. The failure is quiet in the worst way, because the envelope is still emitted and still correct and the exit code is still right, so a consumer sees a successful run whose document will not parse. Measured on `5f95613`, a one-command block printing a single line makes `json.load` of the whole stdout fail at character 0 while the process exits 0. `verify` is the verb an orchestrator most needs to read programmatically, which is the third argument spec 043 1.2 used to justify building it, so the one verb that had to be machine-readable is the one that is not. The correction keeps the child's bytes and moves them: under `--json` both child streams are streamed to the parent's stderr as they arrive, and the command and exit transcript spec 043 3.5 requires joins them there, so stdout carries the envelope and nothing else on the report path and on the error path alike. Nothing is captured in memory, no field joins the verdict schema, and without the flag every byte goes where it goes today.
An unclassified review failure blocks the merge
The AI review job classifies a failed reviewer invocation into two outcomes, and its fall-through is a pass. On 2026-09-20 the provider returned "Your organization does not have access to Claude. Please login again or contact your administrator." and the job matched it against a token allowlist (`authentication_error`, `permission_error`, `unauthorized`, `forbidden`, and three more), found no match, set `review_status=api_failure`, exited 0, and `ci-gate` went green on PR #267 with no review. The refusal was prose and the allowlist reads tokens, so an access refusal took the transient-outage branch. Two further holes sit beside it: `review_status=ok` is set from exit status alone, so a reviewer that exits 0 having written nothing publishes an empty review the gate reads as reviewed, and the transient notice swallows a failed `gh pr comment` with a warning, so the one artifact that makes a skip visible can be absent while the gate still passes. This spec states the classification policy the job must implement: success and failure are separated by exit status before any text is read, refusal signals outrank transient signals inside a failed invocation, transient signals are recognized only in the contextual forms their emitting layer produces, and everything else blocks. It is defended by a test that executes the workflow's own script rather than a copy of its patterns, so inverting a blocking branch is caught even when its regexes are untouched.
The engine ships governance, not an environment
spec-spine has been shipping two products. One is a governance engine: a compiler, an indexer, a classifier, a coupling gate, a verifier. The other is a development environment: twenty-nine files of skills, agents, rules, hooks, a Makefile and a CI workflow, embedded into the binary as a 2,707-line generated module and written out by a public `init --with-kit`, plus two further generated projections of the same instructions for other agent runtimes. The second product is now Statecraft's: the Statecraft CLI is the sole distributor and initializer of the managed development environment, and it places the compiled governance artifacts at `.statecraft/derived/`. This spec removes the public initialization command, the kit, the embedded kit, the agent-tree projections and the parity machinery that held them equal; narrows the retained library scaffold to governance starter content and nothing else; relocates this repository's own committed derived trees; and records, per predecessor, what the removal does to the claims and the acceptance those specs left behind.
The harness this repository runs
Sixteen specs governed one tree. `.claude/` is four hooks, ten skills, four agents and four rules, and the requirements on them were spread over specs 093, 047, 048, 051, 063, 068, 071, 072, 078, 080, 081, 082, 099, 104, 110 and 116, each written when that tree was a product this repository distributed. Spec 092 removed the distribution; what is left is one tree that one repository reads, described in sixteen places, most of them half about copies that no longer exist. This spec is that description, in one place: what the hooks may do, what the push gate refuses, how every hook reads a verdict, what the ten skills are and what each must contain, what the four rules say, and who owns the protocol. Every surviving requirement of the seventeen is carried here; what is dropped is named in §6 with the reason, and in every case the reason is that its subject was a copy spec 092 deleted.
One gate, and the boundaries it holds
The governed loop has one definition and three boundaries where it is enforced: the commit, the pull request, and the merge. Five specs described that, each written against a copy the repository distributed. 020 put the merge driver in `.githooks/`; 064 wrote the composite gate as `kit/Makefile` and the workflow as `kit/govern.yml`; 089 fixed the guard that conflated a skip with a failure; 090 added the commit-boundary hook; 114 gave the target its controls and made the workflow call it instead of restating it. Spec 092 removed the copies and moved the gate to the repository root. This spec is the gate, in one place: the target and its controls, the two CI legs that call it, the commit-boundary hook that refuses without repairing, and the merge driver that resolves a same-shard conflict by regenerating rather than by leaving markers.
The corpus describes what exists
Spec 092 removed the kit and obeyed every standing rule while doing it: it withdrew claims, declared amendments, replaced acceptance, and edited no predecessor's prose. The result is a corpus where 102 unit claims were withdrawn from 29 specs, 29 specs no longer run their own acceptance because one block stands for all of them, two blocks pass only because the files they assert about are gone, and 41 documents describe artifacts that no longer exist while reading as live instruction. That is the cost of the rules, paid once, and the owner authorized a one-time exception to settle it. This spec is the record of that exception: what was deleted, what was merged into what, how the ordinals were made contiguous again, and what the rules go back to being the moment it is done.
Compaction is a verb, not a session
Spec 095 removed 27 specs and renumbered 93, which meant rewriting about six thousand references by hand. Seven distinct defects were introduced doing it, every one a rewrite rule that matched more or less than its author meant, and every one silent: the corpus compiled, the gate passed, and the citations pointed at the wrong documents. Four were caught only because spec 089 built a sweep that runs every acceptance block, and the fifth was caught by the sweep finding a sixth. A corpus whose ids are the tool's own vocabulary should not need a session to renumber itself. This spec adds `spec-spine compact`: a plan in, a rewritten corpus out as data, idempotent, refusing every ambiguity the five defects came from, and reporting what it changed rather than leaving a six-thousand-line diff to read.
A path leaves the corpus the way a spec does
Spec 096 mechanizes one retirement: a spec id leaving the corpus. On 2026-09-20 a different one was done by hand, `.claude/rules/` leaving this repository for a section of `AGENTS.md`, and it touched 81 references in 46 files, three frontmatter units, one hashed-input glob, one hook path pattern, six acceptance lines, three Rust tests and 122 derived shards. None of that is reachable by `compact`: its three reference forms are all spelled `NNN-slug`, and a path is spelled six other ways. This spec adds the second half of the same verb, a `retire` plan naming paths rather than specs, with the reference grammar a path actually has and, more importantly, the exclusions: eleven of those 81 occurrences had to survive, because a sentence recording that a path USED to exist is not a citation of it.
A citation the renumber could not see
Spec 096 made compaction a verb and recorded seven defects the hand renumber produced. Two of them, the wrapped citation and the multi-ordinal citation, were measured after 096 was filed and were never repaired: `compact` refuses a plan naming a spec the corpus no longer has, so it prevents the next renumber and not the last one. Re-measuring on 2026-09-21 against the collapse commit found the debt is not 41 occurrences but 638, in three further classes nobody had looked for: 90 spec documents are still titled with another spec's ordinal, and 474 citations are written as a bare ordinal (`084 §3.1`, `050's`) which none of 096's three forms matches. This spec repairs all 638, adds the two forms and the file-selection fix that would have prevented them, and makes a title heading self-checking with a new lint code so this class cannot come back silently.
A merged acceptance is asked again
`verify` is outside the gate chain because it executes what a spec declares and a pull request is a stranger's code. Spec 089 built the sweep that re-asks the corpus's acceptance and made it maintainer-invoked, which leaves the window it was built to close open between runs: spec 092's block was red on `main` from #277 until #278 with every required check green, and no light turned. This spec runs the sweep where the trust boundary already allows it, on the default branch after a merge and nightly, scoped after a merge to the specs that own a changed path. It amends spec 089's "never CI" to "never on untrusted input", which is the rule that clause was written to hold.
A deleted path is judged where it lived
The coupling gate resolves a deleted path's owners from the head index, where the claim the same change withdrew no longer exists, so a correct removal is refused as C-001 against a package floor that has nothing to say about it. A deleted path is judged against the snapshot that precedes the segment its deletion belongs to, reconstructed from that snapshot's own source bytes, and a snapshot the gate cannot reconstruct is an explicit non-success rather than a head-only pass.
Readiness is scheduling, not approval
`registry plan` partitions by `implementation` and `depends_on` and never by `status`, so a draft appears on the ready set by design. The layering is deliberate and stays; what is missing is a governed sentence saying so, which a consumer reading the document directly has no way to infer.
A ready spec carries its status
`ReadySpec` carries `id` and `title`, so a consumer reading `registry plan` directly cannot apply an approval rule without a second query per entry. An additive `status` field is a read-schema MINOR that lets a direct consumer see what `/next` sees. An enhancement with a named cost, not a defect fix.
A verifier fixture is a published artifact
The tamper and cross-version cases an independent verifier must reproduce exist only as commands inside spec 068's acceptance block and as a measured table in docs/authority-evidence.md. A verifier outside this repository cannot run either. This emits them as a named, versioned fixture set: the bytes, the corpus each payload is about, and the outcome each case must reach, all measured against the shipped verifier rather than asserted.
A producer is tested as published
The published spec-spine-core 0.21.0 emits AGENTS.md and a `.claude/rules/` tree; the current source under the same version string emits neither. Both states passed a green workspace suite, because "the workspace is green" and "the artifact a consumer installs behaves this way" are different claims and only the first was ever tested. The producer contract is asserted against the packaged crate, through its public facade, from outside the workspace, and that check gates a release.
Governed scope is enabled here
Spec 078 added `[coverage] governed_scope` so a repository can bring its governance files under the ownership ratchet, and the repository that wrote it never turned it on. The measured cost is four unclaimed paths. They are claimed, the scope is declared over exactly the files this repository already folds into its content hash, and what stays exempt is stated rather than implied.
Obligations are declared constraints
A spec's normative content is prose, so nothing downstream can name a single requirement, cite it, or tell whether the sentence it cited has changed. This adds an optional `obligations` frontmatter key: three kinds, ids stable within a spec, an anchor validated against the spec's own headings, explicit inputs for a verification, and withdrawal in place rather than deletion. The registry gains a digest for every section of every spec, beside the spec's full content hash, and a read resolves a qualified `<spec-id>#<obligation-id>` reference. No gate reads any of it.
A context closure is declared
A piece of work has no declared answer to "what must I have read before I act on this", and no way to tell later whether that context changed. A ContextClosure is that answer as data: the specs, sections and obligations a task is answerable to, named by the task's owner. spec-spine resolves a closure request against the committed ledger and returns every member with its identity and one order-independent digest over all of them, so "the context this work was done against has changed" is one comparison. The closure lives in the consumer's record; spec-spine only resolves it, and no gate reads one.
A work scope is declared
Spec 072 reports that two ready specs claim overlapping territory, at spec granularity. What was left of WorkScope is the path-level half: one piece of work declares which paths it expects to change alone, which it expects to change alongside named other specs, and which it only reads. A scope is a document the consumer holds, like a context closure (spec 107). spec-spine evaluates it against the committed ownership index, reporting undeclared crossings, unowned paths and wrong sharing declarations, and compares two scopes for conflicting intentions. Both answers are reads. Nothing here locks, reserves, excludes or permits anything, and no gate reads a scope.
Impact and conflict are declared
A spec can say which specs it extends and amends. It cannot say which requirements it is expected to affect, or which requirements it knowingly conflicts with. Both are declared in frontmatter against spec 106's qualified obligation ids, validated at compile, carried in the registry, and answered by one read that inverts the declarations so the affected side can see them. Nothing gates on either, and nothing computes an impact.
An interface reference is digest-pinned
A spec can cite another repository's spec only in prose, so the citation cannot go stale visibly. This declares a cross-corpus interface reference pinned to the cited spec's content hash, and optionally to the digests of the sections it relies on, and adds one verifier that recomputes those digests from a corpus the caller supplies locally. A moved interface becomes a named, exit-coded fact. Discovery, fetching and trust in the exporter stay with the caller; nothing is ever pinned from what was first observed.
A move is a reviewed mapping
The gate passes `--no-renames`, so a move arrives as a deletion and an addition and each half is judged where it is true. That is correct and it is not the whole story: nothing records that the two halves are one act. The spec performing a move declares it in its own frontmatter as authored, reviewed data. The compiler validates the declaration's shape, the lint checks its paths against the tree, and a lookup derived from every declaration follows declared chains, reporting ambiguity and cycles instead of guessing through them. A move is informational: it transfers no ownership, authorizes no deletion, infers no similarity and changes no verdict.
Typed overlays ride the existing seam
Four proposals (effect contracts, budgets, dependency rationale, contract adapters) all want to attach typed data to a spec's units without changing what a unit is. The corpus already has the seam: declared extra frontmatter (spec 012), carried into the registry as `extraFrontmatter`. This states the contract an overlay keeps on that seam, pins what the engine does and does not promise about it (canonical values, not original bytes; a hash scope that is the declaring spec's, not every shard), and asserts it with several independent overlays in one corpus. No new unit kind, no engine enforcement, and no domain schemas.
A waiver has a declared lifecycle
A `Spec-Drift-Waiver:` line clears every refusal in the pull request it appears in, with no scope, no expiry and no record of what it excused. A waiver may now declare a path scope, an expiry, an ancestry and a use limit on lines of its own. The gate evaluates each declared check over inputs the caller supplies (an as-of date, an ancestry answer, a prior use count) and reports it as satisfied, failed or not evaluated, never guessing a missing input. A failed check makes the waiver clear nothing; a scoped waiver clears only its paths; and the verdict says which waiver cleared which violation. Nothing is counted, consumed, stored or authorized by the engine, and an undeclared waiver behaves exactly as before.
Authoring adapters and intent are separated
The feature register carried A10 (authoring-tool adapters) and B23 (an intent declaration) as one item. They belong to different owners: A10 is environment delivery, which is Statecraft's since spec 092, and B23 is a corpus-side declaration, which is spec-spine's. This separates them, records what an adapter author can build against, and specifies the smallest optional structured intent a spec can carry: a standing goal and its non-goals, validated for shape, transported into the registry, and read by no gate. The prose stays authoritative; attempt-specific planning stays with the consumer.
Bindings are designed, not shipped
`docs/bindings-plan.md` designs napi, pyo3 and cgo bindings and says no binding code belongs in this repository. That mandate lived only in a document header. This records it in the corpus as a policy, and states the obligations the JSON facade keeps so that a binding remains buildable elsewhere, each as it measurably holds today. It builds nothing, endorses no external binding, and is reviewable rather than enforced. Its implementation is `n-a`: there is no binding to implement, and this spec is the decision not to.
A deferred contract claims nothing
`L-001` warns when a spec declares no ownership edge, and exempts a superseded, retired or retroactive one because each is making a correct statement by claiming nothing. A spec deliberately not scheduled (`implementation: deferred`) is in the same position and is not exempt, so the one shape the corpus contract leaves a deferred contract, no territory, makes `lint --fail-on-warn` red. The exemption is extended to `deferred`, covers the declaration and never a claim, and re-arms the moment the spec is scheduled.
The gate resolves the binary it documents
The root Makefile's header documents a binary-resolution order preferring the binary this checkout builds, and its assignment implements none of it: `SPEC_SPINE ?= spec-spine` is the PATH fallback and nothing else, so `make gate` on a machine with an installed CLI governs with whatever that CLI is. Measured here at three releases behind the checkout. The assignment is corrected to the order the header states, an unusable explicit override is refused rather than silently replaced, and the resolved binary and its version are announced before the chain runs.
The renumber is history, not a standing rule
Spec 095's acceptance asserts that the live corpus's ordinals are contiguous, `o == list(range(len(o)))`. Its requirement (3.3) is narrower: the one-time renumber left the survivors contiguous. Its own D-2 says reserved gaps are deliberate. Merging spec 117 while 105 to 116 stay reserved made the block red with no defect in the corpus. The repository owner ruled that the renumber is a historical transformation and not a perpetual contiguity rule (docs/release-candidate-0.22.0.md 10.1). This spec carries 095's block in full through `amends_verification`. The historical contiguity is read from the map that records it, and the live corpus is held to identity rules that survive growth, with fixture negatives showing each can still refuse. 095's requirements and block are not edited; its file gains only the superseded- acceptance note spec 082 3.4 requires.
A release verdict judges what was built
The whole-corpus sweep (spec 089) runs every declared acceptance block and exits 1 when any fails. This repository files a spec's acceptance before the spec is built and writes it to fail first, so any filed-but-unbuilt draft keeps the sweep red. "Sweep green" as a release pre-flight is then unreachable for as long as the backlog is honest. The sweep had no lifecycle predicate, and the only ways to get a green run were to build unrequested features or to hide the failures. This spec adds a release verdict next to the corpus verdict. It is judged over the specs whose implementation is built, with pending work run, listed with its real outcome and never counted as success or exemption, and `--release` puts it in the exit code. The corpus verdict and its exit code are unchanged. It also moves the default run directory out of the macOS temporary tree, whose daily purge is the inferred cause of a run in which 26 blocks failed that had not, and makes that directory new per run, so a rerun cannot clear the evidence of the run before it.
A scoped sweep judges the binary the blocks name
The `Acceptance` workflow gives the sweep a binary built outside the sweep's worktree through `SPEC_SPINE_BIN`. Most acceptance blocks call `./target/release/spec-spine` relative to the worktree, and several do not build it first. In a whole-corpus run an earlier block's `cargo build --release` creates the file, so they pass by ordering. The push leg sweeps only the specs a merge touched, and when none of those builds, every block fails at exit 127, `not found`. That is how the ratification merge at `da47632b` and the merge at `ff79c68a` both went red with no acceptance failing. The workflow now lets the sweep build the binary from the revision inside its worktree, spec 089 3.8's default, so a block judges the binary it names.
The Acceptance workflow judges what was built
The nightly `Acceptance` leg exits on the sweep's corpus verdict, so it is red for as long as any draft is filed ahead of its build, which is this repository's normal state. A signal that is always red cannot tell a reader that an implemented spec's acceptance just broke. Spec 119 built the release verdict and left the workflow's choice open. This spec takes it: both legs exit on the release verdict, the full selected corpus still runs, and a new report step puts both verdicts, every release failure and every pending block's real outcome into the run's summary and annotations. A missing, unreadable or inconsistent report fails the job on its own.
A commit refuses an unresolved merge or an unformatted Rust change
The commit-boundary hook (spec 094) refuses a stale or invalid derived tree and nothing else, so a merge whose conflict was "resolved" by staging the markers committed cleanly, and so did a Rust change whose formatting check had failed earlier in the same command chain. Both happened on this repository's expansion branches. The hook now also refuses unmerged index entries, conflict markers on the lines a commit adds (git's own detector, with git's own attribute as the only opt-out), and a staged Rust change that fails `cargo fmt --all --check`. It still writes and stages nothing.
A startup verdict names its reader
Three session starts on 2026-09-23 reported "spec registry: INVALID; codebase index: STALE" on a corpus that was valid and fresh. The reader was the repository's own `target/release/spec-spine`, built from an earlier revision and left in place while the checkout fast-forwarded past the frontmatter grammar it knows, so an incompatible reader's refusal was printed as a finding about the corpus. The hooks that turn `check` into a message now name the executable they selected, and when that executable is the in-tree build and is older than the source it is built from, they say the verdict is an earlier revision's reading and keep what it said. They still read only: nothing builds, regenerates or installs.
The expansion line carries its own version
The frozen 0.22.0 candidate (`f9fa6a8f`) and `main` both answered `spec-spine 0.22.0` while emitting different registry, read and verdict schemas, so the version string could not say which engine a verdict came from, and an older reader judged a newer corpus invalid instead of refusing it. `main` carries the version the next release will be cut at (0.23.0 when filed, 0.24.0 since D-3, 0.25.0 since D-4, 0.26.0 since D-5, 0.27.0 since D-6, 0.28.0 since D-7), in all three package manifests; this repository's version floor moves with it, so a reader older than the corpus's grammar refuses by name (exit 3); the release runbook makes both a standing step; and a script records a binary's identity from evidence (path, digest, revision, whether it is that checkout's current build, and the schema axes it emits) rather than from `--version`. The frozen candidate is not recut.
A forwarded line arrives whole
`verify` forwards a command's stdout and stderr to its own stderr from two threads, one write per read. A read ends wherever the pipe's contents did, which is mid-line whenever the command writes faster than the drain, so a line of one stream could be spliced by a chunk of the other. Spec 090's own flood test counts intact lines and caught it intermittently: the post-merge acceptance sweep at `0bf9ff78` failed twice, in two different specs' runs of the same suite, with 8191 of 8192 lines intact. Each drain now delivers whole lines only, holding a partial line until its newline arrives or a bound is reached. Order between the streams is still not promised.
A derived file stays in its directory
`compile` and `index` name each per-spec shard `<id>.json` after the spec's frontmatter `id`, and `attest --spec` names its attestation the same way. None of the three checked that the id was a file name before joining it to a directory. A corpus declaring `id: "../../../package"` made `compile` overwrite the repository's `package.json` with shard JSON, an absolute id wrote wherever it pointed, and `index` and `attest --spec` did the same while exiting 0. `V-012` reported the id, but after the write rather than instead of it. These are verbs documented as safe to run against a stranger's branch. Every file name derived from a spec id is now checked before anything is written, pruned or created: a name that is not one plain file name refuses the whole write with exit 3 and leaves the tree as it was.
A derived file is where its path says
Spec 126 made every file name derived from a spec id one plain name, so an id can no longer walk a write out of its directory. Two ways past that remained. A symbolic link committed inside the derived tree (git records it like any file) made `compile`, `index` and `attest` write through it and prune `*.json` files wherever it pointed, all with exit 0. And a name that passes 126's lexical rule can still name a Windows device (`CON.json`, `nul.tar.json`), which is not a file at all there. Every path below the repository root that these verbs write, create, remove or prune is now checked for a link before anything moves, a device name is refused on every platform, and a refusal leaves the tree exactly as it was. A repository reached through a link still works.
A derived tree stays in its repository
`[layout] derived_dir` is read from the committed `spec-spine.toml`, and nothing checked where it pointed. `derived_dir = "../outside"`, an absolute path, or `x/../../outside` made `compile`, `index` and `attest` write their shards outside the repository and prune `*.json` files there, all with exit 0, on `main` and on spec 127's build. Spec 127 checks every component below the repository root for links, and stops where a path leaves the root (its D-4), so it did not reach this. A `derived_dir` that is absolute, carries a `..` segment, or uses a Windows path form is now refused when the configuration is loaded, before any verb reads or writes, and the checked writer refuses any path that is not wholly below its root. A repository reached through a link still works.
A configuration passed as JSON obeys the loader's rules
`load_config` refuses a `derived_dir` that leaves the repository (128), a `state_dir` that is the repository root or overlaps a governed root (036), and a malformed `[index.slices]` entry (011). The JSON facade deserialized its configuration with serde alone and ran none of those rules, at all 22 entries that take one, so a binding could hand the engine a configuration no `spec-spine.toml` can hold. `scaffold_init_json`, the entry Statecraft links, returned `Ok` with a `spec-spine.toml` that every verb then refuses with exit 3, and it wrote each value between literal quotes, so a `"` broke the file and a `\` changed the value. The rules now live in one public function, `validate_config`, which the loader and every facade entry call, and the scaffold refuses what the loader refuses and escapes what it writes. Checks that need the filesystem or the running binary stay where they are.
A planned claim ends at completion
Spec 063's `planned: true` lets a spec claim territory it has not written, so a corpus gating on `--fail-on-unresolved` can ratify before it builds. 063 bound the flag to completion only through the lint (`L-011`): a spec at `implementation: complete` whose planned unit is absent passed `check`, `index check` and both with `--fail-on-unresolved`, exit 0, because the index dropped the diagnostic for a planned unit at every lifecycle. A consumer whose hooks run `check` alone never saw the contradiction. The flag now holds a claim open only until the spec says `complete`; after that the absent unit classifies like any settled claim, the natural `I-0xx` error, and `check` exits 1. `index coverage` also stops listing a planned unit that has resolved as "not yet written". Nothing changes for an unmarked claim or for a spec whose work is not complete.
A scaffold can pin its producer
Statecraft writes the `spec-spine.toml` that `scaffold_init_json` returns byte for byte, and that file pins nothing: the `[meta]` header and the key are both commented out (055 §3.4), and the example is a caret range. A repository Statecraft initializes is therefore judged by whatever binary a hook finds. This spec adds an opt-in scaffold option, `pinExactVersion`, that emits an active `[meta]` table with `required_version = "=<this producer's version>"`, through a new facade entry `scaffold_init_opts_json(config_json, options_json)` and its Rust form `scaffold_init_opts`. Without the option the output is byte-identical to `scaffold_init_json`'s, measured against 0.25.0 for Statecraft's configuration and the default. The configuration is validated exactly as 129 requires, and an unknown option is refused rather than silently producing an unpinned file.
One exit contract for the family
spec-spine and the Statecraft CLI answered in two exit vocabularies. spec-spine spent 2 on staleness and 3 on everything that went wrong, from a malformed `spec-spine.toml` to a failed read to a refused write, so a consumer could not tell "you asked for something this tool will not do" from "the disk failed", and a containment refusal was reported as I/O. This spec adopts the family contract the two tools share: 0 ok, 1 finding (validation, drift, stale, unresolved, not found, authored content that does not parse), 2 refused (an invalid configuration, a version pin not met, a containment refusal: nothing done), 3 usage, 4 failed (I/O, internal, a tool-produced artifact that fails its schema). `Error` gains `Refused`, `Usage` and `Internal`, and every call site is classified. The `--json` envelope becomes the family envelope, version 1.0.0: `schemaVersion`, `tool`, `verb`, `outcome`, `exitCode`, `summary`, then `report` or `error`, with `ok` removed and `error.kind` a closed set of ten tokens. It is a deliberate breaking change, accepted by the owner, taken once so the consumer migrates once.
Owner delegation is declared
What an agent may decide in this repository without asking was spread across the owner's session instructions and never written where every agent reads. `AGENTS.md` gains an "Owner delegation" section that states it: agents decide, record and report reversible choices inside an adopted direction, relocation-only changes, test and evidence design, repository hygiene, and unambiguous corrections of internal inconsistencies; ratification, waivers, publication, spending, trust roots and signing, changes to the gate, check suite or acceptance authority, anything visible outside the repository or affecting another adopter, and deleting anything remote stay with the owner. Every handoff ends with one decision table. The section widens no authority the standing rules withhold.
The suite runs on Windows
spec-spine ships a Windows binary and proves its shard trees byte-identical on Windows, but CI only ever built there: the workspace test suite ran on Linux alone, so a Windows behavior difference could ship with every check green. A `test (windows)` job now runs `cargo test --workspace` on `windows-latest` as a check of its own, required through `ci-gate`. A test that cannot run on Windows is gated or quarantined in its source with a tracked finding, never skipped by the workflow, and Windows results are reported apart from build results.
The toolchain floor and the dependencies are checked
The workspace declares `rust-version = "1.85"` and nothing ever built it there; the code uses let-chains, stable only since 1.88, so the declaration was false. Dependencies were never checked for advisories, licenses or sources, and the dev tree carried two open RustSec advisories through `jsonschema`'s HTTP resolver, which no test uses. CI now builds and tests at the declared `rust-version`, read from `Cargo.toml`, and runs `cargo deny check` against a committed `deny.toml`; both are required through `ci-gate`. `jsonschema` drops its default features, which removes the whole HTTP and TLS dev tree and both advisories, and the declared `rust-version` is corrected to the measured floor. `CONTRIBUTING.md` states the loop.
The legacy ledger is paid: registry and grammar
10 specs filed before `verify` existed (001, 002, 009, 012, 013, 014, 015, 017, 018, 026) sit on the legacy ledger in `scripts/verify-sweep.sh` with no executable acceptance, although the tests that hold their behavior exist and pass. This spec carries that acceptance for them under `amends_verification`, the route spec 132 used, and deletes their ledger lines in the same change. Every command names its tests exactly and asserts the exact pass count, so a renamed or deleted test turns the block red instead of passing on an empty filter. The area is the registry compiler, its query surface, and the frontmatter and edge grammar.
The legacy ledger is paid: index, lint and resolution
10 specs filed before `verify` existed (003, 004, 010, 011, 016, 020, 022, 023, 024, 025) sit on the legacy ledger in `scripts/verify-sweep.sh` with no executable acceptance, although the tests that hold their behavior exist and pass. This spec carries that acceptance for them under `amends_verification`, the route spec 132 used, and deletes their ledger lines in the same change. Every command names its tests exactly and asserts the exact pass count, so a renamed or deleted test turns the block red instead of passing on an empty filter. The area is the codebase index, lint, section and symbol resolution, and sharding.
The legacy ledger is paid: coupling, coverage and freshness
10 specs filed before `verify` existed (005, 008, 027, 028, 029, 030, 031, 033, 035, 036) sit on the legacy ledger in `scripts/verify-sweep.sh` with no executable acceptance, although the tests that hold their behavior exist and pass. This spec carries that acceptance for them under `amends_verification`, the route spec 132 used, and deletes their ledger lines in the same change. Every command names its tests exactly and asserts the exact pass count, so a renamed or deleted test turns the block red instead of passing on an empty filter. The area is the coupling gate, ownership coverage, freshness and scheduling.
The legacy ledger is paid: verdicts, attestation and lifecycle
9 specs filed before `verify` existed (007, 021, 032, 034, 038, 039, 040, 041, 042) sit on the legacy ledger in `scripts/verify-sweep.sh` with no executable acceptance, although the tests that hold their behavior exist and pass. This spec carries that acceptance for them under `amends_verification`, the route spec 132 used, and deletes their ledger lines in the same change. Every command names its tests exactly and asserts the exact pass count, so a renamed or deleted test turns the block red instead of passing on an empty filter. The area is verdicts, attestation, lifecycle leniency, governance documents and the PyPI shim.
A prose-only example stays prose-only
Spec 043's acceptance, and one of its tests, use spec 041 as the corpus's standing example of a spec whose acceptance is prose only: `verify 041` must report `not-declared`. Spec 139 carries 041's acceptance, which makes that example false while the rule it illustrates still holds. This spec carries 043's block unchanged except that the example is 037, which stays prose-only by design (139 3.3), and moves the test's example the same way.
A governance edit rewrites one file
Every index shard's `shardHash` folds one global scalar over `spec-spine.toml` and every `[index] extra_hashed_inputs` file, so a one-line edit to any root document restamps every shard. Measured on this repository at `153d35df`: an `AGENTS.md` edit rewrites 139 shards, and two pull requests editing two different root documents conflict on all 139. An adopter's merge queue pays that on every such PR. This spec moves the global inputs out of the shard hashes into one committed sidecar, `codebase-index/inputs.json`, with one entry per input file, compared byte for byte like a shard. A governance edit then rewrites that one file, and two edits to different documents change lines far enough apart to merge. The aggregate index content hash folds the sidecar, so an attestation still covers every authored input.
A relocation is proven
Splitting an approved spec had no honest path. A partial `supersedes` added the new spec as an owner of a unit and left the old one owning it too, so an edit to either spec cleared a change to the code. Moving a section of text out of an approved spec read as a requirement change, whatever the text said, because a section's digest is keyed by its spec's path. `amends_sections` accepted any string, and nothing said whether `amends` grants code ownership. This spec makes a partial `supersedes` an exclusive hand-off of its unit; adds a `relocates` declaration that `delta` proves against the merge base, classing a proven move `relocation` instead of `requirement`; validates `amends_sections` against the amended specs' headings (L-017); warns when two live specs establish one unit (L-018); and states the rule that `amends` changes what a spec requires and never who owns its code.
Line endings do not stale the ledger
Every emitted derived file is canonical JSON with LF endings, and the freshness reads compare the committed bytes with the recompute exactly (spec 086). Git on Windows with `core.autocrlf=true` and no `.gitattributes` rule for the derived tree checks those files out with CRLF, so every shard read as `modified`: a false staleness that regenerating cannot cure, because the next checkout converts again. Spec 134 quarantined three tests for it as WF-7. This spec makes the comparison read a committed file whose only difference is CRLF for LF as the same text, in the registry, the index and the inputs sidecar, keeps every other byte significant, and lifts the quarantine so the Windows test job proves it.
A repository path is one type
Paths the engine joins onto the repository root were checked key by key, or not at all. `derived_dir` refused `/`, `..`, `\` and `:` (spec 128); `specs_dir` and `standards_dir` accepted anything, so a corpus could be read from outside the repository; a compact plan refused a leading slash (WF-8) and let the drive-relative `C:foo` through; no key refused a Windows device-name segment; and a link inside the repository could point any governed read outside it. This spec gives one rule for a repository-relative path, applies it to every layout root and to compact plan paths, and refuses a run whose tree holds a link that resolves outside the repository, exit 2, while a repository reached through a link keeps working.
An unresolved claim is not called stale
Spec 079 taught `check` and `index check` to tell a stale shard from an unresolved claim, because the two have different remedies and regenerating cures only the first. The other verbs that read the committed index (`index coverage`, `index owner`, `couple`, `scope`, and `delta` at the merge base) kept the folded verdict and still said "index is stale" about a claim that resolves to nothing, naming a `blocking-diagnostics` shard and implying the wrong fix. This spec gives them one guard that keeps the two apart: a committed shard whose bytes moved is staleness, as before; an unresolved claim with byte-exact shards is a validation finding that names the claim and says regenerating does not clear it. The exit code stays 1; under `--json` the error kind becomes `validation`, with the claim in `violations`.
Carried acceptance follows 139 and 144
The pre-release sweep of `e440b714` failed two approved specs' acceptance, both invalidated by specs merged this cycle rather than by a defect. Spec 046's block lints this corpus from a scratch root whose `specs` is a link to this repository's `specs/`, a link that resolves outside the scratch root, which spec 144 now refuses (exit 2). Spec 089's block asserts that the built-in legacy ledger reports spec 011 `exempt`, and spec 137 retired 011's line when it gave 011 an executable acceptance. This spec carries both blocks under `amends_verification`, unchanged except that 046's scratch root copies the corpus instead of linking it, and 089 asks the ledger about 019, which stays exempt by design (139 3.3).
A link above the derived root is checked on read
Spec 144's link walk skips every symbolic link at or above the derived and state roots and leaves them to spec 127, whose refusal fires only on write. A read-only verb therefore reads through such a link unchecked: with `.statecraft` a link to a directory outside the repository, `check` reads the committed derived tree and a governed file stored beside it from outside and reports the repository fresh, exit 0. This spec checks those links on read with 144's own rule, while still not descending into either root.
The link rule is tested on Windows
Spec 144's rule that no governed read leaves the repository through a link is tested only on Unix: both link tests in `tests/repo_path.rs` are `#[cfg(unix)]`, and 144 §4 puts Windows links and junctions out of scope. Windows is a shipped platform with a required `test (windows)` job (spec 134), and it has two link kinds 144's walk must see: symbolic links and directory junctions. This spec adds a Windows test for both, and records what a runner without the symbolic-link privilege does.
The installer is tested
Spec 006 is one of the four specs the pre-release sweep still reports `exempt`, because `install.sh`, the tag-gated release pipeline and npm publication "run only in a release" (139 3.3). Two of its three channels do not: `install.sh` is a shell script whose every decision (platform, tag, checksum, attestation policy, install directory) can be exercised offline against a local fixture release, and the npm shim already has an offline unit test and smoke test. This spec adds an offline test of `install.sh`, gives 006 an executable acceptance that runs it and the npm shim's tests, and removes 006's line from the legacy ledger. The release pipeline's publication steps stay unexercised by it, and it says so.
A change runs the acceptance it can break
Specs 144 and 137 each broke an approved spec's acceptance (046 and 089), and nothing noticed until the pre-release sweep of `e440b714`, after both had merged; spec 146 repaired them. The sweep runs only on a merged revision (specs 089, 099), and no pre-merge step runs any acceptance other than the changed spec's own. This spec adds a pre-merge acceptance run: a selector that names every spec whose acceptance a change can break, and a step that runs those blocks on the pull request's head before it merges, with the counts recorded in the pull request. Measured on 136 to 146, a selector keyed on references alone would not have been narrower than the corpus, and would have caught 046 only through its generic `lint` line, so the selector falls back to the whole corpus when engine source changes.
Carried acceptance tests what it names
The 0.27.0 cross-reference of 136 to 146 found three acceptance plans that could pass without testing what their spec now says. 018's plan (carried by 136) never runs 142's amendment of it: its coupling tests build their index by hand, so they pass whether or not the index hands a partially superseded unit over. 144's link-rule line accepts any positive test count, so a link test that stops running still passes. 145's binary line drives only `index coverage`, while six other verbs read the same guard. This spec carries all three plans under `amends_verification`: 018's section gains the hand-off, run through `index` and the shipped binary; 144's line names its three tests; 145's block gains one fixture driven through all seven verbs. Each new line fails under a mutation the carried line it tightens passes.
A refusal says what it is in every form
Three consumer-visible inconsistencies survived 0.27.0, each documented in its handoff with a workaround. `check --json` and `index check --json` still report an unresolved claim as `fresh: false` with a `stale shard` line, where the human output and every other verb say validation, not staleness (spec 145). `index coverage`, `index owner`, `scope evaluate` and `registry show` print nothing on stdout under `--json` when they refuse, so a consumer gets an empty document where the verdict verbs give an error envelope. And a compact plan path refused for a `:` is told to drop `.`, `..` and a leading slash, none of which it has. This spec makes the index half of the JSON freshness report answer drift alone and carry the claims separately, gives every `--json` verb an error envelope on failure, and makes the compact refusal name the rule the path broke.
A test count is exact
Thirty-six live acceptance lines, in seventeen blocks that eighty specs' plans resolve to, accept `test result: ok. [1-9][0-9]* passed`: they pass while any one test in the target runs, so deleting, renaming, ignoring or platform-gating most of a test file still passes. This spec states the rule (no live plan accepts a loose count) and asserts it corpus-wide through `verify <id> --plan`. It holds no block itself: each of the seventeen is tightened by its own spec, in the form spec 151 gave 144, so no single spec becomes the acceptance of eighty. This spec is built last, when its rule holds.
The pre-merge sweep is a skill step, not a gate step
Spec 150 put the pre-merge `verify-sweep.sh --affected-by` run into the `/ship` and `/shepherd` skills, as its 3.2 requires. Spec 089's acceptance, carried for 046 and 089 by spec 146, asserts that no skill mentions `verify-sweep` at all, so 046, 089 and 146 fail from 150's merge on. 150's own sweep did not show it: those three failed earlier in the same block, on the stale 006 ledger entry. This spec carries 146's block under `amends_verification`, unchanged except that the one line asserts what 089 meant and 150 keeps: every mention of the sweep in a skill is the pre-merge `--affected-by` step, and the gate list names no sweep.
Select bounded content from one repository snapshot
Adds a deterministic, read-only selected-content request and response over one explicitly identified repository snapshot. Callers may select a whole spec, spec section, obligation, owned source unit, explicit file, explicit directory member, symbol, module, or deterministically supported test, with bounded projections, stable spans and digests, canonical ordering, continuation, explicit omissions, and no execution or authority effect.
Statecraft Profile 11 governs this repository
Statecraft is the adopted owner of the managed development environment, but this repository still carries its pre-adoption CI workflow, AI review and instruction surface. Profile 11 is adopted at one recorded identity, with its exact spec-spine pin and managed authority files, while the Windows, MSRV, cargo-deny, no-default-features, self-governance and determinism checks particular to this engine remain required behind the one ci-gate status.
The affected-acceptance sweep runs in the merge queue
Spec 150 runs the affected-acceptance sweep in the session that drives a pull request, on its head, and records the counts in the pull request body (D-1). Any engine change selects the whole corpus, so each remediation round costs the session about 45 minutes and a fresh body record, and two pull requests swept at once share one machine. 150 D-1 named the alternative and left it to a later spec: run the sweep in CI on `merge_group`, which sweeps only a change a maintainer has queued. This spec takes it. The sweep becomes a required merge-queue check, sharded across a job matrix so a whole-corpus run is not one sequential job, and the session step and the body record retire. The job belongs to the Statecraft profile, so every governed repository gets it; this repository declares it until the profile carries it.
The affected selection is a governed read
Spec 150's selector, which names every spec whose acceptance a change can break, lives in an embedded Python block of `scripts/verify-sweep.sh`, a script only this repository carries. Spec 157 moves the pre-merge sweep into the merge queue across every Statecraft-governed repository, and the Statecraft owner decided (2026-09-27) that spec-spine owns selection: the profile's sweep job calls a spec-spine read and shards the plan it returns. This spec adds that read, `verify --affected-by <base> --plan --json`: the CLI turns the Git range into changed paths, the core selects as a pure function of the configuration, the corpus and those paths, and the answer names each selected spec, the rule that selected it and its effective plan. The whole-corpus trigger becomes configuration rather than this repository's layout, and `verify-sweep.sh` calls the read instead of keeping a second selector.
Ratification has two paths, both an owner approval
This repository builds a spec while it is still a draft and ratifies it in a second, hand-authored pull request. The owner has chosen ratify-then-build instead, with ratification as a triggerable action gated on an owner-only deployment environment, and a second path that ratifies at merge when a pull request lands a spec at draft and complete. Both paths flip only `status`, bind the approval to the exact bytes shown, and leave ratification owner-only.
A consumer is served answers, not access
Fixes how spec-spine serves a programmatic consumer, the Statecraft CLI first. There are two tiers, split by whose pinned version decides the answer rather than by who is asking: a judging tier (the CLI's JSON envelopes and the family exit contract, answered by the binary the judged repository's own pin admits) and a producer tier (pure library functions whose output the caller owns). No surface is shaped for, gated to, or named after one consumer. A consumer's depth comes from a declared consumer contract instead: the verbs, schema axes, envelope members and exit codes it relies on, asserted by this repository's own suite, plus one public `capabilities` verb that states what a binary supports so no consumer has to probe `--help`.
Exact test counts for 082
Spec 082's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 082's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 084
Spec 084's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 084's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 085
Spec 085's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 085's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 086
Spec 086's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 086's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 102
Spec 102's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 102's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 093
Spec 093's acceptance block carries three lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 093's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 096
Spec 096's acceptance block carries eight lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 096's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 098
Spec 098's acceptance block carries two lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 098's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 137
Spec 137's acceptance block carries three lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 137's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 138
Spec 138's acceptance block carries two lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 138's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 149
Spec 149's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 149's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 151
Spec 151's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 151's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 088
Spec 088's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 088's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 092
Spec 092's acceptance block carries four lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 092's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 097
Spec 097's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 097's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 099
Spec 099's acceptance block carries one line that accepts `test result: ok. [1-9][0-9]* passed`, which passes while any one test in the target runs. This spec holds 099's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
Exact test counts for 156
Spec 156's acceptance block carries four lines that accept `test result: ok. [1-9][0-9]* passed`, which pass while any one test in the target runs. This spec holds 156's block, carries it byte-identical, and replaces each loose line with spec 151's form: the tests the line ran, named with `--exact`, and `N passed; 0 failed`. One of spec 153's per-block tightening specs (153 D-5).
A repository's pin selects the engine that runs
Adds a launcher: a small executable, separate from the engine, that a user or a consumer invokes as `spec-spine`. It finds the repository, reads the engine pin from `spec-spine.toml` with a tolerant reader, resolves an installed engine of exactly that release, verifies its digest, and executes it with the original arguments, working directory, streams and exit status. Engines are stored once per user, content addressed, and a repository may instead declare a local tool directory. A committed `spec-spine.lock`, which no engine reads, records each target's engine digest. Acquisition of a missing engine happens only when the user or CI enabled it, never because a repository asked. A consumer asks `launcher resolve --json` and executes the absolute path it returns. The launcher never judges a repository.
Agent attribution is refused in commits and pull requests
Pull request 417 reached a green ci-gate with four commits whose author and committer were an agent identity, an agent co-author trailer on every commit, and an agent footer, attribution marker and project-thread link in its body. The authored-content checker refused only the em dash and one session-link form, and nothing read commit identity. The checker now refuses agent-session links and agent attribution in every text it already reads, and refuses an agent author or committer on every commit the governance step judges. The project settings stop Claude Code from writing the attribution in the first place.
The carried gate line names the queue sweep
Spec 157 added `affected-acceptance` to `ci-gate`'s `needs:` in `statecraft-ci.yml`, as its section 3.1 requires. Spec 187's carried block, which is the plan of 156, 091, 094, 124, 134 and 135, still asserts the six-job list byte for byte, so all seven plans fail at the same command on `main` since 157 merged, and every queued change that selects them is refused. This spec holds 187's block, carries it byte-identical, and replaces that one line with the seven-job list 157 made true.
Profile 13 keeps the exact engine local
Adopts Statecraft revision 13 without narrowing the engine-specific checks or retiring the repository-owned merge-queue acceptance sweep.
statecraft-cli ↗
Local work environment · 34 shown
Bootstrap spec system
Foundational contract for this repository: authored truth lives only in markdown (+ YAML frontmatter); machine-consumable truth is compiler-emitted JSON only; every artifact is a deterministic function of (config, file contents); a typed authority graph governs who-owns-what. This repository was born governed: the corpus existed before the first line of product code, and no spec here may claim code that does not exist.
The product boundary, the component owners, and the separation of authority
What this product is, what it is not, and who owns each capability it depends on. Fixes the claim vocabulary (specified / implemented / tested / released), the two interaction modes and which one is built first, the rule that separates an authority change from an implementation change, and the reuse disposition for every shared component this repository would otherwise reimplement. Owns the product half of the constitution and the two founding records under docs/. It owns no code.
Project registration and the Statecraft-managed environment: one global home, one project area, one initialization flow, and where authority comes from
Defines repository registration, initialization, the global and per-project environment, ownership classes, manifests, configuration provenance, upgrade, removal, transfer, and setup-profile rendering. Harness delivery, governance-producer adoption, and managed-session evidence are separated into specs 008, 009, and 015.
Work selection, workspace preparation, the run record, and recovery after interruption
The core loop's semantics. How a unit of work is identified by reading spec-spine's structured report rather than re-deriving it; how an isolated workspace is prepared so the operator's checkout is never edited; the append-only hash-linked run record with intent written before an effect and outcome after it; the closed outcome set (completed, failed, refused, interrupted, cancelled) and the rule that a retry appends an attempt rather than replacing history; supervisor-owned refusal accounting a supervised process cannot erase; and reconciliation of an ambiguous effect before any retry, with no exactly-once promise.
The execution adapter boundary and the first provider: one protocol, declared capabilities, a qualification suite, a constructed child environment, and what a real stream can witness
The single seam through which this product runs an agent, and the first provider behind it. Fixes the three-part protocol (a request the supervisor writes, an event stream it reads, a result it records), the closed capability vocabulary with required tokens refusing before any process starts and preferred tokens degrading visibly, the negative suite that qualifies an adapter binary version, and the constructed rather than filtered child environment whose property is a complete record and explicitly not containment of hostile code. Sections 3.9 to 3.16 name Claude Code as the first provider and fix what its stream can and cannot witness: which capability tokens the manifest may declare, each against a measurement rather than a reading of the flags; the finding that a mid-stream denial notification accompanies the terminal refusal record and that a denied session still classifies itself as a success; which of two denial mechanisms the adapter must use, because they have opposite evidence properties; and what a qualification record binds to.
Independent acceptance, the receipt, the separately reported evidence dimensions, and the envelope two products exchange
How a claim of completion becomes, or fails to become, an acceptance. Fixes the identification of candidate bytes and the trusted base, the authority-set rule that stops a candidate from being judged by rules it wrote, the receipt that binds repository, base, candidate, suite, exit codes and policy digest, the four evidence dimensions reported separately from admission with closed value sets, independently supplied trust roots, the preservation of original bytes and named hash constructions, and the three names for absence, none of which reads as success. It also owns the envelope crate the statecraft platform wrote against these serializations and transferred here, which makes one owner for the reference, the dimensions, admission and the absence words rather than two implementations of one wire format.
The command surface: one binary, every verb the other specs name, and what an exit code means
The binary, and every verb reachable through it. Specs 002 to 005 each describe operator verbs and then own only a library crate, so nothing they specify is runnable. This spec owns crates/statecraft-cli/ and binds those verbs to a process: the command tree, the rule that a command is a thin binding and never a second implementation, the closed exit-code vocabulary that distinguishes a refusal from a failure from a finding, human and --json output as two renderings of one value, and the binary's name being a recorded decision rather than a Cargo default. It also carries the work, run and accept bindings that were specified separately as 009: discovery as a join of two spec-spine reports because one of them does not carry status, inspection as a read-only fold of the run record and nothing else, and the rule that a binding needing a library entry point gets it from the crate that owns the behavior. Section 3.11.1 adds the five verbs 002 sections 3.25 to 3.29 need: inspecting and explicitly upgrading the required harness identity, obtaining the managed-session payload, and recording startup evidence beside submitting qualification evidence for admission.
The family envelope: every --json answer in the exit and JSON contract spec-spine speaks
Amends 006 sections 3.3 and 3.4. Every --json answer this product writes becomes the envelope spec-spine has written since 0.26.0 (its spec 132 section 3.4): schemaVersion, tool, verb, outcome, exitCode, summary, then report for exit 0 or 1 and error for 2, 3 or 4, where error.kind is one of the family's ten tokens. No exit number moves. A usage error under --json is an envelope on stdout. The replacement of 002 section 3.23's check translation, which #118 proposed for the same change, is left to the spec-spine 0.28.0 adoption, which changes the producer answer it would read. Replaces the historical proposal from #118 with the amendment that the current model requires (001 section 5).
Harness delivery, hooks, and skills
Carries the environment adapter, delivered harness, consented settings, required identity, and startup-delivery requirements relocated from spec 002. It keeps repository initialization separate from the harness that a registered adapter delivers.
Governance producer adoption
Carries the governance producer boundary relocated from spec 002: exact released identity, supported APIs, and the rule that Statecraft consumes spec-spine without becoming a second compiler.
Setup profile revision 9: a project decides whether governance refuses an unresolved claim
Amends 002's setup profile. github-actions-rust revision 9 adds one boolean parameter, governance.fail_on_unresolved, default true. True renders what revision 8 renders: gate.sh governance runs index check with --fail-on-unresolved. False omits only that flag: index check still runs, names each unresolved claim and fails a stale or invalid index, and every other governance step is unchanged. It exists for a corpus that approves a spec before building it, where an approved spec's unbuilt claim is unresolved by design. Owner decision of 2026-09-25 ("statecraft-cli switch"), requested by the travel-memory program for Rahi.
Initialization never scaffolds a second 000 spec and never writes an approved one
Amends 002 section 3.15's reconciliation of the contract path <specs_dir>/000-bootstrap/spec.md. When the corpus already has another spec holding the ordinal 000, init does not scaffold the bootstrap spec and drops a stale managed record of one that is not on disk. When init does write the bootstrap spec, it writes it with status draft, because ratification is the owner's act and never a tool's. An existing 000-bootstrap spec is adopted and never rewritten, as before. A defect found by the travel-memory program in hiqlite #47 and rahi #90.
A setup profile declares remote desired state and doctor compares it without writing
Amends 002's setup-profile and remote-doctor contract. A profile renders a versioned desired remote-state document covering required checks, extra jobs, code-owner review, merge queue, the review-exception environment and its reviewers, workflow-token defaults, secret names, repository custom properties, and profile identity. doctor --remote compares every field independently and never writes remote state or reads a secret value.
Supervised adoption of one wire-witness sidecar per attempt
Amends 003 and 004 so Statecraft may adopt wire-witness as an exact linked producer, bracket one sidecar as its own durable effect, supply one per-attempt loopback proxy endpoint to the child, and report confinement and witness posture without treating testimony as authority or creating a general sidecar host.
Admission of wire-exchange evidence
Amends the run record, evidence envelope, and command projection so an exact statecraft/wire-exchange/v1 artifact can be bound to one run attempt and sidecar effect, judged deterministically under explicit policy, and shown without retaining secret values or confusing producer testimony with independent acceptance.
Managed-session evidence and admission
Carries the live-observation, control, startup-record, launch-state, managed-trial, trailer, and confinement requirements relocated from spec 002. It separates runtime evidence from installation and harness delivery.
Active specifications stay bounded and historical journals stay outside the corpus
Amends spec 001's authoring model so an active spec carries current requirements and concise resolved rationale, never an append-only work log. A mechanical gate bounds active files and moves historical journals and handoffs to a nonnormative archive outside the compiler input.
Setup profile revision 10: AI review defaults to a 3000-line diff cap
Amends spec 002's setup profile. github-actions-rust revision 10 raises the inherited AI review diff cap from 2000 to 3000 changed lines so ordinary substantial pull requests remain reviewable. Explicit operator selections remain authoritative and are preserved on upgrade.
Governed inputs for a complete first bootstrap
Amends 002's initialization contract and 009's producer call so a fresh repository can supply setup-profile parameters through one explicit, plan-bound input document, receives the exact governance-producer pin from the producer itself, and remains honestly partial when project-owned prerequisites are absent.
Immutable context-packet consumption across repository snapshots
Amends Statecraft's run, adapter, acceptance, command, and governance-producer contracts so one task may bind immutable spec-spine context packets from several independently identified repository snapshots, record intended and provider-visible delivery separately, and judge packet integrity, closure, freshness, citations, and evidence without reimplementing producer-owned selectors, closures, or digests.
Documentation task and structured result contracts
Amends Statecraft's run, adapter, acceptance, and command contracts with an immutable documentation-task document and a provider-neutral structured documentation result. It separates task authority from provider output, binds every claim to an explicit lifecycle grade and supporting references, preserves omissions and contradictions, and keeps provider completion apart from independent acceptance and publication.
Independent documentation acceptance and review
Amends Statecraft's run, acceptance, and command contracts with an independent documentation-acceptance record. It keeps intended delivery, observed transport, schema validation, citation resolution, semantic review, and acceptance as separate facts; requires claim-grade support, preserved unknowns and contradictions, valid artifacts, and a fresh context manifest; and grants no application or publication authority.
Fixed-export documentation workflow
Amends Statecraft's run, adapter, acceptance, and command contracts with a bounded offline documentation workflow over revision-pinned context exports. It preserves immutable task, delivery, raw result, validation, review, correction, and acceptance records as separate facts; proves the workflow with a deterministic fake adapter; and grants no live provider, repository application, remote export, or publication authority.
Setup profile revision 11: the commit walk runs a contained spec-spine
Amends spec 002's setup profile. github-actions-rust revision 11 copies the spec-spine binary into each temporary commit worktree as a regular file instead of linking it from outside, so spec-spine's containment rule can read every commit's tree. The walk's verdicts are otherwise unchanged.
Setup profile revision 12: a token-budgeted AI review and ratification as a merge condition
Amends spec 002's setup profile and spec 017's review cap. github-actions-rust revision 12 leaves a managed file out of the AI review only when its bytes match the digest its policy records, sends deletions as a list under their own cap, measures the reviewable change in estimated tokens, and reviews a change larger than one call in file groups whose verdicts are merged. It also refuses a merge that changes a path a draft spec owns, unless that pull request ratifies the spec with the owner's exception. An unreadable base now refuses instead of letting the candidate's policy and scripts judge it.
Prompt-injection qualification: untrusted content never expands authority
Amends the adapter negative suite and the documentation lane with a versioned hostile-content corpus and a deterministic product-invariance suite. Every surface that carries repository, packet, citation, provider, wire or tool-facing text is exercised with hostile payloads, and the suite proves by construction that Statecraft's decisions are unchanged: no authority expands, no credential is disclosed, no forbidden capability is invoked, no review or acceptance is bypassed. Provider susceptibility is a separate, later measurement that is recorded as testimony and is never the boundary. Delimiters and detectors are not security controls.
doctor reports a path whose recorded ownership disagrees with the transfer journal or the current rendering
Amends spec 002 section 3.5 with one additive, read-only doctor finding, ownership-disagreement. For each path, doctor compares three facts: the manifest entry's class, the latest transfer-journal record for the path, and the class the current selection's rendering would give it. Each incompatible ownership state defined below is reported with both sources named. doctor repairs nothing, transfers nothing and never treats a rendering it could not compute as agreement. Backlog SC-006.
Observation and proposals: noticing that a repository's governed truth changed, and proposing typed work without acting on it
Adds the transition this product lacks between "spec-spine says the repository means X" and "the operator is offered the work that follows". One observation reads exactly one commit through spec-spine's existing structured reports, reduces the answers to three closed projections, and compares them with the last observation recorded for that repository. Each semantic transition a built-in subscription names becomes a proposal with a deterministic identity, recorded in an append-only chain in the product home. A proposal is never executed by this spec: the operator admits it, admission re-derives its preconditions against the current commit, and an admitted proposal enters spec 003's existing run path with its cause recorded in the attempt's intent. No watcher, no loop, no automatic admission, and no change to spec-spine.
Agent attribution and agent commit identities are refused
Amends spec 001's authored-content rules so agent attribution is suppressed at the project boundary and refused in every text the governance gate reads. The same gate refuses commits whose author or committer uses a known agent address. Display names do not decide identity, and external contributors remain allowed.
One resolved judge: every spec-spine call this product makes runs the executable one resolution selected
Every spec-spine invocation this product makes, in every verb and in every hook it delivers, runs the executable that one resolution selected for that operation, and records which one. No call site names a bare `spec-spine` and lets PATH decide. The resolution's sources are, in order, the supervisor's path in a managed session, an operator override, the project's declared engine as spec-spine's own launcher resolves it when that launcher is present, the repository-local install named by the active setup profile, the repository's own build and then PATH; every candidate except the supervisor's already-resolved path is put to the pin, and each passed over is named. `doctor` reports the resolved executable, never a separate PATH probe. Where the resolved engine carries `check --json`, the check is read from its envelope instead of from its wording. Amends spec 008 section 3.23 contracts 2, 4 and 5.
A governed project needs no provider-specific directory, and this product writes none
Makes the absence of provider-specific project directories (`.claude/`, `.codex/`, `.agents/`, `.agent/` and their equivalents) a supported and tested operating condition. No adapter declares a managed or adopted path inside one. The Claude Code adapter's owned instructions file moves out of `.claude/statecraft/` into content this product already manages, and its pointer, where one is still needed, imports `.statecraft/AGENTS.md` directly. A project that already holds the old file converges through the ordinary upgrade and removal rules. A qualification case starts with every provider directory absent and proves initialization, the governed verbs and context delivery work and recreate none of them. Brings the adapter into line with spec 002 section 3.12 and spec 008 section 3.14, and amends the adapter declaration of spec 004.
Setup profile revision 13: the repository-local engine lives in .bin/
The owner decided that neither spec-spine nor Statecraft uses `.tooling/bin` and that the repository-local spec-spine is `.bin/spec-spine`. Profile github-actions-rust revision 13 installs the pinned engine there, its gate, commit walk, CI cache and policy commands read it there, and its ignore fragment ignores `.bin/`. The resolver's repository-local candidate, the delivered hooks and this repository's own `Makefile` follow. A revision-12 project converges through the ordinary managed upgrade.
Required project-owned code checks under managed governance
Profile revision 14 keeps Rust as its default and admits an explicitly selected external code surface. A contained executable local script and a tracked reusable workflow remain required. Rust prerequisites apply only to the Rust selection. Governance, coupling, AI review, protected owner exceptions and the single required aggregate retain their existing rules.
A reviewed initialization plan may explicitly move the adopted spec-spine pin
Adds an explicit exact --spine request to init plan and init apply. The source and destination pins, producer identity, original configuration digest and minimal authored edit are part of the consented plan identity. Apply changes only the existing required_version value in spec-spine.toml after revalidating the plan, then uses the ordinary managed upgrade rules. No implicit pin move, dependency rewrite, ratification or branch protection change is authorized by initialization.
wire-witness ↗
Wire evidence · 13 shown
Bootstrap spec system
Foundational contract: authored truth lives only in markdown (+ YAML frontmatter); machine-consumable truth is compiler-emitted JSON only; every artifact is a deterministic function of (config, file contents); a typed authority graph governs who-owns-what.
Boundaries, authority, and dependency layout
Defines wire-witness as a library plus isolated hosts that capture provider wire exchanges and produce testimony without granting authority. Assigns ecosystem responsibilities, fixes the three-crate dependency layout, and reserves captured-output training, hosting, and publication to the owner.
Exchange record and provider normalization
Defines wire-witness.exchange/1, the canonical testimony produced from one observed provider exchange, and lossless normalization rules for Anthropic and OpenAI request, response, streaming, identity, usage, and cost fields.
Redaction, custody, and retention
Defines metadata-only default retention, explicit content capture, mandatory credential removal before durable writes, per-attempt custody, 0600 outputs, and a fresh memory-only CA private key for every capture attempt.
Allowlisted capture proxy and transparent streams
Defines the per-attempt proxy that intercepts only explicitly allowlisted provider hosts, tunnels authentication endpoints and every other host without inspection, and transparently handles CONNECT, TLS, HTTP/1.1, HTTP/2, SSE, and WebSocket traffic with bounded parsing and backpressure.
Attempt binding and stdio sidecar protocol
Defines the immutable AttemptBinding supplied by Statecraft, the versioned stdio protocol for one sidecar per attempt, capture-digest closure of the bracketed effect, and statecraft/wire-exchange/v1 testimony for admission by the host rather than by the witness.
Standalone host and per-process environment
Defines the standalone CLI, unsupervised session identity, child-only proxy and trust environment, additive JSON output, and the prohibition on global proxy, certificate, shell, browser, or operating-system trust mutation.
Repository constitution principles
Proposes six wire-witness-specific constitution principles covering testimony, secret custody, ambient-system mutation, exact supervised binding, lifecycle claims, and explicit incomplete or residual-risk states. The principles remain inactive until owner approval and the governed standing-constitution edit.
Instruction delivery observation
Defines a content-gated, offline comparison that reports whether exact operator-supplied instruction bytes were observed in a complete outbound provider request, without turning one observation into adapter qualification, policy, or authority.
Usage and cost summaries
Defines a deterministic binding-level summary of observed request counts, provider-attributed usage, reported cost, labeled estimates, identity variation, completeness, and gaps without converting testimony into a charge, quota decision, budget, or provider qualification.
Policy-bounded corpus export
Defines an immutable, bounded export manifest for policy-permitted wire testimony, preserving binding, retention, completeness, redaction, and artifact identity without granting permission to retain, disclose, replay, train on, admit, or evaluate the captured material.
Agent attribution is refused and ratification is owner-gated
Refuses agent attribution and known agent commit identities, suppresses provider attribution at its source, and adopts the two-flow Statecraft profile in which implementation and owner ratification remain separate.
Profile 13 uses the repository-local engine in .bin
Adopts Statecraft profile revision 13 with the existing exact engine pin, and replaces revision 12 acceptance with the recorded revision 13 identity.
Snapshot pins are reviewed changes, not live branch tracking. How this catalog is built → · Download the presentation payload