# FORAY Verifier Specification

**Document Version:** 0.2 (supersedes v0.1 — preserved unmodified in this directory, per the record-doc versioning convention)
**Created:** 2026-07-24T00:00:00Z
**Status:** DRAFT — F-15 gate artifact, HOLD for Operator read. This document is the independence artifact: review finding F-15 (register v0.2, adopted from review R2-A) names the Verifier Specification + offline conformance suite as the single largest credibility gap — "verification requires no access to DUNIN7" claimed ahead of the artifact that makes it true. This document and the conformance suite it specifies (`foray-api/conformance/`) are that artifact.
**Normative sources, in authority order:**
1. `spec/amendments/FORAY_Protocol_v4_2_F_Wire_Migration_Amendment-v0_7.md` (ACCEPTED, in force 2026-07-24) — the wire this verifier verifies: §2 breaking-change table, §4 migration function, §5 dual-version acceptance, §7 canonical serialization at both layers, §8 additions.
2. `spec/schema/foray-transaction-v4_2.schema.json` — the 4.2 canonical schema, sole owner of wire names (D8), with `docs/decisions/foray-42-schema-stage-contract-v0_1.md` stating the enforced-vs-delegated split this specification restates as verification law (§5.1).
3. FORAY Protocol v4.1.5 Transaction Conformance Amendment — the canonical **4.1** shape the migration converts *from* (permanent legacy arm).
4. FORAY Protocol v4.1.3 Batch Anchoring Amendment §4–§6 — the ABH struct, tree rules, and domain separation; byte encoding ratified into the lineage at amendment §7.2 from `spec/foray-abh-serialization-v0_2.md`.
5. FORAY Protocol v4.1.4 Anchor Assurance & Chain Plurality Amendment — assurance classes, chain registry, repository classes (carried for the anchor-infrastructure findings, §4.4/§9).

**Informative (never required):** the reference implementation (`foray-api/lib/foray-42-canonical.mjs`, `foray-validate-4-2.mjs`, `foray-migrate.mjs`, `foray-tree.mjs`, `foray-abh.mjs`; `foray-api/conformance/reference-verifier.mjs`), the WP-1 reference verifier (`foray/verifier/`), and this document's worked derivations. **An implementer must be able to proceed from this specification, its normative sources, and the conformance-suite vectors alone.** Where this document and a normative source conflict, the source governs; conflicts are defects to be reported, never silently resolved.

---

## 1. Why this document is the product

Every guarantee in the protocol is realized — or squandered — inside the verifier. A verifier that accepts non-canonical bytes, skips the domain-separation byte, tolerates float error in the allocation sum, or displays ✓ without stating what the ✓ attests converts the architecture into theater. This specification exists so that "FORAY verified" has exactly one meaning — and so that meaning is checkable by a party with **no access to DUNIN7 code, services, or people**. Everything a conformant verifier needs is: this specification, the normative documents above (all published in this repository), the conformance-suite vectors, and — for anchor-level classes only — the public chain's own data.

This document supersedes Verifier Specification v0.1 (2026-07-11), which was drafted against the 4.1 wire and the proof-bundle input format. What carries forward from v0.1: the outcome taxonomy (§3), the mandatory-display discipline (§7.3), the threat lineage N1–N15 (re-keyed in §4.5), and the test-the-tests requirement (§8.4). What is new: the 4.2 record and legacy-migration input classes (v0.1 verified anchors, never records), the byte-exact serialization procedures of §6, and the offline conformance suite as a shipped artifact rather than a plan.

## 2. Input classes

A verification request presents exactly one of four input classes. A conformant verifier states which class it evaluated; a submission fitting no class is **UNSUPPORTED**, never silently approximated.

| Class | Input | Question answered |
|---|---|---|
| **R** — 4.2 record | A record with `schema_version: "4.2"` — as parsed JSON (data-model judgment) or as bytes (data-model + byte-conformance judgment) | Is this a conformant 4.2 FORAY record? Are these bytes its one legal spelling? |
| **L** — legacy record | A record with `schema_version: "4.1"` or `"4.0"` | Is this record valid against its own version's canonical shape, and what 4.2 record does the frozen migration chain deterministically produce from it? |
| **A** — anchored record | A record's exact bytes + its `leaf_salt` + its lane name + direct-mode header context (§5.4), against an on-chain payload | Do these bytes, and no others, reproduce the 32-byte value actually anchored on the public chain? |
| **H** — batch header | An ABH `batch_header` — as field values (encode direction) or as bytes (decode direction) | Is this the one legal byte encoding of this header, and what `anchored_value` does it commit to? |

Classes compose: a full anchored-evidence verification is L (if legacy) or R, then A. Class H is A's serialization core, exposed as its own class because the amendment §7.2 makes the header layout independently normative and independently testable (vector family ABH).

