Date: 2026-09-10. Status: DRAFT for public review (Phase 3 deliverable, §8).
Normative language: MUST / MUST NOT / SHOULD / MAY per RFC 2119.
Reference implementation: this repository (veridict/ package) at the commit
introducing this file. Where the standard and the reference implementation
diverge, the divergence MUST be recorded in §14 (registry errata) until
resolved.
Veridict answers one question with evidence that survives verification: what did this actor claim, what did independent producers find, and who decided the residual risk? Everything below exists to make that answer append-only, falsifiable, and honestly inconclusive when it must be.
| Level | Who | MUST satisfy |
|---|---|---|
| Core | ledger, schema, ladder, policy, certificate, dossier | §2–§5, §7–§13 |
| Verifier | built-in W1a/W1b producers | §2–§5; W1a/W1b ceilings |
| Watcher | third-party producers | §2, §4, §6 (+ conformance kit §6.7) |
| Resolver | human-facing decision surfaces (CLI/UX) | §10, §12, §13 |
A conforming Core implementation MUST be verifiable offline: replay of a certificate MUST require only the ledger file, the certificate file, and Core-level code (§11.4).
2.1 Canonical JSON. canonical_json(obj) = JSON with sorted keys,
compact separators, ASCII escaping. All digests and signatures are computed
over canonical form. Two implementations that disagree on canonical form
cannot interoperate — this is the single most load-bearing rule.
2.2 Digests. payload_digest(payload) = sha256(canonical_json(payload)).
Identifiers are sha256(<dotted preimage>)[:N] with N fixed per type (§4.2,
§11.1).
2.3 Ledger. An append-only JSONL sequence of entries. Each entry carries
at minimum: seq (0-based, contiguous), ts, entry_type, author
(ActorRef: kind, identity, version), payload, payload_hash
(= payload_digest), prev_hash, entry_hash. The preimage of entry_hash
MUST bind all prior fields including prev_hash; the genesis entry's
prev_hash MUST be a fixed sentinel. ts MUST be an ISO-8601 UTC string
— floats MUST NOT occupy hashed positions, because canonical float
formatting is language-fragile while string formatting is not; seq carries
the ordering and ts carries provenance. An implementation MUST refuse to
load a line that is not valid JSON (a truncated tail is a crash artifact, not
a silent gap) — it MUST raise a chain error naming the line number (§7.3 m2).
2.4 Authoritative kinds. The entry-type registry (§14) is append-only.
v1.0.0 registers: key.enrolled, task.started, actor.output,
claim.registered, evidence.recorded, divergence.flagged,
deliberation.rounded, calibration.updated, escalation.requested,
dossier.issued, escalation.resolved, policy.fail_safe,
policy.decision, watch.observed, policy.passed, checkpoint.anchored,
certificate.issued, watcher.registered, watcher.revoked.
2.5 Policy-is-data. Every policy decision MUST embed the policy_digest
of the declaration that produced it. Decisions are reproducible from
(policy, claims, evidence) or they are not decisions.
3.1 ActorRef kinds registered in v1.0.0: verifier, jury, watcher,
adjudicator, divergence_detector, system, human,
watcher_registry.
3.2 Authority disjointness (§5.4): a producer of one kind MUST NOT write
another kind's entries. Watchers are authoritative over NOTHING — they return
evidence items; the orchestrator records them. The human is authoritative
ONLY over escalation.resolved (R3). Automated systems MUST NOT write
escalation.resolved (§12.4).
3.3 Abstain ≠ refute. Absence of evidence is never evidence. A producer that errors, times out, or returns an out-of-contract output MUST be recorded as an abstention and MUST NOT produce a REFUTES stance.
4.1 Fields: claim_id, task_id, subject, predicate, scope,
summary, derived_from, verifiability, falsifiable_by, critical_class.
4.2 verifiability ∈ {"MACHINE_CHECKABLE", "DOCTRINAL", "MIXED"}.
claim_id = sha256(task_id | summary | verifiability)[:16] — the FULL claim
body, never a truncated natural-language slug (a truncating id silently
merges distinct claims and poisons every downstream join). An implementation
MUST reject a duplicate claim_id within one extraction pass (fail-loud, not
dedupe-silent).
4.3 A claim without a falsification channel MUST NOT be admitted as MACHINE_CHECKABLE.
5.1 Fields: evidence_id, claim_id, evidence_class ∈
{TEST_EXECUTION, REPRODUCIBLE_RUN, FORMAL_PROOF, STATIC_ANALYSIS,
JURY_OPINION, WATCHER_REPORT}, tier ∈ {W1a, W1b, W2, W3}, producer,
artifact_ref, reproducibility {deterministic, rerun_recipe}, stance ∈
{SUPPORTS, REFUTES}, confidence ∈ [0,1], rationale, schema_version.
5.2 Tier semantics: W1a = executed machine truth; W1b = deterministic static truth; W2 = heterogeneous-jury doctrine; W3 = unverified doctrine.
5.3 The five immutable tier rules (§4.3). A conforming implementation
MUST enforce, and MUST NOT offer configuration to disable:
1. Strict ordering W1a > W1b > W2 > W3.
2. Doctrine (W2/W3) can never overturn W1a.
3. A W1b refutation of a claim supported by W1a opens a depth-budgeted
meta-claim (coverage question), whose verdicts feed the policy engine
and the certificate like any claim's.
4. A SPLIT is information: it is recorded (divergence.flagged) and never
averaged away.
5. W3 alone can never yield VERIFIED; a MACHINE_CHECKABLE claim without W1
evidence is at best INCONCLUSIVE.
5.4 Divergence classification (normative). Over the W2/W3 evidence on a
claim: if there is no doctrinal evidence, or no REFUTES, or no SUPPORTS, the
divergence is UNANIMOUS. Otherwise let m = the minority stance count; if
m / |doctrinal| ≤ divergence_tolerance the divergence is MAJORITY, else
SPLIT. W1a/W1b conflicts are not divergence — the ladder (§7) handles them
directly.
6.1 WatcherManifest (registration contract): watcher_id, name,
version; producer {identity, maintainer} (anonymous watchers MUST be
rejected); capabilities {evidence_classes, max_tier, subscribes_to};
resource_class {timeout_seconds, cost_budget, sandbox_level};
integrity {code_hash, update_policy} (the watcher's own code is pinned).
6.2 The W1a ceiling. max_tier ∈ {"W1b", "W2", "W3"} — a manifest
claiming W1a MUST be rejected at construction. W1a is executed machine truth
and is reserved to built-in verifiers. Session output tiers map 1:1 from the
ceiling; no runtime path may surface W1a from a watcher.
6.3 Blind sessions. The doctrine function receives exactly
(claim_summary, artifact_reference) — the artifact REFERENCE (path or
digest), never other producers' outputs. The deliberation round (§9) is the
only cross-visible exception, and it is jury-only.
6.4 Authority boundary. A watcher session returns evidence items; the
orchestrator records them as evidence.recorded with author kind watcher.
Abstention (error, timeout, None, out-of-contract stance) yields NO entry
from that run — an abstaining watcher MUST NOT block, refute, or flag.
6.5 Resource contract. A declared timeout_seconds > 0 MUST be enforced
(deadline → abstain). A hung watcher must never block the audit.
6.6 Registration and revocation. Registration is a watcher.registered
entry whose
payload binds {manifest, manifest_digest, signature{key_id, algorithm,
sig_b64}}, the signature taken over the canonical manifest body with a key
enrolled in the SAME ledger (offline parity). Latest registration wins;
verification MUST re-check entry integrity, digest-vs-body, signature, and
the manifest invariants.
Revocation is a signed watcher.revoked entry naming the watcher and
the digest of the revoked manifest. Lifecycle resolution is latest-entry-
wins: the most recent watcher.registered or watcher.revoked entry for a
watcher determines whether it is active. Revocation is FORWARD-LOOKING:
evidence recorded before the revocation stays in the ledger (append-only —
no deletion), and certificates anchored before the revocation remain valid;
a conforming orchestrator MUST consult the registry and MUST NOT route
claims to a revoked watcher (a revoked watcher is not a participant — its
silence records neither evidence nor abstention); a marketplace index MUST
NOT list watchers whose lifecycle status is revoked.
6.7 Conformance kit (certification precondition). A watcher MUST pass the
v1.0.0 kit before marketplace listing: C1 manifest invariants; C2 W1a
ceiling; C3 blindness; C4/C5/C6 abstain on error/None/bad-stance; C7
confidence clamp; C8 evidence shape on a benign probe; C9 registry
verification; C10 deadline enforcement. The kit probes the CONTRACT, not the
watcher's opinions. The kit is a LOWER BOUND on trustworthiness, not a
proof: it exercises a watcher's function a bounded number of times, so a
stateful function can pass the kit and deviate in production — marketplace
certification therefore additionally requires review of the manifest's
code_hash. Similarly, deadline enforcement protects the audit, not the
host: an expired watcher's thread runs until its function completes
(host-side isolation is the control for a function that never returns).
Walk top-down; the first matching rule decides. w1a/w1b = the W1a/W1b
evidence sets; "all-SUPPORTS" is over every evidence item on the claim.
verifiability == MACHINE_CHECKABLE ∧ W1a non-empty ∧ all W1a
SUPPORTS ∧ no REFUTES at any tier ⇒ VERIFIED (doctrine is advisory). If
the meta-claim depth budget > 0, a coverage meta-claim is registered.claim.critical_class ∈ policy.criticality ∧ divergence == SPLIT
⇒ ESCALATED to the human risk owner; the system MUST generate a dossier
(§12) and MUST NOT resolve itself.inconclusive-unresolved:{claim_id}; all doctrinal stances
SUPPORTS ⇒ VERIFIED; ANY doctrinal REFUTES — even inside a MAJORITY
favoring SUPPORTS ⇒ REFUTED (fail-closed: a lone doctrinal dissenter
blocks, never passes). W1b present but W1a absent ⇒ W1b SUPPORT outranks
doctrine per §5.3 rule 1: verdict VERIFIED, dissent recorded in the
divergence field and not promoted into the verdict (Erratum D14).
W3-only evidence (no W2) ⇒ INCONCLUSIVE (§4.3 rule
5). Every remaining case ⇒ INCONCLUSIVE — a conforming implementation has
NO silent pass. Errata (§14.2): the design's R2 makes SPLIT handling
mode-dependent (CERTIFICATE proceeds with a risk note); v1.0.0 keeps
non-critical SPLITs INCONCLUSIVE — that refinement is a v1.1 candidate.Meta-claims: the R1/R2 coverage questions are registered as new DOCTRINAL
claims (predicate coverage-of:<parent predicate>), adjudicated by the
jury within the remaining depth budget; their verdicts feed the policy
engine and the certificate like any claim's.
A first-round split that went through deliberation keeps a visible risk note ("first-round split; post-deliberation consensus" / "… consensus not reached") on its adjudication.
8.1 When the W1a majority stance on a claim contradicts a W2/W3 producer's
stance, a calibration.updated entry records delta −0.1 for that producer
identity. A producer agreeing with machine truth after a prior negative
records +0.05 (rehabilitation). A tied W1a majority records nothing.
8.2 factor = clamp(1.0 + Σ deltas, 0.5, 1.0) (unknown identity ⇒ 1.0).
The factor MUST be applied to W2/W3 confidence at record time. Calibration
MUST NOT touch tier or stance — a discounted juror's REFUTES is worth exactly
what it was, minus the trust. Meta-claims are excluded (their evidence is the
same round).
9.1 Gating: only after a first-round SPLIT, and only when policy
deliberation_rounds ≥ 1. Default 1; 0 disables.
9.2 Mechanics: each juror receives the OTHERS' first-round opinions (identity-labeled: identity, family, stance, confidence; rationale NOT forwarded) and may revise via its hook; without a hook it keeps its first-round opinion. Revised items carry fresh evidence-id salts.
9.3 Supersession honesty: first-round items STAY in the ledger;
deliberation.rounded records {claim_id, first_round, revised, consensus};
the divergence.flagged entry is never removed; adjudication uses the
post-deliberation set.
9.4 Replay parity and its scope guard. Offline verification MUST
adjudicate on the post-deliberation set: it MUST exclude exactly the
first-round item ids named by deliberation.rounded entries at or below
the certificate's anchored checkpoint (seq ≤ checkpoint_seq). Exclusions
from entries above the anchor MUST be ignored — the signed anchor pins that
prefix's chain hash, so pre-issuance deliberation is tamper-evident while a
post-issuance fake deliberation entry must never be able to erase refuting
evidence from a replay (v1.0.0 erratum D4: the unbounded form of this rule
was exploitable and is forbidden).
9.5 Certificate fields are reconciled, never trusted (errata D17, D18, D19, D20). A certificate carries two KINDS of field that are not independent evidence, and a conforming verifier must handle both.
(a) Derived summaries — fields that FOLLOW from the claim verdicts:
risk_level, score, divergence_summary (D17, D18). Recompute every
one from the verdicts validated in this same pass and reject any
divergence. The requirement is stated for the CLASS, not the field names:
any future field that summarizes verdicts inherits it without further
errata. Two fields that must agree, with no rule checking that they
agree, is the same hazard D12 and D15 closed; the exposure is a framing
attack (good work shown as risky, or a disagreement hidden as settled),
not a pass-through, because the verdicts a summary rests on are
themselves recomputed.
(b) Inputs to the verdicts — fields the replay USES rather than
derives: policy_ref and its duplicate policy_mode (D19). These are
strictly more dangerous than summaries, because forging one changes the
verdicts rather than only their description. A conforming verifier MUST
NOT replay under the certificate's own description of the policy: it MUST
reconcile policy_ref against the policy the ledger records the run
actually used (policy.decision carries policy_digest), MUST reject a
digest mismatch, and MUST fail closed when the claimed policy_id has no
recorded entry — an unrecorded policy is unfalsifiable and the first fix
attempt skipped the check entirely, which is the same class as D17 but
worse.
Scope note for (b), kept honest: this binds the certificate to the ledger's record of the policy, which is tamper-evident but only as meaningful as the policy the issuer actually ran. An issuer who runs a lenient policy and records it honestly gets a certificate that honestly reflects a lenient policy. The rule removes the ability to claim one policy and have run another; it does not make the claimed policy strict.
(c) The replay scope itself (D20). Reconciliation is only meaningful
if the replay reads the same ledger the anchor pins. Every entry the
replay trusts — claim.registered, evidence.recorded, the
deliberation.rounded supersession set, the policy.decision record —
MUST be restricted to the anchored prefix (seq <= checkpoint_seq). The
certificate's signed anchor pins that prefix's chain_hash, so entries
inside it are tamper-evident; entries appended after issuance are outside
the pin and MUST NOT reach the replay. The failure mode is asymmetric in
both directions: an appended entry can rescue an unattested certificate
(D19) or flip a verdict to match a forged one (D20 — a W1a SUPPORTS item
turns an honestly-INCONCLUSIVE claim VERIFIED, because the ladder gives
W1a priority over W2). key.enrolled lookup is exempt: next() takes
the first matching entry and the ledger is append-only, so the issuer's
enrollment always precedes a later duplicate.
10.1 Modes: CERTIFICATE (record only), GATE (fail-closed),
WATCH (observe-only — never blocks), HYBRID (gate semantics + full
record).
10.2 The decision is blocked iff critical_bad ∨ any_REFUTED ∨ blocking_flags
under GATE/HYBRID. Registered v1.0.0 flags: coverage-below-threshold,
divergence-split:{claim_id}, inconclusive-unresolved:{claim_id}. An
INCONCLUSIVE machine-checkable claim MUST surface a flag — a gate consumer
MUST be able to distinguish "passed clean" from "passed with an unresolved
machine claim".
Erratum D15, second half (§14.2): not every flag is a verdict.
meta-coverage-unconfirmed:{claim_id} is advisory — it records that a
juror declined to answer its own coverage question. A flag that blocks
IS a verdict, and letting an advisory one block hands the deciding vote
to the very dissenter §5.3 rule 2 refuses to honor. Measured before the
fix: 3/3 clean audits blocked under HYBRID with a jury that refutes
everything, because the jury's refusal to confirm coverage blocked the
audit it was refusing to cover. The exclusion is from blocking_flags
only — the flag stays visible in the decision, so a consumer that wants
to treat unconfirmed meta-coverage as a stop signal can, but the gate
itself does not. The fail-closed surface does not shrink: a refuted
TOP-LEVEL claim still blocks, critical-class claims still block; only
the juror's refusal stops deciding.
10.3 WATCH mode records watch.observed and never blocks; the flags are
computed identically. Errata (§14.2): v1.0.0 defines WATCH as a policy
mode over the same evidence ledger — it does NOT specify a streaming/realtime
transport; flag latency budgets for a stream (design §6.5) are a v1.1
candidate, deferred behind a demonstrated need; the divergence.flagged
and inconclusive-unresolved flags computed for WATCH claims are still
recorded as ledger entries, only the blocking consequence is absent.
Erratum D10 (§14.2): a reference transport candidate now exists
(veridict/watcher_stream.py) implementing append detection + per-
increment flag recomputation over the same §7 ladder, WITHOUT claiming
conformance to any latency budget — the normative latency sentence is
proposed in §14.2 and deferred to v1.1.
11.1 The certificate body carries: cert_id (sha256(task_id|artifact_digest)
[:24]), subject {artifact_digest, task_id, actor_identity}, policy_ref
(with the decision-relevant thresholds), claims [{claim_id, verdict_value,
divergence, evidence_ids}], jury_composition, disclosure_level,
divergence_summary, risk_level, score, scope_limits (MUST include the
honesty clause "claim coverage is heuristic, not exhaustive" as its first
element), ledger_anchor {checkpoint_seq, chain_hash}, signatures
(ed25519 over the canonical body-without-signatures).
11.2 Issuance order is normative: claims and evidence first, then a
checkpoint.anchored entry (pinning the chain hash), then
certificate.issued.
11.3 Verification outputs {valid, chain_valid, signature_valid,
verdicts_match, errors} and MUST check: chain integrity from the file
(including refusal on malformed lines); signature against a key enrolled in
the ledger; anchor (checkpoint exists at checkpoint_seq, its chain_hash
matches the signed body, and a matching certificate.issued entry exists at
seq ≥ checkpoint); evidence references (every evidence_ids entry must exist
in the ledger — defense-in-depth over the anchor pin); and verdict recompute
(replay §5 ladder over ledger evidence for each cert claim, under the
cert's policy_ref, with the §9.4 exclusion scope). Any failure ⇒
valid: false with named errors.
11.4 The verifier MUST import Core-level modules only — never jury/verifier implementations.
12.1 On ESCALATED, the system MUST issue a dossier.issued entry after
escalation.requested. The dossier is a VIEW over the ledger — never a
separate truth. Fields: schema_version, dossier_id
(sha256(claim_id|artifact_digest)[:16]), claim {…, verdict, rung: R3,
divergence}, summary_page (plain language), risk_frame ("If the REFUTES
side is right: …" built from refuting evidence rationales, with an explicit
fallback), the FOUR options — accept_with_risk, demand_rerun,
narrow_claim, reject — each with an honest consequence, default
("response window expiry triggers policy.fail_safe (R4)"),
evidence_links (claim id + evidence ids), response_window_hours.
12.2 Every dossier sentence MUST link to ledger entry ids.
12.3 escalation.resolved records the human's decision (one of the four),
decided_by, and an optional risk note, authored by kind human. An
unknown decision MUST be rejected.
12.4 Fail-safe. Response-window expiry MUST be recorded as
policy.fail_safe with consequence
gate_stays_blocked_or_certificate_stamped_unresolved. Silence must never
converge to acceptance.
13.1 Certificates are point-in-time statements: evidence appended after issuance legitimately flips a replay verdict (that is tamper-evidence working). Consumers MUST re-verify against the CURRENT ledger.
13.2 Jury heterogeneity is a policy minimum (min_jury_families), not proof
of independence; the diversity statement travels in jury_composition.
13.3 Local-jury mode (air-gapped deployments) is conformant if heterogeneity is preserved and the composition is disclosed.
13.4 Formal verification of the Core is a Phase 3 stretch goal and is NOT a v1.0.0 conformance requirement. Partial progress exists (erratum D11: the §7 ladder's five tier rules are machine-checked in Lean 4), but that covers the adjudication logic only — the hash chain, signature layer, and juror honesty remain unproven. Until those are covered too, the core's honesty rests on the canonical-form discipline and the replay algorithm — a stated residual risk.
14.1 The entry-type registry and the flag registry are append-only. Adding a type is minor (patch the list, bump patch version). Changing a payload schema, a digest preimage, or a tier rule is MAJOR and requires a migration note.
14.2 Errata. v1.0.0-draft erratum D4 (2026-09-10): replay exclusion of
superseded deliberation items was unspecified; §9.4 fixes the scope guard.
Erratum D5 (2026-09-10): the entry-hash preimage originally omitted ts and
schema_version; §2.3 now binds every stored field (chain format break —
≤0.3.0 ledgers regenerate) and mandates ISO-8601 string timestamps. Erratum
D6 (2026-09-10): §7's R4 originally read "anything else ⇒ INCONCLUSIVE",
contradicting the reference ladder; §7 now states the doctrinal-consensus
rule normatively (VERIFIED on unanimous SUPPORTS, REFUTED on any doctrinal
REFUTES even within a majority, INCONCLUSIVE on non-critical SPLIT / W3-only
/ no evidence); the design's mode-dependent SPLIT handling (§6.2 R2) remains
a v1.1 candidate. Erratum D7 (2026-09-10): §9.2 is now explicit — a revision
hook that is absent or errors degrades to keep-opinion; a juror MUST NOT be
dropped from the revised basis, which would silently erase its first-round
REFUTES. Erratum D8 (2026-09-11, certificate mutation differential): the
spec-only verifier crashed (IndexError) on a certificate whose
signatures array was empty, instead of returning an invalid verdict — a
hostile certificate must produce a verdict, never a crash (fail-closed).
Fixed in examples/spec_verifier.py; pinned by 16 certificate mutations
in tests/test_cert_mutation_parity.py where both verifiers agree.
Erratum D9 (2026-09-11, nightly hardening): the static analyzer's file
walk did not skip hidden directories — creating a second interpreter
environment (.venv312) inside the repository produced thousands of
forbidden-call findings from third-party packages, and the §6 fail-closed
gate correctly BLOCKED the release on the resulting REFUTES. The gate's
behavior was correct; the scanner was not. The walk now skips all
dotted directories plus known build directories, pinned by
tests/test_verifiers.py::test_static_analyzer_ignores_hidden_dirs.
Erratum D10 (2026-09-11, issue #3): the §10.3 WATCH transport gap now has
a reference implementation candidate — veridict/watcher_stream.py
(poll-based append detection, per-increment §10.2 flag recomputation
through the SAME §7 ladder as the batch engine, never blocks, torn
trailing lines held back). It does NOT claim conformance to a latency
budget yet: no normative latency sentence exists in v1.0.0 and none is
added by an implementation unilaterally. The v1.1 candidate sentence
proposed: "A conforming WATCH transport MUST surface the flag set
recomputed over an appended entry within a bounded detection interval
declared by the deployment, and MUST NOT block, alter, or append to the
audited ledger." Ratification is deferred to v1.1 (§10.3 errata).
Status (2026-09-12): the ratification draft now exists — see
2026-09-12-veridict-standard-v1.1-delta.md
(amendment A1 proposes ratifying this exact sentence; the delta stays DRAFT
until an independent implementation exercises it). Erratum D10 remains OPEN
until v1.1.0 is ratified. Erratum D11 (2026-09-19, issue #8): the §7
ladder's five tier rules are now machine-checked in Lean 4
(proofs/ladder/Ladder.lean, kernel-checked, no bespoke axioms — the
agree decision function depends only on propext, Classical.choice,
and Quot.sound). The model is cross-checked against the shipped Python
ladder over all 28,080 bounded evidence configurations
(proofs/ladder/TruthTable.lean, regenerated from the reference
implementation per run by scripts/export_lean_truth_table.py), so
model/implementation divergence inside the sampled domain fails the
proof CI. Limits, stated plainly: this is a proof about the ladder's
logic, not about the system. It says the decision function admits no
silent pass and no silent gap for the configurations sampled; it does
NOT prove the hash chain, the signature layer, or that a deployed
juror reported honest evidence — those remain §11.3's verification
business, not §7's. A reviewer asking "is the ladder sound?" now gets a
checked artifact; a reviewer asking "was this audit truthful?" still
gets a certificate to recompute.
14.3 The standard is Apache-2.0 (D8: spec + core + offline verifier are open; hosted platform, certification authority, enterprise integrations are commercial).
Erratum D12 (2026-09-19, jury composition is normative after all): the
reference implementation requires a jury of at least two providers drawn
from at least two distinct family values before it will issue a
certificate, but §5 never stated that requirement as normative text — §5.2
defines only tier semantics. An implementer reading this standard alone
could lawfully build a single-provider jury that self-reviews its own
output, which is exactly the self-preference the requirement exists to
forbid. This closes that gap: a conforming implementation MUST NOT issue a
certificate on a jury of fewer than two providers or fewer than two
families. The reference implementation additionally excludes any juror
from the audited author's own model family before this count and fails
closed rather than shrinking to a conflicted panel — implementations MUST
do both. Amendment A2 proposes ratifying these sentences; the delta stays
DRAFT until an independent implementation exercises it (issue #1's exit
criterion). Note the limit: these rules constrain composition, not
honesty — two families do not prove either juror reported truthfully,
which remains §11.3's business. Status (2026-09-20): the rule is
normative here in §14.2 (this erratum) and enforced in the reference
implementation at Jury.__init__, which refuses a panel of fewer than
two providers or two families BEFORE any certificate is issued —
measured, not asserted. The ratification text now exists in
2026-09-12-veridict-standard-v1.1-delta.md
(amendment A2, same shape as A1); it stays DRAFT until an independent
implementation exercises it. Until then a verifier checking this rule
reads it from this erratum, not from a ratified §5.
Erratum D13 (2026-09-19, entry_type taxonomy was implicit): §2 lists
entry_type as a field but never enumerated its permitted values, and the
JSON Schema described the registry as "open" with only an example list —
which omitted task.started, watch.observed, escalation.requested,
escalation.resolved, calibration.updated, gate.blocked, and
actor.output, all of which the reference implementation emits. An
implementer reading the text alone could not know these exist. The schema
now enumerates the core types in full. The registry remains open by
design, but extensions MUST be namespaced extension.*, so a verifier
facing an unknown type abstains instead of silently misparsing it — the
same hazard Sester's ERRATUM-K0.2 closed on their side for their event
taxonomy. Semantic consumers (any code that totals or nets over entries)
MUST understand the core types first: a charge and its refund are
distinguished by entry type, not by a payload field, and a consumer that
ignores the taxonomy silently diverges.
Erratum D14 (2026-09-20, R4 did not respect W1b outranking doctrine): R4's text reads "with no W1a/W1b on the claim ... ANY doctrinal REFUTES ⇒ REFUTED". The guard is correctly stated for the no-W1 case, but the reference implementation applied the REFUTED branch without re-checking for W1b support, so a claim with deterministic static SUPPORT at W1b could still be turned REFUTED by a juror's dissent. That contradicts §5.3 rule 1 (strict ordering W1a > W1b > W2 > W3): W1b is machine truth, and a doctrinal opinion cannot outrank it, only fail to corroborate it. The implementation now returns VERIFIED when W1b SUPPORTS and only doctrine REFUTES, with the dissent recorded in the divergence field rather than promoted into the verdict. The old behavior was not a security hole — it erred toward blocking, i.e. toward fail-closed — but it converted a juror's uninformed refusal into a decision. The real-LLM canary measured this exactly: 3B jurors REFUTED claims with the rationale "the digest provides no information about the function", and every clean case in the corpus was blocked. Two changes follow: (a) the R4 branch now honors W1b; (b) jurors now receive the artifact's source text, bounded, because a juror that cannot see the code it is judging is not auditing.
Erratum D15 (2026-09-20, coverage meta-claims were decisive at the
gate): R1/R2 open a DOCTRINAL meta-claim ("does the W1a evidence
actually cover this claim?") precisely because a juror dissented. That
meta-claim's only evidence is the same jury round — it carries no W1
truth of its own. The policy engine's blocking rule scanned all claims
including meta-claims, so a juror's refusal to answer its own coverage
question blocked the gate. The dissenter thereby decided the outcome,
nullifying §5.3 rule 2 (doctrine can never overturn W1a) by indirection.
Meta-claims are now recorded, adjudicated, and shown in the certificate,
and a REFUTED meta-claim is surfaced as meta-coverage-unconfirmed:{id}
rather than promoted to a block. Top-level refuted claims still block as
before. This is a narrower fail-closed surface, not a weaker one: a
genuine top-level machine refutation still blocks, and an unconfirmed
coverage question is visible in the certificate and in the run's flags
instead of silently deciding it.
Erratum D16 (2026-09-20, the verified-blind spot is real, and it is the honest one): §5.3 rule 2 says doctrine can never overturn W1a machine evidence. That rule is correct and stays. But the real-LLM canary made the consequence concrete rather than theoretical: a defect whose own tests still passes has W1a SUPPORT, so a juror that correctly spots the defect in the code cannot turn the verdict. The 25-class corpus is deliberately built that way — a bug that fails its tests is already caught by W1a and needs no juror — so of 25 defect classes the end-to-end audit catches 2. This is not a bug in the ladder; it is the design's blind spot, named.
The blind spot is in the DESIGN, not the model. Measured with a judgment sonde (scripts/measure_jury_judgment.py) that asks each juror its verdict on the intent claim given the artifact's source, deliberately NOT routed through the ladder: 42/48 measurements correctly REFUTE the defective artifacts (23/24 for qwen2.5:3b, 19/24 for llama3.2:3b), against 2/4 false refutations on clean code. The model sees most of the defects; the verdict still says VERIFIED, because W1a SUPPORT is machine truth and a 3B model that calls half the clean code it sees defective must not be able to turn a verdict on its own. The false-refutation rate IS the justification for rule 2, measured rather than asserted — and the catch rate is the cost of it, measured rather than assumed.
Two consequences, both stated so nobody reads a certificate as more than it says. (a) A VERIFIED certificate is evidence that machine checks passed and jurors did not dissent on the artifact — it is not a proof the artifact is correct, only of what was checked. §13 already says this normatively; D16 records the measurement that makes it load-bearing. (b) The right response to a juror dissent the machine evidence contradicts is not to ignore it and not to let it decide, but to escalate the coverage question — which is what R1's meta-claim does, and what D15 keeps visible. A consumer who wants the dissent to block sets the claim's class critical and gets ESCALATED; a consumer who wants machine truth to decide gets VERIFIED with the dissent recorded. The choice is policy, and both are conforming.
Erratum D17 (2026-09-20, derived summary fields were unverifiable —
Tamga ERRATUM-A2 class): a certificate carries summary fields that
FOLLOW from the claim verdicts — risk_level and score. §9 required
the verifier to recompute the verdicts themselves, and the implementation
did. But nothing required it to recompute the summaries, and the reference
implementation did not: it validated each verdict_value against the
ledger and then trusted risk_level and score as stored. A field that a
consumer decides on (the settlement policy gates release on risk_level)
was unconstrained by any check.
The consequence is asymmetric, and the asymmetry matters more than the
bug. An attacker cannot hide a bad result this way: to make the true risk
high a claim verdict must be REFUTED, and verdicts ARE recomputed, so
the mismatch fires. What an attacker CAN do is the reverse — write
risk_level: "high" over a fully-VERIFIED certificate, or score: 0.0
over a high one. That is a framing attack, not a pass-through: it makes
good work look bad, or inflates risk to break a settlement that should
have released. It cannot make bad work pass. The honest statement of the
exposure is "summary fields could be forged to mislead a consumer", not
"bad code could be certified".
§9 now requires: a conforming verifier MUST recompute every derived summary field from the verdicts it validated, and reject any divergence. The reference verifier does. Two fields are derived today; a v1.1 field that summarizes verdicts inherits the same rule without further errata, because the requirement is stated for the class, not the field names. This is the same hazard as D12 and D15 in kind — two fields that must agree, with no rule checking that they do — and was found by the same method: an independent verifier (Tamga) attacking the standard rather than reading the code.
Erratum D18 (2026-09-20, divergence_summary was the second member of
D17's class): sweeping the class D17 names found another member in the
reference implementation. divergence_summary maps each claim to its
adjudication divergence (UNANIMOUS / SPLIT / ...), and the verifier
checked the verdict values but not this per-claim summary. Same hazard,
same fix: it is recomputed from the same replay the verdicts come from.
Why it matters more than a cosmetic mismatch: a SPLIT is the trigger for deliberation (§9.1), and an honest recorded disagreement is one of the few things a certificate exists to surface. Forging it to UNANIMOUS on a certificate the verifier accepts would present a contested finding as settled — the framing direction of A2', hiding disagreement rather than hiding a bad verdict. It cannot manufacture a REFUTED verdict; the verdict check still fires. It can hide the disagreement.
One member of the class is deliberately NOT recomputed: jury_composition.
§9.5 applies to fields whose source IS the replayed evidence. The jury's
family composition is established at issuance and enforced there (D12:
Jury.__init__ refuses a panel of fewer than two families before any
certificate is issued — measured, not asserted), but the ledger carries
no record of jury composition independent of the evidence producers, so
a verifier has nothing to recompute it from. The attack path is closed
at the source instead. A future version that records jury composition
into the ledger moves this field under §9.5 with no further erratum,
because §9.5 is stated for the class.
Erratum D19 (2026-09-20, policy provenance — the hazard D17's sweep found, and the one that is an INPUT not a consequence): sweeping the class §9.5 names, three more fields were unconstrained in the reference implementation, and one of them is not like the others.
policy_mode is the same value stored twice (top level and inside
policy_ref), and the two copies could disagree. Boring duplication —
recompute one from the other and reject the mismatch.
policy_ref is the interesting one. Unlike risk_level or
divergence_summary, which FOLLOW from the verdicts, policy_ref is used
to BUILD the replay policy — it is an input to the verdicts, not a
consequence of them. A verifier that reads the policy from the
certificate replays under whatever policy the issuer claims to have
used, which means the issuer is trusted for the policy, not just for the
arithmetic. And the first fix attempt had a fail-open hole exactly here:
the check looked up a matching policy.decision entry in the ledger and
only compared digests when one existed. A cert claiming a policy_id the
ledger never recorded therefore SKIPPED the check — unfalsifiable by
construction. That is the same class as D17 but worse, because a forged
policy can change verdicts, not just summaries: claim a policy with no
critical classes and a tolerant divergence threshold over a ledger whose
real policy was strict, and the replay adjudicates leniently.
§9.5 now requires the check to fail closed when no recorded policy
matches the claimed policy_id, and to reject a digest mismatch. The
verifier reconciles the cert's policy against the policy the ledger
records the run actually used (policy.decision carries policy_digest),
rather than trusting the certificate's own description of it.
Scope note, kept honest: this binds the certificate to the ledger's record of the policy, which is tamper-evident but is only as meaningful as the policy the issuer actually ran. A issuer who runs a lenient policy and records it honestly gets a certificate that honestly reflects a lenient policy. The fix removes the ability to claim one policy and have run another; it does not make the claimed policy strict.
Erratum D20 (2026-09-20, the replay itself was unscoped — D19's class,
found one layer deeper): D19 reconciled the cert against the ledger, but
the reconciliation only matters if the replay reads the same ledger the
anchor pins. It did not. Three queries fed the replay — claim.registered,
evidence.recorded (twice) — and all three scanned the whole ledger,
while the analogous D4 guard on deliberation.rounded already restricted
itself to the anchored prefix. Same class as D19, same fix, one layer down.
The shape is worth recording because the first PoC attempt against it
failed, and the reason it failed is the reason the bug is real. The
ladder gives W1a machine evidence absolute priority over W2 jury
opinion, so injecting a post-issuance W2 SUPPORTS item cannot move a
verdict — the ladder is already robust there. What does move is a W1a
item: a claim the honest run adjudicated INCONCLUSIVE (no evidence) can
be flipped to VERIFIED by appending a TEST_EXECUTION SUPPORTS entry
after the checkpoint. Measured: without the scope guard, replay recomputes
VERIFIED over a cert that says INCONCLUSIVE, and verification rejects on
the mismatch (detection, not prevention — and a cooperating issuer who
ships the cert with the forged verdict gets silent acceptance).
§9.5 now scopes every replay query to the anchored prefix
(seq <= checkpoint_seq), the same boundary D4 uses. The verifier sees
exactly the in-prefix state the signed anchor pins; post-issuance entries
are invisible to it in either direction — they can neither rescue an
unattested cert (D19) nor flip a verdict (D20). key.enrolled lookup
needs no guard: next() takes the first matching entry and the ledger is
append-only, so the issuer's enrollment always precedes an attacker's
duplicate.