Development record
`/abcd:audit` Is the Umbrella; Conversation/Edit Chain Is Its Default Application
Press Release
abcd ships
/abcd:audit— a compliance-grade audit umbrella with a tamper-evident hash chain over the project's conversation and edit history as its default application./abcd:auditis the whole; specific verifications register as sub-verbs that share the substrate. The default application —/abcd:audit chain— covers every Claude Code session transcript, every native spec state transition, every disembark/embark/launch/intent invocation, and every file edit committed under abcd's lifecycle hooks. JCS-canonicalised (RFC 8785) entries land in a Merkle chain at.abcd/logbook/audit/chain-<ts>/. Sibling sub-verbs (/abcd:audit lifeboat <path>per itd-35; future audit applications) reuse the substrate's primitives without re-implementing them. Bare/abcd:auditshows status, last-verified timestamp, chain head, and the registered sub-verbs (per the universal bare-command-as-help convention) — it never mutates state. The chain-specific verification report comes from/abcd:audit chain. Mismatches surface immediately with the offending entry's path, line, and expected vs observed hash."When an auditor asked 'can you prove the project's history is intact?' we used to say 'check git'," said Dave, compliance lead. "git is great for code provenance but says nothing about the agent conversations and tool invocations that produced the code.
/abcd:auditis the missing half: every conversation, every edit, hashed and chained. And because/abcd:auditis an umbrella, when we needed lifeboat integrity verification we got/abcd:audit lifeboatfor free — same substrate, different target."
Why This Matters
/abcd:audit is the whole; specific verifications are applications. This intent ships the whole: the umbrella verb, the substrate (JCS, UUIDv7, Merkle), the bare-verb dispatcher behaviour, and the default conversation/edit-chain application. Future audit needs (lifeboat integrity per itd-35, schema audit, prompt-quality audit) register as sub-verbs that share this substrate rather than re-implementing chain mechanics from scratch.
abcd captures a lot of project history — the native transcript store's transcripts, the native spec store's spec states, .abcd/logbook/ per-command reports, the .abcd/work/ shared working material and .abcd/development/ curated durable record. That history is legible but not tamper-evident: anyone with filesystem access can edit a transcript or rewrite a logbook entry post-hoc, and a casual reader has no way to detect it. For most uses this is fine — abcd's audience is solo developers and small teams operating in good faith.
For compliance-sensitive contexts (regulated industries, third-party handoffs, post-incident audits, regulated AI governance), the non-repudiability of project history becomes a hard requirement. Auditors need to prove that what's in the repo today is what was written yesterday — that the conversations, the agent decisions, and the resulting edits haven't been silently rewritten.
The mechanism is well-established (lifted from ~/.abcd/ v0's F-031 / F-032 hash-chain work): JCS-canonicalised JSON for deterministic serialisation, UUIDv7 IDs for time-ordered entries, SHA-256 segment hashes chained via a Merkle tree to a single root hash. The trail covers four artefact classes:
- Conversation transcripts (native transcript store) — every session as a discrete entry
- Lifecycle hook events (intent moves drafts→planned→shipped, capture state changes, ahoy install/upgrade) — every state transition
- Tool invocations (
/abcd:disembark,/abcd:embark,/abcd:launch,/abcd:intent plan/ship/review/consistency/shape,/abcd:capture,/abcd:grill) — every command run - File edits committed under abcd's pre-commit hook — the diffs that actually changed the repo
abcd doesn't need to host a separate audit service: the chain lives in .abcd/logbook/audit/chain-<ts>/, ships with disembark, and verifies on embark.
What's In Scope
- Native hash-chain mechanics — port the chain mechanics from
~/.abcd/scripts/hash-chain.pypatterns. JCS RFC 8785 canonicalisation; UUIDv7 RFC 9562 IDs; SHA-256 segment hashes; Merkle root anchoring. - Hook integration: ahoy installs a pre-commit hook + a Stop hook that append entries to the active chain segment as transcripts close, lifecycle hooks fire, and edits commit. Append-only; no in-place rewrites.
- Chain segmentation: each segment covers one calendar day (configurable). Segment-to-segment chaining anchors yesterday's segment in today's, so the chain head transitively covers all history.
/abcd:audit(bare) — status+help only per the universal bare-command-as-help convention. Shows chain head, last-verified timestamp, registered sub-verbs (chain,lifeboat, future), and suggested next actions. Never mutates state./abcd:audit chain— the verification action: verifies the chain end-to-end and writes a report to.abcd/logbook/audit/chain-<ts>/report.{json,md}. This is the default application of the umbrella; itd-16 owns this sub-verb's contract.audit.schema.json— the public format for chain segments and the verification report.- Disembark integration:
/abcd:disembarkincludes the latest chain segments in the lifeboat under_provenance.jsonaudit_chain_root. The chain root hash anchors the lifeboat's authenticity. - Embark integration:
/abcd:embarkverifies the lifeboat's chain root before unpacking; mismatch aborts the unpack with the offending segment named. - Launch integration (per itd-66): launch payload manifest excludes the chain by default (sensitive); explicit opt-in via
.abcd/launch.allowif a release should ship its own chain. - Opt-in via
.abcd/config.json—oracle.audit.chain.enabled = true | false(defaultfalseto avoid surprising users; flip totruefor compliance-sensitive projects).
What's Out of Scope
- Cryptographic signing — chain entries are hashed but not signed. Signing is a separate concern (could become itd-XX if a use case emerges; e.g., signed releases).
- Cross-project chains — each project's chain is self-contained. No global root hash spanning multiple repos.
- Hashing source repo state at disembark — abcd hashes its own conversation/edit history, not arbitrary repo contents.
gitalready provides the latter. - Lifeboat-artefact-only Merkle (the original itd-16 framing, 2026-05-03 → 2026-05-08) — replaced by this broader conversation/edit chain. If a narrower lifeboat-only integrity proof is needed independent of the full chain, that's a separate intent.
- Real-time chain verification on every command — verification is on-demand (
/abcd:audit chain) plus mandatory at embark. Continuous verification would add latency without proportional value. - Chain compression / pruning — entries grow unboundedly with project activity. Pruning policy is a future concern if chains become unwieldy.
- Specific audit applications other than chain and lifeboat — schema audit, prompt-quality audit, third-party-vendor audit, etc., are deliberately not in scope here. The umbrella is open; specific applications register as their own intents (precedent: itd-35 for lifeboat).
Acceptance Criteria
BDD format, per
itd-1-acceptance-gates. These gates are checked byintent-fidelity-reviewerwhen this intent moves toshipped/.
- Given
oracle.audit.chain.enabled = true, when any abcd command runs (/abcd:intent plan,/abcd:capture,/abcd:disembark, etc.), then an entry is appended to today's chain segment with a UUIDv7 ID, JCS-canonicalised payload, SHA-256 hash, and aprev_hashfield linking to the previous entry; the entry conforms toaudit.schema.json. - Given a project with chain segments spanning multiple days, when the user runs
/abcd:audit chain, then abcd recomputes every entry's hash, walks the chain, and writes a verification report to.abcd/logbook/audit/chain-<ts>/report.{json,md}. Bare/abcd:audit(no sub-verb) shows the chain head hash, last-verified timestamp, registered sub-verbs, and any pending entries — status+help only, no state mutation, per the universal bare-command-as-help convention. - Given a chain entry has been tampered with (e.g., a transcript edited post-hoc), when
/abcd:audit chainruns, then verification fails on the offending entry, names the file and entry ID, prints expected vs observed hashes, and exits non-zero. No further entries are verified after the first mismatch (preserves the failing-fast invariant). - Given a disembark with chain enabled, when the lifeboat is packed, then
_provenance.jsonincludesaudit_chain_root: <hash>and the latest chain segments are included in the lifeboat underaudit/. The Merkle root anchors the lifeboat to a verifiable point in project history. - Given an embark with a lifeboat that includes a chain root, when
/abcd:embark from <path>runs, then embark verifies the chain root matches the embedded segments BEFORE unpacking; a mismatch aborts the unpack with a clear message naming the divergent segment. - Given chain enablement is opt-in (default
false), when a user withoracle.audit.chain.enabled = falseruns disembark, then no chain entries are written,_provenance.jsonomitsaudit_chain_root, and embark of the resulting lifeboat skips chain verification without warning — opt-in means opt-in cleanly on both ends. - Given itd-9 (schema migration) and itd-16 (hash chain) both ship, when embark applies a schema migration to a chain entry, then the migrated entry's hash is RE-COMPUTED post-migration and recorded alongside the original hash in the migration log — schema upgrades are not silent over the chain.
SOTA
Surveyed on 2026-10-23; sources, evidence tiers and the local fit risks are in the security-isolation SOTA note.
- The named primitives are standards and fit: RFC 8785 (JCS, Informational) sorts properties recursively and serialises numbers as ECMAScript does, for data that must be "expressed in an invariant format"; RFC 9562 (Proposed Standard) gives UUIDv7 "a time-ordered value field" and leaves counter rollover to the application (RFC 8785; RFC 9562).
- The tree should be RFC 6962's: leaf
SHA-256(0x00 || d), nodeSHA-256(0x01 || l || r), "required to give second preimage resistance"; Go'sx/mod/sumdb/tlogis compatible with those proofs and would be a Go dependency needing sign-off (RFC 6962; x/mod/sumdb/tlog). - The chain head has a standard text form: a C2SP checkpoint, three lines (origin, tree size, base64 root) plus signature lines, the form the Go checksum database serves (tlog-checkpoint; Go modules reference).
- Tamper-evidence needs a reader who holds an earlier head: a witness cosigns only a checkpoint consistent with the last one it signed; without someone outside the filesystem, whoever edits a transcript can recompute the chain, so a chain alone detects a partial edit, not a rewrite. The press release's "non-repudiability" is not reachable without that anchor; the ruling stands and this records the gap (tlog-witness).
- Signing needs no new dependency:
ssh-keygen -Y signunder a namespace, verified against an allowed-signers file; git'sgpg.format = sshuses the same (ssh-keygen(1); git gpg config). Signing is out of scope by the record; the cheapest anchor is nonetheless a signed head, the next cheapest a head pushed to the forge as a tag, which anchors only where the forge refuses to move or delete it (a tag ruleset or an immutable release) or a third party keeps a copy; otherwise whoever can push can rewrite the tag along with the chain. - The attestation stack exists and is more than this needs: in-toto Statements in DSSE envelopes, SLSA provenance, Rekor; "still under development", and a public log for private transcripts runs against the launch rule (in-toto Statement; DSSE; Rekor).
- What is hashed is the redacted transcript, since the history store redacts on write; the claim must say so.
Path 2: native, shaped to RFC 6962 hashing and the C2SP checkpoint, with the head's signature and external anchor as the first seam; JCS and UUIDv7 as drafted. The RFC 6962 tree is the segment's Merkle root; criterion 1's per-entry prev_hash stays as drafted, so the tree adds inclusion and consistency proofs over a chain it does not replace, and no criterion changes. The independent fit-challenge runs at plan time.
Open Questions
- (SOTA 2026-10-23) Where the head is anchored: a signed checkpoint in the local tier, a tag pushed to the forge (an anchor only where protected or witnessed), or nowhere (and then the claim is "partial edits are detected", not "history is provable").
- Default on or opt-in? Recommend opt-in (
enabled: false) to avoid surprising users with verification failures during normal flow. Compliance-sensitive projects flip totruedeliberately. - Segment granularity — daily segments fit the "one Merkle root per day" pattern from
~/.abcd/v0. Hourly might be too granular; weekly might miss the audit point. Configurable in.abcd/config.json? - Interaction with itd-9 schema migration — does a migration regenerate the chain or carry it forward? Likely re-anchors the migrated entry while preserving the pre-migration hash for forensic comparison.
dev-synccontent (which changes between disembarks) — hashed at sync time or only at disembark? Likely sync time, so the chain reflects every dev-sync step.- Public-repo launch behaviour — should
/abcd:launchstrip chain segments from the public payload (default-deny) or include them as ship-with-provenance (opt-in)? Recommend default-deny; users with compliance ship-with-provenance needs opt in via.abcd/launch.allow. - Verification cost — chains grow unboundedly; large projects may need partial verification (
/abcd:audit --since <date>). Out of scope for the initial ship; revisit if chains become unwieldy in practice.
Audit Notes
Empty. Populated by intent-fidelity-reviewer when intent moves to shipped/.
References
- README.md
/abcd:auditrow (canonical command shape: "Compliance-grade trail: tamper-evident hash-chain over the project's conversations and edits, so anyone can verify nothing was retroactively altered"). 02-constraints/04-naming.md—/abcd:auditreserved for formal verification surface; not metaphor-mapped, dignified register.- itd-9 (cross-version lifeboat schema migration) — coordinates with chain re-anchoring on migration.
~/.abcd/v0 F-031 / F-032 hash-chain work — provides the JCS + UUIDv7 + Merkle pattern.- Prior framing: this intent originally scoped to lifeboat-artefact integrity (2026-05-03 → 2026-05-08). Rewritten 2026-05-08 to align with README's canonical conversation/edit framing. The narrower lifeboat-only integrity proof may return as a separate intent if the use case re-surfaces independently.
Frontmatter
| id | itd-16 |
|---|---|
| type | intent |
| lifecycle | drafts |
| kind | standalone |
| severity | major |
.abcd/development/intents/drafts/itd-16-hash-chain-merkle-audit.md
open on GitHub ↗ · commit history ↗
References1
- itd-9Old Lifeboats Still Unpackbuilds on
Referenced by10
- adr-40Review, audit, lint, and gate are four buckets, separated by what each comparesrelated
- iss-2610262031298011itd-16 lands its hash chain under .abcd/logbook/audit/, the retired logbook locationrelated
- iss-2610262031305231The naming chapter stages /abcd:audit lifeboat under itd-16, while itd-16 assigns that sub-verb to itd-35related
- iss-2610262034027543itd-16 writes the chain verification report under .abcd/logbook/audit/; the intent surface puts the chain sub-verb's receipts in the local ephemeral logs tierrelated
- iss-2610262036067608itd-16 makes /abcd:audit an open umbrella a schema or prompt-quality audit can join; 16-lint reserves it for hash-chain fidelity and calls a check of form a lint.related
- iss-2610262038541403itd-16 writes its chain report under .abcd/logbook/; 03-configuration says logbook/ is retired in favour of the local tier's logsrelated
- iss-2610262038559695itd-16 calls bare-command-as-help a universal convention; the surfaces register says bare invocation is a convention, not a universalrelated
- iss-2610262040357240itd-16 puts its hash chain under .abcd/logbook/; the build sequence says that location is retired and a test fails the build on it.related
- iss-2610262042244924The audit verb is the hash-chain umbrella in itd-16 and a repo-conformance check in itd-85related
- itd-35Lifeboats Carry Their Own Integrity Proofblocks
mentions28
- dcn-26070800000000082026-07-08 — itd-76 grilled: leak guard promises literal strings only
- dcn-26081600000000032026-08-16 — the assessment vocabulary settled at a maintainer grill and filed as adr-40 (proposed): **four buckets — lint / review / audit / gate — separated by what each compares**, not by determinism, not by trigger. The rule was already half-recorded (05-internals/01-agents.md rules the two verdict families disjoint: "reviews assess a change, criterion verdicts assess a promise vs reality") and three shipped surfaces contradict it — `intent review` emits family 2 so it is an audit, `abcd audit` is deterministic rule-checking so it is a lint (and it consumed the `/abcd:audit` name 02-constraints/04-naming.md reserves for itd-16), and `disembark oracle` emits family 1 while the brief calls its output an audit. Grill rulings: **one surface performs exactly one act** (multi-act surfaces split at design time — cheap, because every multi-act surface abcd has is unbuilt, so they are specified rather than refactored); the buckets are a **closed list extended by PR, not a criterion** (an autonomous run given a criterion interprets it; a table lookup matches or fails); `intent ready` survives as the fourth bucket **gate** because it names a decision rather than a comparison, preserving itd-94's exit-code contract; determinism is orthogonal (itd-16's hash-chain check is a deterministic *audit*; itd-85's conformance check is a *lint* however built); automatic-vs-manual never enters a verb name (the trigger is the facilitator's, per itd-97); the oracle is the model-access seam beneath review and audit, never a bucket. **Clean break, no aliases** — pre-1.0.0, `--impact breaking` drives version derivation (iss-171 precedent), users re-download; an alias would additionally collide with the documentation discipline's ban on change-narration, leaving an undocumented silent forward. The role mapping is the operational reason the split earns its cost: product thinker reads `audit` and essentially nothing else, which makes itd-99's "never hand a product thinker a technical decision" mechanically checkable. Same grill corrected a wrong finding: iss-246 was filed claiming abcd's record documents a surface far larger than the binary with no gate catching it; **false** — the `Status` column in 04-surfaces/README.md is machine-checked by `surface_coverage` (iss-35), the brief carries ~100 inline `design target` markers, and adr-5 makes the brief aspirational by design. The real defect is narrower: `surface_coverage` is blind *inside* a row, so six of twenty rows read `shipped` then qualify themselves in prose, and documents outside the registry cite those sub-verbs as live (all three citing **predecessor-store** spec ids spc-29/spc-30, whose prose survived the migration to the native store). Fix per adr-40 §6: sub-verb tables carrying two facts per verb — bucket and existence — with `surface_coverage` extended to registered cobra sub-commands, one table serving both disciplines. Also established: the twelve-step record walk completes at ten steps; step 2 (`capture promote`) has no verb and step 12 (`capture resolve`) cannot write `resolved_by`, so **walkability is sequenced before coherence** — renaming ~90 files across a process nobody can walk is polish before function. Plan at plans/2026-08-16-process-coherence-and-walkability.md; six intents to plan, none plannable unattended. Rejected: one polymorphic `review <id>` verb (merges the disjoint families, erases the role signal), classifying acts rather than surfaces (makes the bucket a statement of intent rather than a guarantee, and hands product thinkers lint findings), three buckets without `gate` (forces `intent ready` to become `intent lint`), and adding `verify`/`validate` (itd-109's verification suite is a packaging of lints and audits, not a fifth kind).
- iss-21itd-8 and itd-16 cite the launch intent as itd-launch by name, not number — a dangling-reference class the itd-78 graph lint should flag; re-point to the concrete itd
- iss-141tamper-evident receipts: receipts and review directories are hash-anchored and manifests are verified, but nothing is cryptographically signed and there is no append-only chain over the receipt history — integrity evidence, not proof against a capable insider. itd-16 (hash-chain/Merkle audit umbrella, draft) is the design home; this capture tracks the narrower question of whether the existing receipt flow should become tamper-evident (RFC 9162-style verifiable log or signing) independent of the full audit umbrella. The itd-100 crosswalk's tamper-evidence row cites this id.
- iss-238brief/05-internals/01-agents.md still reserves /abcd:audit for 'compliance / hash-chain' (itd-16's umbrella), contradicting the shipped repo-conformance audit surface documented in 04-surfaces/16-audit.md (itd-85 lineage).
- iss-2610150749052356The generated ADR and drafts indexes dropped hand-written relationship notes the old rows carried ('refines adr-50' on adr-55, 'amends adr-19' and 'extends adr-19, adr-20' on adr-31, adr-39/adr-23 links on itd-22, a reframe note on itd-16), because a row is derived from the record's H1 and status only.
- iss-2610262030497817The naming register attributes /abcd:audit lifeboat to itd-16; the intent chapter attributes it to itd-35
- iss-2610262031419126The naming register reserves `/abcd:audit` for itd-16's formal-verification surface, but itd-85 takes `abcd audit` for a repo-conformance check.
- iss-2610262032256736Launch names its docs-lint gate row the documentation audit; the intent chapter keeps audit and lint as separate verbs, the conformance check being lint
- iss-2610262034151413itd-85 names `abcd audit` as the repository-conformance check; the intent chapter says the conformance check is `abcd lint` and reserves `/abcd:audit` for hash-chain checks.
- iss-2610262035510145The voyage entry calls privacy-hygiene an 'audit rule' run by abcd's 'own audit'; the lint chapter says that verb is a lint and that 'audit' is reserved
- iss-2610262036142715The lint chapter reserves /abcd:audit for itd-16's hash-chain fidelity surface and calls the conformance check abcd lint; itd-85, the intent the chapter cites, ships that same check as abcd audit.
- iss-2610262039195057itd-85 makes `abcd audit` a repository conformance check, but the agent catalogue reserves the top-level `/abcd:audit` for itd-16's hash-chain surface
- iss-2610262043528869`abcd audit` is the repo-conformance verb in itd-85 and the lifeboat-integrity verb in itd-35
- iss-2610262044218994`abcd audit` names a repo-conformance check in one record and is reserved for the hash-chain merkle audit in another
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Them
- itd-4Nothing You Notice Gets Lost
- itd-31The Brief and Every Intent Stay Consistent — Or Abcd Tells You Why They Don't
- itd-32Every Audit In Abcd Knows What It Audits, By What, And Against What
- itd-66abcd Renders The Exact Public Payload, Proves The Excludes Never Leak, And Smoke-Tests The Installed Surface Before Any Snapshot
- itd-76abcd Lets You Consult Sources You Cannot Cite — and Remembers Every Debt
- itd-78abcd Knows What to Build First — Even When It's Something Small
- itd-88Know What a Repo Can and Cannot Tell You — Before You Trust the Lifeboat
- itd-100Alice Reads One Page and Knows Exactly Where abcd Stands in the Agentic-AI Landscape
- itd-124The Conformance Check Calls Itself Lint
- spc-15alice-reads-one-page-and-knows-exactly-where-abcd-stands-in
- spc-29the-conformance-check-calls-itself-lint-abcd-audit-becomes-a
- spc-31source-provenance-ledger