The v0.1 proof-bundle class (Merkle path + header echo + chain/registry/repository context) is **carried, not retired**: batched-mode path verification remains governed by v4.1.3 §5–§6 and v4.1.4, with its executable vector suite in `verifier/vectors/` (WP-1). This document restates its findings in the unified taxonomy (§4.4–§4.5) and adds nothing to and removes nothing from its chain-infrastructure obligations. The classes above are the ones the 4.2 wire break changed or created; the bundle class's record-independent machinery (tree math, registry, repository, streams) is untouched by the break (amendment §9.2–§9.3).

## 3. Outcome taxonomy (exhaustive; no other verdicts exist)

Carried from v0.1 unchanged in membership:

| Outcome | Meaning at v0.2 |
|---|---|
| **VALID** | Every mandatory check for the evaluated class passed |
| **INVALID** | A structural, semantic, byte-level, or anchor-level check failed (sub-verdict **INVALID-CHRONOLOGY** carried for the bundle class, v4.1.3 §7.7) |
| **DISPUTED** | Cryptographically valid but named by an anchored revocation/DISPUTE record (bundle class, v4.1.3 §8) |
| **MATERIAL_UNAVAILABLE** | Anchor verified on-chain; required holder-side material (body bytes, salt, header context) missing — the report MUST name the responsible custodian. Never conflated with INVALID: absence of proof material is not evidence of tampering |
| **PROVISIONAL / PROVISIONAL-STALE** | Repository-class outcomes (v4.1.4 §5), bundle class only |
| **DEGRADED-CHAIN** | De-registered chain, no history checkpoint (v4.1.4 §3C.5), bundle class only |
| **UNSUPPORTED** | Unrecognized class, unknown `schema_version` (§5.3 routing), unregistered chain, or a feature this verifier cannot evaluate — never silently approximated |

**Labels (orthogonal to outcomes, always displayed):** **legacy** (class L: record validated against its own version's shape and converted; source version named), **non-conformance notice** (class L after the conformance terminus — OQ-13: the record is *accepted*; its *production* is non-conformant; the notice's presence is configuration fixed at D9 site promotion), **LEGACY / LATE_RECORDED / escape-anchored** (bundle class, carried from v0.1).

Classes R, L, A, and H produce only VALID / INVALID / MATERIAL_UNAVAILABLE (A only) / UNSUPPORTED. The remaining outcomes belong to the bundle class's chain and repository machinery.

## 4. Finding taxonomy

Findings are the reasons attached to a non-VALID outcome (or to warnings/notes beside a VALID one). Four planes. Every finding carries: its code, the JSONPath or byte offset of the culprit, and prose naming the specific violation — **named-culprit discipline**: "invalid" without the culprit named is itself non-conformant output.

### 4.1 Structural plane (codes E1, E2, E3, E11; migration codes MIG_*)

The record's shape against the 4.2 canonical schema — the schema-enforced half of the stage contract:

| Code | Finding |
|---|---|
| E1 | `schema_version` missing, mismatched, or unroutable |
| E2 | No components: all four component arrays empty or absent |
| E3 | Structural violation of the 4.2 canonical shape: type, requiredness (including F2 presence/non-emptiness, OQ-7), pattern (including F18's `^hash_[0-9a-f]{32}$` and F3's `sha256:`-prefix form mechanism, OQ-8), enumeration, magnitude shadow (±10¹⁵) |
| E11 | An illegal key, with the specific ruling named: a §2.1 retiree (`foray_core`, `terms`, `description`, `entity_hash`, `dependencies`, `condition`, `period`, `compliance_flags`), any 4.1 wire spelling, any `foray_*` register name used as a wire key (register leak, D-W), a non-catalog F-code (catalog is F1–F29), or residue outside F25's two ruled positions (D6/OQ-6) |
| MIG_VERSION, MIG_INPUT, MIG_CORE_SHAPE, MIG_COLLISION, MIG_F25_COLLISION, MIG_F2_SOURCE, MIG_ELEMENT_SHAPE, MIG_PERIOD_SHAPE, MIG_D5_TYPE, MIG_CONDITION, MIG_UNKNOWN_KEY | Class-L migration failures — the §5.3 chain fails loudly with the named code and **no partial output** (amendment §4) |

### 4.2 Semantic plane (codes E4, E5, E7, E8, E9, E10, E12, E14; note N1)

The delegated checks the schema cannot express — stage-contract §2, restated in §5.2 as law:

