Draft — F-15 gate artifact

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.

ClassInputQuestion answered
R — 4.2 recordA 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 recordA 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 recordA record's exact bytes + its leaf_salt + its lane name + direct-mode header context (§5.4), against an on-chain payloadDo these bytes, and no others, reproduce the 32-byte value actually anchored on the public chain?
H — batch headerAn 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:

OutcomeMeaning at v0.2
VALIDEvery mandatory check for the evaluated class passed
INVALIDA structural, semantic, byte-level, or anchor-level check failed (sub-verdict INVALID-CHRONOLOGY carried for the bundle class, v4.1.3 §7.7)
DISPUTEDCryptographically valid but named by an anchored revocation/DISPUTE record (bundle class, v4.1.3 §8)
MATERIAL_UNAVAILABLEAnchor 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-STALERepository-class outcomes (v4.1.4 §5), bundle class only
DEGRADED-CHAINDe-registered chain, no history checkpoint (v4.1.4 §3C.5), bundle class only
UNSUPPORTEDUnrecognized 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:

CodeFinding
E1schema_version missing, mismatched, or unroutable
E2No components: all four component arrays empty or absent
E3Structural 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¹⁵)
E11An 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_KEYClass-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:

CodeFinding
E4Anticipation missing F27/F28 (named-culprit: superseded spellings expected_date/datetime, probability_factor/probability are called out when present)
E5Action missing F9 (named-culprit: amount_settled/actual_amount)
E7Dangling F22/F23/F24 reference
E8Allocation-sum violation: Σ F21 entry amounts ≠ F9 as exact decimals (§6.2)
E9F21 ref unresolvable or ref_type mismatch
E10extension object anywhere in transaction JSON (envelope-only, never record)
E12Numeric value exceeding 15 significant decimal digits (OQ-2)
E14Monetary 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:

CodeFinding
B1Non-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.)
B2Member-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)
B3Encoding violation: insignificant whitespace, non-JCS string escaping, non-UTF-8 bytes
B4One-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)

CodeFindingv0.1 lineage
A1Leaf-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 leafN1
A2Header-binding mismatch: SHA-256(0x02 ‖ serialize(header)) ≠ the anchored 32-byte valueN2
A3Padded-duplicate membership claim: leaf_index ≥ true_leaf_count (bundle class)N4
A4ABH 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
A5Commitment mismatch (class A): recomputed anchored_value ≠ the on-chain payload— (new at v0.2)
A6Required holder-side material missing (salt, body bytes, header context) with the anchor itself verified → outcome MATERIAL_UNAVAILABLE, custodian namedN7
A7Registry/dispute finding → DISPUTED; replay/duplication → INVALID (bundle class)N5, N6
A8Stream/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.1Attack encodedv0.2 codeSuite home
N164-byte internal-node-as-leafA1WP-1 (structural 32-byte law also enforced by construction in class A)
N2true_leaf_count tampered vs anchored headerA2WP-1; F-15 ANCHOR negatives exercise the same binding via A5
N3Dictionary attack against a sibling digestdocumented, not runtime (unchanged)WP-1 (N3-dictionary-attack.md)
N4Proof for a padded duplicateA3WP-1
N5Revoked-in-window keyA7 → DISPUTEDWP-1
N6Duplicate (submitter, submitter_seq)A7 → INVALIDWP-1
N7Withheld blob/salt, anchor genuineA6 → MATERIAL_UNAVAILABLEWP-1; F-15 states the salt-handling law in §5.4
N8Repository split-view pairA8WP-1
N9Overdue checkpointA8 → PROVISIONAL-STALEWP-1
N10Backdated stream insertA8 (GAP/FORK)WP-1
N11Later-anchored _refA8 → INVALID-CHRONOLOGYWP-1
N12Missing extension vs ext_version: 0envelope: A-plane (WP-1); record: E10WP-1 + F-15 VAL
N13Unknown nonzero ext_versionpreserved, never rejected (unchanged)WP-1
N14Single-RPC-source proofA8 (verifier conformance failure)WP-1
N15Serialization edge casesA4 + 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):

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 ids (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. MigrationmigrateV41toV42, 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(0x00tx_hashleaf_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:

#FieldEncodingBytes
1header_versionuint8; 0x00 permanently illegal (first legal value is 1)1
2leveluint8 (protocol restricts values to 0–3 post-decode; not a width constraint)1
3tree_rootraw32
4true_leaf_countuint64 big-endian8
5lane_idraw (= first 8 bytes of SHA-256(UTF-8 lane name), per the ratified convention)8
6window_iduint64 big-endian8
7segment_countuint16 big-endian2
8stream_segments[] × countper segment: stream_id raw 16 ‖ first_seq u64 BE ‖ last_seq u64 BE ‖ head_hash raw 3264 each
9escape_flag0x00 false / 0x01 true; any other byte illegal, never coerced1
10prev_header_hashraw32

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

ClassA PASS attestsA PASS does NOT attest
RThe record conforms structurally, semantically, and (when bytes were judged) byte-exactly to the 4.2 wireThat the record was anchored; that its content is true; that its declared amounts, dates, or parties correspond to reality
LThe legacy record is valid against its own version's canonical shape and converts deterministically to the stated 4.2 recordAnything about the producer's conformance standing beyond the stated notice; any judgment of the record's content
HThese bytes are the one legal encoding of this header and commit to the stated anchored_valueThat the header was ever anchored
ATemporal + 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-byteEverything 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)

FamilyContents
CSCanonical 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)
ABHGolden 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
MIGThe 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
VALStructural/semantic positives and negatives with named culprits; the register-leak probe; legacy acceptance with byte-equal conversion; the post-terminus notice case
ANCHORThe 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 provenancedesigned | 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