| Code | Finding |
|---|---|
| E4 | Anticipation missing F27/F28 (named-culprit: superseded spellings `expected_date`/`datetime`, `probability_factor`/`probability` are called out when present) |
| E5 | Action missing F9 (named-culprit: `amount_settled`/`actual_amount`) |
| E7 | Dangling F22/F23/F24 reference |
| E8 | Allocation-sum violation: Σ F21 entry amounts ≠ F9 as exact decimals (§6.2) |
| E9 | F21 `ref` unresolvable or `ref_type` mismatch |
| E10 | `extension` object anywhere in transaction JSON (envelope-only, never record) |
| E12 | Numeric value exceeding 15 significant decimal digits (OQ-2) |
| E14 | Monetary fact with no resolvable currency: component F8 else transaction F8, chain exhausted (OQ-9) |
| N1 (note) | F2 == F1 — legal, defined, dual-reading (native origination / migrated legacy); the `legacy` label disambiguates |

### 4.3 Byte plane (codes B1–B4)

Byte-level conformance of a record's claimed canonical form (§6.1). On a **transport** submission (JSON whose member order and whitespace are not asserted canonical), B1 is reported as warning W3 and B2–B4 are not evaluated — a transport submission is judged at the data model. On a **canonical-bytes** submission (class R bytes, MIG goldens, any hashing path), every B-finding is INVALID:

| Code | Finding |
|---|---|
| B1 | Non-canonical number spelling: exponent form, trailing fractional zeros, `-0`, or any spelling other than the shortest round-trip plain-decimal (§6.1 step 2). *(Superfluous leading zeros and explicit `+` are additionally excluded by JSON grammar itself.)* |
| B2 | Member-order violation: F-coded members not first in ascending numeric F-order; envelope tail not in the ruled enumeration; `id` not last in a component element; a non-lexicographic interior (§6.1 step 3) |
| B3 | Encoding violation: insignificant whitespace, non-JCS string escaping, non-UTF-8 bytes |
| B4 | One-spelling violation: the submitted bytes are not byte-identical to the canonical serialization of the value they parse to (the umbrella check — B1–B3 name the specific cause when identifiable) |

### 4.4 Anchor plane (codes A1–A8)

| Code | Finding | v0.1 lineage |
|---|---|---|
| A1 | Leaf-format/domain-separation violation: leaf ≠ SHA-256(`0x00` ‖ tx_hash ‖ leaf_salt) with tx_hash and leaf_salt each exactly 32 bytes; internal node ≠ SHA-256(`0x01` ‖ L ‖ R); any raw/64-byte leaf | N1 |
| A2 | Header-binding mismatch: SHA-256(`0x02` ‖ serialize(header)) ≠ the anchored 32-byte value | N2 |
| A3 | Padded-duplicate membership claim: `leaf_index ≥ true_leaf_count` (bundle class) | N4 |
| A4 | ABH decode violation: illegal `escape_flag` byte, `header_version` `0x00`, truncated segments, trailing bytes, segment-order violation, duplicate (`stream_id`, `first_seq`) pair (§6.3) | N15 |
| A5 | Commitment mismatch (class A): recomputed `anchored_value` ≠ the on-chain payload | — (new at v0.2) |
| A6 | Required holder-side material missing (salt, body bytes, header context) with the anchor itself verified → outcome MATERIAL_UNAVAILABLE, custodian named | N7 |
| A7 | Registry/dispute finding → DISPUTED; replay/duplication → INVALID (bundle class) | N5, N6 |
| A8 | Stream/continuity/chronology finding (GAP/FORK/INVALID-CHRONOLOGY), repository split-view/staleness, chain-access-independence failure (bundle class) | N8–N11, N14 |

### 4.5 The N1–N15 lineage, re-keyed

v0.1's negative-vector suite maps forward as follows. "Suite home" names where the executable vector lives today: **F-15** = `foray-api/conformance/` (this document's suite), **WP-1** = `foray/verifier/vectors/` (carried, still runs offline against canned contexts).

| v0.1 | Attack encoded | v0.2 code | Suite home |
|---|---|---|---|
| N1 | 64-byte internal-node-as-leaf | A1 | WP-1 (structural 32-byte law also enforced by construction in class A) |
| N2 | `true_leaf_count` tampered vs anchored header | A2 | WP-1; F-15 ANCHOR negatives exercise the same binding via A5 |
| N3 | Dictionary attack against a sibling digest | documented, not runtime (unchanged) | WP-1 (`N3-dictionary-attack.md`) |
| N4 | Proof for a padded duplicate | A3 | WP-1 |
| N5 | Revoked-in-window key | A7 → DISPUTED | WP-1 |
| N6 | Duplicate `(submitter, submitter_seq)` | A7 → INVALID | WP-1 |
| N7 | Withheld blob/salt, anchor genuine | A6 → MATERIAL_UNAVAILABLE | WP-1; F-15 states the salt-handling law in §5.4 |
| N8 | Repository split-view pair | A8 | WP-1 |
| N9 | Overdue checkpoint | A8 → PROVISIONAL-STALE | WP-1 |
| N10 | Backdated stream insert | A8 (GAP/FORK) | WP-1 |
| N11 | Later-anchored `_ref` | A8 → INVALID-CHRONOLOGY | WP-1 |
| N12 | Missing `extension` vs `ext_version: 0` | envelope: A-plane (WP-1); record: E10 | WP-1 + F-15 VAL |
| N13 | Unknown nonzero `ext_version` | preserved, never rejected (unchanged) | WP-1 |
| N14 | Single-RPC-source proof | A8 (verifier conformance failure) | WP-1 |
| N15 | Serialization edge cases | A4 + B1–B4 (the 4.2 wire made both layers byte-normative) | **F-15** (ABH + CS families) |

## 5. Verification algorithm, per class

Short-circuit is permitted only into a failing outcome — never into VALID. Every check below is mandatory for its class.

### 5.1 Class R — a 4.2 record

**R-1. Parse.** The submission must parse as a JSON object. Non-object → E3, INVALID.

**R-2. Version routing.** `schema_version` must be exactly `"4.2"` (const-pinned, OQ-3). `"4.1"`/`"4.0"` → class L. Anything else → UNSUPPORTED (E1 named).

**R-3. Structural walk** — the schema-enforced half of the stage contract, restated as law. At every fact-bearing object (the transaction top level and each component element):

- Every substance key matches `^F[0-9]+$` and is in the ratified catalog F1–F29 at a scope the amendment §2 table permits; every other key is a member of the ruled envelope set (transaction: `schema_version`, `timestamp`, the four component arrays, `attestations`, `component_hashes`, `merkle_root`, `blockchain_anchor`, `audit_data_anchor`, `privacy_metadata`; component element: `id` alone). Any other key → E11 with the specific ruling named (§4.1).
- Required at transaction level: F1, F2 (non-empty, OQ-7), F3, F7, F8, F26, plus the envelope requireds (`schema_version`, `timestamp`, the component arrays present per the 4.1-parity carry, `component_hashes`, `merkle_root`, `blockchain_anchor`). Arrangement: `id`, F4, F5, F11. Accrual: `id`, F22. Anticipation: `id`, F22, F23. Action: `id`, F22, F23, F24.
- F3 single-form (OQ-8): cleartext, or salted digest matching `^sha256:[0-9a-f]{64}$`; a malformed reserved-prefix value fails both arms → E3.
- Formula typing (D5): F18 `^hash_[0-9a-f]{32}$`, F19 object, F20 number → E3 on violation.
- F25 at exactly two positions (D6/OQ-6), object-valued.
- F21 entries closed: `ref`, `ref_type`, `amount`, `currency` (+ ruled optionals per the schema); an extra entry key is residue outside F25 → E11.
- Every wire number inside the ±10¹⁵ magnitude shadow → E3.
- **Deliberate non-checks, stated so silence is inspected:** F5 party-entry interiors are open (protected/hashed party forms are catalog-cadence work — schema-stage finding §4.1); F28 carries no ruled range; F19 member values are unconstrained.

**R-4. Semantic checks** — the delegated half, all eight:

1. Component count ≥ 1 (E2).
2. Anticipation F27 (string) and F28 (number) present, Action F9 (number) present — deliberately not schema-required so the culprit is named, superseded spellings called out (E4/E5).
3. F22/F23/F24 entries resolve to existing element `id`s (E7).
4. F21 `ref` resolves and `ref_type` matches the referent's actual component kind (E9).
5. Allocation sum: for every Action with non-empty F21, Σ entry `amount` == F9 **exactly, as decimal values** — decimal-string or scaled-integer arithmetic mandatory, binary-float comparison forbidden, no tolerance (E8). Operands are the submission's raw literals when bytes are available, else the canonical spelling of the parsed values (exact within the 15-digit profile).
6. Decimal profile: every numeric value — residue and envelope interiors included, they are hashed bytes too — at most 15 significant decimal digits (E12).
7. Currency resolution: every monetary fact (transaction F7; Arrangement/Accrual F7; Anticipation F10; Action F9) resolves component-F8-else-transaction-F8; exhausted chain → E14. Resolution only — a verifier never materializes an inherited F8.
8. No `extension` object anywhere in the record (E10). F2 == F1 → note N1, never an error.

**R-5. Byte conformance** (when bytes are submitted as canonical): serialize the parsed record per §6.1; the result must be byte-identical to the submission → B4 (with B1/B2/B3 naming the specific cause). On transport submissions, number-literal spelling alone is checked against the raw bytes and reported as W3 warnings.

**R-6. Verdict** — outcome plus every applicable finding, warning, and note. Hiding any is non-conformance (§7.3).

### 5.2 Class L — a legacy record plus the frozen migration chain

**L-1. Route.** `"4.0"` → the v4.0→v4.1 hop (base spec §7.2: singular `*_ref` wraps to plural arrays, missing ref arrays materialize as `[]`, restamp) — then continue as `"4.1"`.

**L-2. Own-shape validation first.** The (possibly hopped) record validates against the **4.1 canonical shape** (v4.1.5). Failure → INVALID with the 4.1-lineage findings, `legacy` label, conversion **not attempted** (amendment §5.2: legacy records validate against their own version's canonical shape first).

**L-3. Migration** — `migrateV41toV42`, amendment §4 steps 1–10, restated: restamp to `"4.2"`; flatten every `foray_core` (OQ-4) — a key present both inside and beside a wrapper is MIG_COLLISION; re-key per the §2 table **scope-aware**; F2 := legacy `transaction_id` (missing/empty → MIG_F2_SOURCE); `entity` cleartext → F3, a legacy `entity_hash` value preserved into transaction F25 (never dropped, never re-derivable); Accrual `period {start, end}` → F13/F14, unknown period member → MIG_UNKNOWN_KEY; `dependencies` deleted at **every** scope, nothing relocated (D4, scope corrected at v0.7); Arrangement `terms` — object members land in the element's F25 verbatim, a non-object keeps the key `terms`; `description` → the element's F25 at **every** component scope (v0.5); `compliance_flags` → transaction F25 (D3.5); `legal_documentation` → F29; D5 types gated, never coerced (MIG_D5_TYPE); `condition` presence → MIG_CONDITION; any unmapped fact-level key → MIG_UNKNOWN_KEY. F25 assembly is name-preserving; a residue-key collision → MIG_F25_COLLISION. **Deterministic and mechanical:** no clock, no randomness, no network, no interpretation; two conformant implementations produce byte-identical §6.1 output. Failure is loud: named code, no partial output.

**L-4. Judge the output as class R** (labels: `legacy`). The response names the source version and the applied chain.

**L-5. Terminus notice** (OQ-13). Legacy acceptance is **permanent** — it never sunsets. After the conformance terminus (D9 site promotion; a configuration fact, not a computation), the response additionally carries the stated non-conformance notice. The teeth are conformance status and engagement terms, never service refusal — the evidence is innocent of the producer's lateness.

### 5.3 Class H — a batch header

**Encode direction:** field values → the one legal byte string per §6.3 → `anchored_value` = SHA-256(`0x02` ‖ bytes).
**Decode direction:** bytes → fields, rejecting (A4) every §6.3 illegality: non-`{0x00,0x01}` `escape_flag`, `header_version` `0x00`, truncation against `segment_count`, trailing bytes, segment order violating ascending (`stream_id`, `first_seq`), duplicate (`stream_id`, `first_seq`) pair. Decode-then-encode must reproduce the input bytes exactly; any difference → A4.

### 5.4 Class A — an anchored record against an on-chain payload

Inputs: the record's **exact bytes** (the bytes hashed at anchoring — for pre-migration anchors these are 4.1-wire bytes; the migration never re-anchors), the 32-byte `leaf_salt`, the lane name, the header context (`header_version`, `level`, `true_leaf_count`, `window_id`, `escape_flag`, `prev_header_hash`), and the on-chain payload (obtained by any means — explorer, node, archive; the *comparison* is offline and the payload's own authenticity is the chain's guarantee, not this class's subject).

**A-1.** `tx_hash` = SHA-256(body bytes). Exactly 32 bytes of hash over exactly the submitted bytes — no canonicalization, no whitespace adjustment: the bytes anchored are the bytes hashed.
**A-2.** `leaf` = SHA-256(`0x00` ‖ `tx_hash` ‖ `leaf_salt`), both operands exactly 32 bytes (A1 on violation).
**A-3.** Direct mode (`true_leaf_count` = 1): `tree_root` = the leaf itself. (Batched mode: recompute the root through the Merkle path per v4.1.3 §6 — bundle-class machinery, carried.)
**A-4.** Assemble the header (`lane_id` = first 8 bytes of SHA-256(UTF-8 lane name)), serialize per §6.3, `anchored_value` = SHA-256(`0x02` ‖ header bytes).
**A-5.** Compare to the on-chain payload **byte-exactly**. Match → VALID. Mismatch → A5, INVALID. Missing salt, body bytes, or header context with the anchor itself confirmed on-chain → A6, MATERIAL_UNAVAILABLE, custodian named — never INVALID.

**Custody law this class implies (F-2 model, stated for implementers):** verification requires holder-side material the chain does not carry — the body bytes, the salt, and `window_id` (drawn at signing time, request-granularity). A holder who retains none of these has an anchor and no proof; that is the holder's custody posture, not a protocol defect. The live population's custody sheets (`docs/proofs-material/`) are the worked demonstration, and the suite's ANCHOR vectors are built from them.

## 6. Byte-exact serialization verification

### 6.1 Layer (a) — the 4.2 record (amendment §7.1)

The canonical byte form is JCS (RFC 8785) with a spec-defined member order and a stricter number rule. Verification procedure — given bytes claimed canonical:

1. **Parse** as JSON (UTF-8). Parse failure → B3.
2. **Number rule (OQ-2).** Every numeric literal must be: exactly representable in ≤ 15 significant decimal digits (E12 otherwise), and spelled as the shortest decimal representation that round-trips its binary64, in **plain decimal notation at every magnitude** — no exponent form (stock number-to-string emits `1e-8`; the canonical spelling is `0.00000001` — this is the likeliest real divergence between implementations, not edge-case padding), no trailing fractional zeros, no superfluous leading zeros, no `-0`, no explicit `+`. Exactly one byte spelling per value → B1 on violation.
3. **Member order (OQ-11).** Within every fact-bearing object: F-coded members first, **ascending numeric** F-number (F7 before F10 — lexicographic sorting is a distinct, wrong order), then envelope members in the fixed enumeration — transaction: `schema_version`, `timestamp`, `arrangements`, `accruals`, `anticipations`, `actions`, `attestations`, `component_hashes`, `merkle_root`, `blockchain_anchor`, `audit_data_anchor`, `privacy_metadata` (absent optionals omitted; the order is of members present); component element: **`id` last**. Interiors — envelope objects' inner members, F25 residue, F5 party entries, F21 allocation entries — sort JCS-lexicographic (UTF-16 code-unit order). **Arrays are never reordered** — element order is content, not encoding. Violation → B2.
4. **Strings and whitespace.** JCS string escaping (the ECMAScript JSON.stringify serialization RFC 8785 §3.2.2.2 adopts), UTF-8, no insignificant whitespace → B3.
5. **The one-spelling check.** Re-serialize the parsed value under rules 2–4; the result must equal the input byte-for-byte → B4. This single comparison subsumes 2–4; a verifier reports the most specific finding it can identify.

Idempotence is a theorem of these rules (canonicalize ∘ parse ∘ canonicalize = canonicalize) and a conformance vector (CS-5), not an extra rule.

### 6.2 The allocation-sum comparison (amendment §7.1.5)

Σ F21 entry amounts vs F9, compared **as decimal values** — parse each operand's decimal string into scaled integers (value = units × 10⁻ˢᶜᵃˡᵉ), rescale to the common maximum scale, compare integers. Binary-float comparison is forbidden; there is no tolerance. Zero-decimal (JPY) and three-decimal (BHD) currencies need no special casing — a binary-float-tolerance implementation fails the three-decimal vectors, which is the point of their inclusion.

### 6.3 Layer (b) — the ABH byte layout (amendment §7.2)

The one legal encoding of `batch_header`, packed, no padding, no terminator:

| # | Field | Encoding | Bytes |
|---|---|---|---|
| 1 | `header_version` | uint8; `0x00` permanently illegal (first legal value is 1) | 1 |
| 2 | `level` | uint8 (protocol restricts *values* to 0–3 post-decode; not a width constraint) | 1 |
| 3 | `tree_root` | raw | 32 |
| 4 | `true_leaf_count` | uint64 big-endian | 8 |
| 5 | `lane_id` | raw (= first 8 bytes of SHA-256(UTF-8 lane name), per the ratified convention) | 8 |
| 6 | `window_id` | uint64 big-endian | 8 |
| 7 | `segment_count` | uint16 big-endian | 2 |
| 8 | `stream_segments[]` × count | per segment: `stream_id` raw 16 ‖ `first_seq` u64 BE ‖ `last_seq` u64 BE ‖ `head_hash` raw 32 | 64 each |
| 9 | `escape_flag` | `0x00` false / `0x01` true; any other byte illegal, never coerced | 1 |
| 10 | `prev_header_hash` | raw | 32 |

`anchored_value` = SHA-256(`0x02` ‖ rows 1–10). A zero-segment header is 93 bytes before the domain byte. Big-endian for every multi-byte integer and `segment_count` as uint16 are **ratifications of deployed, anchored reality** — every mainnet anchor in the custody record was derived under this encoding and independently re-verified byte-identical on-chain (amendment §7.2).

**Segment order (OQ-10, ruled fresh):** ascending (`stream_id`, `first_seq`); equal pairs structurally illegal. **Comparison bases, so no implementation can read the sort ambiguously:** `stream_id` compares **byte-lexicographic** (opaque bytes, first differing byte decides); `first_seq` compares **on numeric value** — coinciding with its fixed-width big-endian bytes. Two segments with the same `stream_id` and different `first_seq` are legal and order by `first_seq` — this supersedes the at-most-one-segment-per-stream rule of `foray-abh-serialization-v0_2.md` §4.1, which the amendment's ruling replaced when it ruled the tiebreak; the v0_2 document remains the ratified authority for everything the amendment did not re-rule. Sorted order means the header does not preserve segment arrival sequence — no loss; intra-stream ordering lives in the segments' own `seq_no` ranges.

**Decode rejections (A4), exhaustive at this layer:** illegal `escape_flag` byte; `header_version` `0x00`; declared `segment_count` with fewer than count × 64 bytes following (truncation); trailing bytes after `prev_header_hash` (the format has no trailer); segment order violation; duplicate (`stream_id`, `first_seq`) pair — checked independently of the sort check (two identical pairs are trivially "sorted").

## 7. The claim a PASS makes

### 7.1 What VALID attests, per class

| Class | A PASS attests | A PASS does NOT attest |
|---|---|---|
| R | The record conforms structurally, semantically, and (when bytes were judged) byte-exactly to the 4.2 wire | That the record was anchored; that its content is true; that its declared amounts, dates, or parties correspond to reality |
| L | The legacy record is valid against its own version's canonical shape and converts deterministically to the stated 4.2 record | Anything about the producer's conformance standing beyond the stated notice; any judgment of the record's content |
| H | These bytes are the one legal encoding of this header and commit to the stated `anchored_value` | That the header was ever anchored |
| A | **Temporal + integrity proof:** the exact submitted bytes existed no later than the chain block time of the anchoring transaction and have not been altered since — because SHA-256(body) ‖ salt ‖ lane ‖ header reproduce the on-chain payload byte-for-byte | Everything in §7.2 |

### 7.2 The trust-model boundary (binding, per the Attestation and Trust Model)

FORAY proves records **existed** unaltered from a point in time; it proves **claims were made**, not that claims are true. A PASS never attests: truth of the recorded assertions, physical reality, attestor competence, or data accuracy at source. FORAY makes the originating system's declarations tamper-evident; it does not make them true. Verification output that presents a PASS as proof of any of these is non-conformant regardless of its cryptography.

**The F-18 rider (permanent claim-discipline law, carried verbatim):**

> no artifact may imply that salted formula IDs prove execution correctness — they prove definition retention. The hash proves which formula was declared and that it is unaltered since; it does not prove the formula ran, or ran correctly. Binds all future artifacts; the v1.8 site wording check already queued in the review register enforces it retroactively.

A verifier's display of F18 findings, and any prose a conformant verifier emits about formula facts (F18/F19/F20), is bound by this rider.

### 7.3 Mandatory display (hiding any is non-conformance)

Every verification output, machine or human, MUST show: the **outcome** with every finding's code and named culprit; the **class evaluated**; all **labels** (`legacy` with source version and applied chain; the non-conformance notice when configured); for class A, the **anchoring transaction id and the only time actually proven** (the chain's block time — in-record timestamps are submitter assertions, v4.1.3 §7); for MATERIAL_UNAVAILABLE, the **responsible custodian**; for byte-level judgments, whether the submission was judged as **transport or as canonical bytes**. A verifier that displays ✓ without stating what the ✓ attests (§7.1) is non-conformant regardless of its cryptography.

## 8. The offline conformance suite

The suite lives at `foray-api/conformance/` — vectors, manifest, runner, reference adapter, and broken-verifier variants. It runs **offline**: one command, no network, no DUNIN7 service. Its on-chain expectations (ANCHOR family) are recorded values from the live custody population, checkable against the public chain independently but not fetched at run time.

### 8.1 Vector families (the amendment §10 plan, authored)

| Family | Contents |
|---|---|
| **CS** | Canonical serialization: decimal accept/reject sets with the 15-digit boundary and tiny-magnitude plain-notation catchers; member-order goldens (numeric-vs-lexicographic F-order, envelope tail, `id`-last, F25 lexicographic interior, optional-envelope omission); string encoding (JCS escaping, non-ASCII, the `sha256:` F3 form); the golden record → bytes → SHA-256; idempotence; ordering negatives (lexicographic and insertion-ordered spellings rejected); exact-decimal allocation sums (JPY, BHD, the float trap) |
| **ABH** | Golden direct-mode header; multi-segment goldens including the same-stream-two-segments tiebreak; decode negatives: bad `escape_flag`, `header_version` 0, truncation, trailer, order violation, duplicate pair, wrong-endian probe |
| **MIG** | The byte-golden migration set — including the v0.5 (description scope), v0.6 (currency scope), and v0.7 (dependencies scope) correction vectors — plus the named-error failure set (D5 types, `condition`, unknown keys) and the 4.0 two-hop chain |
| **VAL** | Structural/semantic positives and negatives with named culprits; the register-leak probe; legacy acceptance with byte-equal conversion; the post-terminus notice case |
| **ANCHOR** | The real anchored population: record-a/b v2 bodies + salts + lane + header context → recomputed commitments → the actual on-chain payloads (provenance `live-anchored-evidence`); designed negatives (wrong salt, tampered body) |

### 8.2 Manifest

`conformance/manifest.json` lists every vector: id, family/class, runner mode, file(s), expected outcome, and **provenance** — `designed` | `golden-from-migration` | `live-anchored-evidence` — with source citations for the live-evidence entries (custody sheet, Kaspa transaction id).

### 8.3 Runner and candidate interface

`node conformance/run-conformance.mjs` executes the full suite against the reference implementation; `--verifier <module.mjs>` executes any candidate implementing the documented interface (`conformance/README.md`): `canonicalize`, `validateDual`, `migrateChain`, `abhEncode`, `abhDecode`, `abhAnchoredValue`, `directCommitment`. The runner reads vectors, invokes the candidate, and emits a per-vector PASS/FAIL report with a non-zero exit on any failure. A candidate needs no DUNIN7 code: the interface is semantic, the vectors are data, and this specification defines every behavior tested.

### 8.4 Test-the-tests

At least one deliberately broken verifier variant MUST fail the suite before the suite is trusted. The shipped mutation set (`conformance/broken/`) demonstrates, per mutation, which vectors catch it — a stock number-formatter, a lexicographic member sort, a binary-float allocation sum, a little-endian ABH encoder, a decoder without the duplicate-pair check, an F8-materializing migration, a domain-separation-free leaf, and a pre-v0.5 Arrangement-only description scope. A suite change that stops catching any shipped mutation is a regression.

### 8.5 Conformance claim rule (carried from v0.1, unchanged in force)

Only implementations passing the **complete** suite — positives and negatives, all families — may describe output as **"FORAY verified."** Partial implementations must use different words. For the bundle class, the WP-1 N-vector suite additionally applies (§4.5).

## 9. Carried v0.1 open items — dispositions at v0.2

1. **Canonical ABH byte layout** (v0.1 item 1) — CLOSED: ratified at amendment §7.2, restated as procedure in §6.3, tested by the ABH family.
2. **Registry-stream snapshot semantics for offline verifiers** (v0.1 item 2) — **carried, still open.** Bundle-class machinery; not needed by classes R/L/A/H. Remains a defect to report, not silently resolve, wherever the bundle class is deployed offline from cached registry state.
3. **Legacy-class vector set from real pre-fix envelopes** (v0.1 item 3) — superseded in spirit by the ANCHOR family: the live-evidence vectors are now real anchored population, not demo envelopes.
4. **§-reference verification** (v0.1 item 4) — this document cites the ACCEPTED amendment v0.7 by its own section numbers, verified against the in-force text at authoring time.

## 10. Findings at v0.2 — reported, not resolved

1. **The incident-probe and 2026-07-14 anchors are deliberately outside the ANCHOR family.** The auth-probe anchor (`0f86f45b…`) has full custody material except `window_id` (recorded for the demonstration records only); `f23d499a…` is NOT RECOVERABLE (terminal); `d6d704ed…` is body-only. The suite's live-evidence family therefore holds exactly the two fully-custodied demonstration anchors. Their sheets record `window_id` values recovered by brute force; the suite treats them as custody inputs, which is the F-2 model's own position.
2. **`window_id` semantics.** v4.1.3 §4 says "monotonic per lane"; the deployed direct-mode path draws `window_id` from request time. This specification treats it as opaque custody material for class A. Whether a verifier should *validate* monotonicity is bundle-class stream machinery, unruled for the direct lane — carried as an open observation, not enforced.
3. **F5 interiors are structurally open** (schema-stage finding §4.1, carried): no byte-level or structural check exists inside F5 party entries beyond JCS-lexicographic interior order. Protected-party forms await catalog cadence.
4. **`attestations` elements.** The envelope enumeration admits an optional `attestations` array; no 4.2 structural definition of its elements exists (the trust-model's exhibit is 4.1-era `foray_core` shape). The suite carries no attestations vector; a 4.2 record carrying one is judged by the schema's actual constraint for that member. Catalog/Stele-cadence work per the ruled sequencing; reported here so the silence is inspected.
5. **Bundle-class vectors remain in WP-1 form** (4.1-era bundle format, canned contexts). Re-issuing them against 4.2-era bundles is future work sequenced with the machine-reader/GEO release; the crosswalk (§4.5) is the bridge until then.

## Contact

- **Author:** Marvin Percival (specification drafted by Claude Code, FORAY project)
- **Email:** marvinp@dunin7.com
- **GitHub:** github.com/DUNIN7/foray
