# `<EntvizCompare>` — comparison feature (design)

**Status:** design, pre-implementation. **Audience:** implementers of `@entviz/react`.
**Depends on:** the entviz spec (`../entviz/docs/spec.md`), paper (`entviz-paper.md`), and
threat model (`threat-model.md`) in the sister repo; and the sibling
[`<EntvizPill>` design](./pill-design.md) (recognition ≠ verification; closed profile;
comparison-text definition; terminology — an entviz is a *visualization*, not a
"fingerprint").

This doc designs the feature that helps a human decide whether *their* entviz matches a
*reference*. Its security-bearing decisions were **stress-tested adversarially** before being
written down; every load-bearing claim links to the supporting review below
([§13 Provenance](#13-provenance--how-this-design-was-validated)). Where this doc states a
security property, it states it **conditionally** — naming the assumptions — because the
review process showed that unconditional phrasing was the main source of error.

---

## 1. Terminology (precise; used consistently)

Operational definitions, adopted from the adjudications because the analyses repeatedly
conflated distinct notions:

- **exact cell match** — two entvizes' cell *i* have identical canonical token text.
- **forge a cell** — produce a *distinct, usable* candidate value whose cell exactly matches a
  cell of a target the attacker **does not control** (for a public key, the attacker must
  retain a usable private key — A "witness").
- **steer the order** — a live participant chooses their seed contribution *after* learning the
  other's, biasing which checks come up early.
- **verified** — the workflow reaches an affirmative verdict. This is split into two:
  **IDENTICAL** (the machine compared both sides in full) and **NO DIFFERENCE** (a human walk
  found no difference across the checks performed — probabilistic, never the machine's `=`).
- **soundness** — P(affirmative verdict │ the values differ) ≤ the advertised bound, under
  stated assumptions.
- **unmatchable** — computationally expensive to forge under a stated work factor; never
  literally impossible.
- **authenticated channel** — the comparison/out-of-band channel (voice/video) has integrity
  and is bound to the intended peer (you recognize them). *Confidentiality is not required.*
- **substitution** — the attacker fixed a different candidate value **before** the ceremony and
  is **not** a live participant. **relay** — an active intermediary observes/modifies ceremony
  messages in real time. **endpoint compromise** — the attacker controls a party's own
  rendering/comparison tool.
- **equality-soundness** vs **reference-authenticity** — proving `A = B` is distinct from
  proving the reference is the value you *should* trust. This feature does the former, never
  the latter.

---

## 2. First principles

1. **Recognition ≠ verification** (paper §2.3, §5.1; [pill design](./pill-design.md) §2). A
   glance recognizes; only a deliberate check verifies. The walk forces deliberate checks.

2. **Proof is asymmetric.** A single mismatch proves inequality with certainty. "Identical" is
   machine-provable only when the machine sees **both** sides in full (text/SVG engines), or a
   tool-driven **complete** read; a raster or a partial human walk yields only *"no difference
   found across what was checked."* (Adjudicated; see
   [synthesis](../../../reviews/comparison-redteam/findings-04-synthesis.md) Ruling 3.)

3. **The text channel is the primary soundness anchor — for *constrained* high-entropy values.**
   For a value whose token bytes the attacker cannot freely choose (a key whose private key
   must remain usable), forging a checked cell is a **partial preimage** costing ≈2^(bits in
   that cell) — see the per-format table in [§7.3](#73-the-confidence-meter). Across the
   spec's alphabets this is **2²⁰–2²⁴ per cell**, never zero. Both independent adjudications
   confirmed this and refuted the panel's "matched for 0 bits" headline for the canonical case
   ([local](../../../reviews/comparison-redteam/findings-05-adjudication.md) Q1;
   [foreign](../../../reviews/comparison-redteam/findings-06-independent-adjudication.md) Q1).
   **Crucial caveat:** for **programmable / attacker-authored** values (arbitrary-text
   fallback, vanity values, a UUID whose bits the attacker fills) the per-cell cost is ~0. So
   *"high entropy"* is **not** the qualifying property; **constrained generation** is. The
   meter credits a cell its real min-entropy, and **0 for any value not locally generated** in
   adversarial mode.

4. **Equality-soundness ≠ reference-authenticity.** A green verdict means "your value equals
   *this reference*," never "this reference is trustworthy." An attacker-supplied reference
   (pasted, dropped, or URL-fetched) makes a correct equality verdict meaningless. Surface
   provenance as first-class, not a footnote.

5. **Endpoint trust is irreducible** (decision-F limit; Signal-safety-number parity). A
   compromised counterparty tool can show *its own user* a fabricated entviz; no comparison
   protocol proves a remote device's state. We state this limit; we do not pretend to solve it.

---

## 3. Verdict state machine (four states)

| state | look | meaning | reachable by |
|---|---|---|---|
| **DIFFERENT** | red `≠` | a mismatch was found — **certain** | every engine; one mismatch |
| **PENDING** | gray `?` | not enough checked | initial / mid-walk |
| **NO DIFFERENCE · coverage** | a **coverage** meter (checks completed of the target), **not** a `1-in-2^N` thermometer | probabilistic; "no difference found in the checks performed" | the human walk, raster fallback |
| **IDENTICAL** | solid green `=` | machine compared both sides in full | text engine; SVG engine (≤512-bit, self-consistent) |

Rules: **a human-driven comparison never reaches IDENTICAL** (the machine cannot certify the
human's eyes). The affirmative human state shows **coverage**, not a nominal `2^-N` (the bits
are correlated and human-eroded; a probability would mis-state them — paper §4.3.9, adjudicated
[synthesis](../../../reviews/comparison-redteam/findings-04-synthesis.md) S6/S13). Reserve any
green/`=` treatment for machine-IDENTICAL.

---

## 4. User surface — two situational choices + one knob

The naive user picks based on situation, never on a security mode:

1. **"I have something to check it against"** — paste a value, or drop/link an entviz (file,
   image, URL). The engine **auto-detects** the medium (§5–6) and does as much by machine as it
   can.
2. **"I'm comparing live with another person"** — the guided two-party ceremony (§8).

The only exposed security knob is a confidence target the user **declares** (there is no
default): **Quick / Good / Complete**, mapping to concrete check plans — pinned in
[§14](#14-m2--the-guided-walk-pinned-specification). The *sensible* choice is **size-aware**
(for a small value Complete is cheap and the spot-check presets degenerate; §14.4). No
commitment / entropy-method / nonce-length choice is ever surfaced; all are derived (and, per
§8, commitment is the live default for the remote ceremony, not a user choice).

---

## 5. Reference acquisition

The medium (text / SVG / raster) is orthogonal to how it arrives: **paste**, **drop**,
**file-pick** (browser file picker / drag-drop; a typed path only in a desktop host), or
**URL-fetch** (`fetch`, then route by type). Caveats baked into the UI:

- **Auto-detect must fail closed.** On ambiguity (polyglots; a value-shaped string that's also
  a URL; an image that is actually an SVG), **reject and ask**, or route to the **least
  authoritative** engine (raster, which can never bless). Sniffing must never *upgrade* an
  artifact onto the IDENTICAL path. (Adjudicated S12.)
- **URL fetch** carries CORS limits, a privacy/referrer leak (surface the origin *before*
  fetch), and the attacker-chosen-reference problem (§2.4). Never present a URL-fetched
  reference with the authority of a locally-held known-good copy.
- **Secret-input warning.** "Paste your value" invites pasting private keys / seed phrases;
  confidentiality is out of scope per the threat model, so the UI must warn on known
  secret-material formats rather than lull (S21).

---

## 6. Per-medium engines

### 6.1 Text → value-level compare (definitive)
Normalize both sides (case/punctuation per the spec's per-alphabet rules) and compare normalized
cores (or normalized comparison-texts). Distinguish **reference entropy** (compare values) from
**reference comparison-text** (compare the text channel). Definitive `IDENTICAL`/`DIFFERENT`.

### 6.2 SVG → recompute, re-render, self-consistency (the hardened path)
A pasted SVG is **attacker-authorable** and the feature has **no golden raster**, so the
conformance checker's "trust the declared `<text>`/`data-*`" stance is unsafe here: Tier-B
excludes glyph pixels, so an SVG whose `<text>` says X while its ink shows Y could otherwise
reach IDENTICAL **with no preimage** (adjudicated S3 — a *requirement* for the unwritten engine,
not a demonstrated bug). The engine MUST:

1. run **strict closed-profile + self-containment validation first** (reject `<foreignObject>`,
   `<style>`, `<image>`, external `href`/`url()`, `@font-face`, `transform` on text, media
   queries, extra per-cell instances) — before extracting any value;
2. recover the normalized core **only at ≤512 bits** (>512-bit cores are unrecoverable from the
   cells; **never IDENTICAL** there);
3. **recompute** all fingerprint-derived fields from the recovered core (do not trust declared
   `data-*`), **re-render** through the tool's own pinned font, and require **self-consistency**
   (gestalt = SHA-512 of its own cells; glyphs = `<text>`);
4. reject any inconsistency → route to the human walk, never IDENTICAL.

### 6.3 Raster → structural predict-and-sample (disprove-only, geometry-anchored)

> **This supersedes an earlier whole-image pixel-diff sketch** that rasterized our own SVG,
> *stretched* it onto the reference's pixel box, and thresholded an aggregate pixel difference. That
> was a defect: it compared a distorted image, silently absorbed aspect-ratio differences into the
> stretch, and returned a reassuring "look very similar" for a coarse, near-meaningless signal. The
> engine below instead **anchors on the entviz's real geometry and samples predicted feature colors.**

**What it proves, and what it can't.** A raster **can never reach IDENTICAL** — we cannot OCR the cell
text, and a nucleus color encodes only a token's few *quant* bits, so matching every sampled color
still does not prove the values equal. It is **disprove-only**: it can reach **DIFFERENT** (a predicted
color is wrong) or, at best, **"no visible difference found (text unread)"**, which is surfaced as
`unknown`/`similar` and **credits zero authentication** (S10) — a raster reference still offers only the
**Complete human walk**, whose text read is the real check. "Couldn't analyze" is surfaced **distinctly
from DIFFERENT** to deny the false-DIFFERENT social lever (S18).

**Why it is nonetheless useful.** The color bar, quartile cells, and blank map are all **SHA-derived**,
so a genuinely different value **avalanches** them — the engine disproves a different value with high
probability. It is *blind* only to a difference that changes cell **text** without changing any sampled
(coarse) color — exactly the residual the human text walk covers. **Disprove-capable, never blessing.**

**Ground truth (the real pixel structure — `entviz.ts` render).** White bounding fill (`:1553`); a thin
**`#808080`** frame — four outer borders + a left divider (`:1713–1717`, `borderLine :1882`), **not
black**; a full-height **left color-bar gutter** (grid sits to its right; geometry `:1281–1285`); a white
**top label strip** (height `nucleusHeight`, type/prefix); grid background ∈ **{white, gold `#e7be00`,
red `#ff3f2f`, blue `#2f3fbf`}** (black excluded); an **optional bottom strip** (note *or* suffix); nuclei
= colored sub-rects with centered text; the **ellipse sits *behind* the nuclei** (never tints a nucleus,
only its surround); quartile marks = triangles in cell **corners**; blank cells filled, black-stroked.

The engine leans on `describeChannels(value, shape)`, which already yields the exact expected
**geometry** (viewBox units) and expected **color** of every feature. Five stages:

0. **Decode** — the React layer rasterizes the pasted image to RGBA; core (isomorphic) takes RGBA + the
   render model. Core never rasterizes our own SVG.
1. **Locate** — probe vertical scan-lines at x = 25% / 50% / 75% of the image width; find the first
   validated axis-aligned **`#808080` rectangle** with **white directly below its top edge** (the label
   strip). Three probes tolerate a screenshot with surrounding chrome. **No valid rectangle → `unknown`**
   ("couldn't find an entviz to analyze — check by eye"). Best-effort; never guesses.
2. **Normalize & gate size** — the left gutter + divider and the top strip fix the **grid's pixel
   rectangle**; **detect and exclude a bottom note/suffix strip** so two values differing only by a note
   compare equivalent; `scale = gridPixelWidth / model.gridW`. Below the **empirically-calibrated minimum
   size** (where fractional-pixel blur corrupts color sampling — set by a calibration test, not guessed)
   → `unknown`.
3. **Shape gate (phase 1)** — the image's grid aspect must match ours within a high tolerance (~98–99%),
   else `unknown` ("a different shape — can't compare"). *(Phase 2: search the value's achievable shapes
   for an aspect match and re-render at it, reaching parity with the shape-independent SVG engine.)*
4. **Verify structure** — the color-bar bands must be **exactly and only** the expected palette colors,
   in the expected order and ~ratio (fingerprint-derived, so this is already value-discriminating); frame
   and top strip present.
5. **Sample the value channels** — map each `cellRects[i]` to pixels and sample the **nucleus at
   horizontal-center near its upper/lower edge** (clear of the centered text and the corner quartile
   marks); likewise sample **quartile-mark corners** and **blank-cell fills**; compare each to the model's
   expected color. **Any mismatch beyond tolerance → `different`.** All match → "no visible difference
   found (text unread)" (`unknown`/`similar`, zero credit).

**Phasing & calibration.** Phase 1 (now): same-shape required. Phase 2 (later): shape-tolerant, for SVG
parity. The tolerances were **calibrated against real browser rasters** (`apps/playground/calibrate.html`
renders an entviz to a canvas at a size ladder and runs the engine): on anti-aliased pixels the binding
floor is **frame detectability**, not nucleus size — the 1px `#808080` border blurs toward white as the
image shrinks, so `locateFrame` returns null (→ `unknown`, fail-closed) below ≈140px wide for a small
value; at and above that the engine is correct (same value → "no visible difference", different value →
`different`). The min-nucleus gate is a secondary guard for a large value whose frame is found but whose
individual nuclei fall below the sampling threshold.

---

## 7. The guided walk (human path: raster-fallback and live)

### 7.1 Interaction
One feature at a time; the human reports **[Matches] / [Differs]**; one **Differs** → certain
DIFFERENT. The target feature is indicated by an **ephemeral focus ring drawn *around* (never
over) it**, in a **tool-controlled container** (iframe/shadow-DOM at a fixed integer scale the
host cannot transform), anchored in the entviz's own coordinate system by re-measuring the
live-laid-out cell geometry — so a host CSS `transform` can't shift the ring to the wrong cell
(S14). The closed profile is preserved (the ring is never baked into the entviz SVG).

### 7.2 Hard exact-text floor
**No affirmative verdict is reached until a fixed floor of *forced, case-confirmed, read-back*
text checks pass.** Gestalt channels (color bar, ellipse, blank map, quartile, nucleus color)
may only **fail-fast to DIFFERENT**; they never independently fill the affirmative meter. This
makes the soundness claim true *by construction* rather than by hoped-for ordering, and it
neutralises order-steering (a steered order cannot avoid the required text checks). (Converged
fix; [synthesis](../../../reviews/comparison-redteam/findings-04-synthesis.md) Ruling 2.)

### 7.3 The confidence meter
Shows **coverage**, not `1-in-2^N`. Credits **effective min-entropy**, and only genuinely
**independent** bits:

- **Independence is per-derivation, not per-channel.** Disjoint slices of the one SHA-512 digest
  *are* independent and may be summed; **overlapping derivations of the same bits must not be**
  (e.g. a cell's edge-singleton, surround, and quartile all ride the same ftok-quant — credit
  the quant once). (Foreign adjudication refinement of S6.)
- **Per-cell text cost is type- and position-specific** — credit a cell its real bits, **not a
  flat 24**:

  | format / position | exact-match cost (constrained generation) |
  |---|---|
  | full hex / base64url token | 24 bits |
  | UUIDv4 cells | ≈24, 24, 18, 24, 24, 8 (six version/variant bits in one token) |
  | CESR first / final token | ≈18 / ≈22 variable bits |
  | 4 base58 chars | ≤ 23.4 bits |
  | 4 base36 chars | ≤ 20.7 bits |
  | 4 chars, 5-bit alphabet (bech32/base32/Crockford) | 20 bits (bit-extension adds none) |
  | 6 decimal chars | ≤ 19.9 bits |
  | Ethereum | six 24-bit cells + one 16-bit final; EIP-55 case adds no identity bits |
  | large-input fingerprint-middle | 24 bits/cell, 96 for all four — **under an honest renderer** |
  | programmable / arbitrary-text / vanity | **~0** (credit 0 for any non-locally-generated value) |

  (Both adjudications; [local](../../../reviews/comparison-redteam/findings-05-adjudication.md) /
  [foreign](../../../reviews/comparison-redteam/findings-06-independent-adjudication.md) Q1.)
- **Viewer-relative (a11y == security, paper §5.4)** but **computed locally and conservatively**
  — never accept discriminability signals from the counterparty; gate a discrete check's credit
  on its *measured rendered size* (don't credit a quartile orientation or plus/dot shape blurred
  below discriminability at the actual scale) (S20/R-F9).

### 7.4 Anti-habituation
Forced one-at-a-time clicks **relocate** rather than defeat rubber-stamping (S9). Mitigations:
**attention probes** (occasionally an item that must be reported `Differs`, scored), and
**active-recall read-back** (the human types/reads a credited cell, not merely clicks
`Matches`). "Quick" must **not** reach an affirmative verdict — cap it at PENDING with
"insufficient checks" language.

### 7.5 Homoglyph hardening
The text anchor is byte-exact but the human reads **glyphs over a noisy channel**; confusables
(`0/O`, `1/l/I`, `5/S`, `8/B`, `-/_`) and dropped case erode effective bits by a bounded,
alphabet-dependent amount (S2 — hardening, not a break). Render the comparison text in a
**pinned, embedded, high-disambiguation font** in the comparison chrome (the spec's
font-embedding "out of scope" applies to the conformant artifact, not a verification tool), and
require an **explicit case + NATO-style readout** (`zero`/`cap-oh`, `one`/`ell`/`cap-eye`) for
credited cells. Lean on the **Crockford-middle** cells for large inputs — single-case and
homoglyph-clean by construction.

---

## 8. The two-party live ceremony

**Threat posture (adjudicated).** Assume the counterparty's **software is hostile** (decision
F). The irreducible requirement is an **authenticated comparison channel** (you recognize the
person on voice/video); confidentiality is *not* required. The compared values are **locked
before the seed** (you don't change your key mid-ceremony) — so observation of the seed is
harmless; the only live threat is a **last-mover steering** the seeded check-order.

**Commitment is the default, not an upsell.** Because a hostile last-moving peer who reveals
their nonce after seeing yours can steer the order (paper §5.2), and the design assumes hostile
counterparty software, the ceremony uses **commit-and-reveal with a high-entropy randomizer**
(binding + hiding; not a bare hash of a short nonce) by default. It removes the
**≈log₂C(K,L)-bit steering discount** (≈14 bits at K=20,L=5; both adjudications quantified
this). Commitment may be **dropped only** when the channel is authenticated, both endpoints
honest, and the seed provably unobserved — a narrow case we do not make the default. *Why this
reverses the pill-era "drop commitment" instinct:* not because text is weak (it isn't — §2.3),
but because the live UX admits a steering peer; see
[local](../../../reviews/comparison-redteam/findings-05-adjudication.md) Q3 and
[foreign](../../../reviews/comparison-redteam/findings-06-independent-adjudication.md) Q3.

**Seed.** Entropy is **scaled to the preset**: require `H_min(seed) ≥ log₂C(K, L_preset)`
(define real K, L, presets — a fixed "14 bits" is right only for Quick). The
**click-harvest** idea is **reconsidered**: human-sourced click entropy does *not* defend
against a malicious local tool (it can falsify the gesture), and an honest tool should use a
platform CSPRNG; harvest the seed from the CSPRNG and reserve any human contribution for the
commitment exchange (foreign adjudication Q3). A short Crockford code is read aloud for the
commit and reveal.

**Local-only evidence.** The verdict, meter, and focus highlights are trusted **only on the
verifying user's own endpoint**; a counterparty's shared screen/meter is *not* evidence (S /
N-4). Over a screen-share, force commitment (or off-screen seed transfer).

**Honest scope.** This ceremony detects a **MITM on the primary (key-exchange) channel** by
comparing over the authenticated OOB channel — the standard SAS guarantee; *contra* an earlier
rebuttal claim, SAS **does** survive a relayed primary channel when the comparison channel is
authenticated. It does **not** defend a compromised endpoint, nor a relay of the comparison
channel itself with no peer authentication.

---

## 9. Property split & API sketch

Mirrors the [pill](./pill-design.md) split: **deterministic entviz render inputs** vs.
**contextual comparison chrome**. A new component composes the existing renderer:

```ts
// <EntvizCompare> — drives a comparison; renders pristine <Entviz> artifacts as panels.
interface EntvizCompareProps {
  // the user's own value (entviz render inputs are deterministic/context-free)
  value: string;
  targetAr?: number; fontSizePt?: number; note?: string | null;
  // reference acquisition (mutually exclusive with `live`)
  reference?: { kind: "text" | "svg" | "raster"; data: string | Blob } | { url: string };
  live?: boolean;                          // the two-party ceremony
  confidence?: "quick" | "strong" | "paranoid";
  // chrome (contextual; localized; a11y) — see pill design §5, §8, §9
  locale?: string; messages?: Partial<Messages>;
  onVerdict?: (v: Verdict) => void;        // { state, coverageBits?, provenance }
  className?: string; style?: React.CSSProperties;
}
type Verdict =
  | { state: "different" }
  | { state: "pending" }
  | { state: "no-difference"; coverageBits: number; complete: boolean }
  | { state: "identical" };
```

Localization, RTL (chrome mirrors, the entviz never does), and a11y follow the
[pill design](./pill-design.md) §8–9, with the same two non-negotiables: never localize/transform
the value or comparison text; never use locale-aware casing on the value.

---

## 10. What must be pinned before implementation (the adjudicators' "unspecified")

Both adjudications stressed that *product severity* is uncertain because these are unspecified.
They are prerequisites, not afterthoughts:

1. **Concrete K (checklist size), L per preset, and the bit thresholds** Quick/Good/Complete
   map to — and the seed `H_min` each requires. *(Pinned for M2 in
   [§14](#14-m2--the-guided-walk-pinned-specification); seed `H_min` is an M3 item.)*
2. **The channel-authentication model** the live ceremony assumes (what makes the OOB channel
   "authenticated" in the UX).
3. **The full verdict state machine** (transitions, what credits/resets the meter).
4. **Measured per-format keygen/output distributions** for the supported typed values (to
   confirm the §7.3 table against real key formats).
5. **Human-factors studies** the paper itself flags as unmeasured (§6.3): habituated walk
   completion; case/homoglyph error rates. Until measured, treat S2 as *necessary* hardening and
   do not advertise nominal bits.

---

## 11. Security rationale index (decision → supporting finding)

| decision | grounding |
|---|---|
| Text is the primary anchor (constrained high-entropy) | adjudications Q1; paper §5.1/§5.3 |
| Credit per-format bits; 0 for programmable/non-local | S16; both adjudications Q1 |
| Coverage meter, not `1-in-2^N`; independent bits only | S6/S13; paper §4.3.9 |
| Hard text floor; gestalt fail-fast only | Ruling 2; S2/S5/S7 |
| Commitment is the live default (steering, not weak text) | Ruling 1 corrected; both adjudications Q3; paper §5.2 |
| Seed scaled to preset; reconsider click-harvest | foreign Q3; S5/S8 |
| SVG: recompute+re-render+self-consistency; no >512 IDENTICAL | S3 (requirement) |
| Raster: disprove-only; UNKNOWN = zero evidence | S10 |
| Auto-detect fail-closed | S12 |
| Anti-habituation (probes, read-back) | S9 |
| Local-only evidence; provenance first-class | S17; endpoint-trust limit |
| Equality-soundness ≠ reference-authenticity | foreign Q2/Q5 |

---

## 12. Deferred / out of scope

- The **adversarial seeded-walk protocol's** full formal specification (the §10 items).
- **Desktop-host** typed-path file references and OS clipboard integration.
- Anything the [pill design](./pill-design.md) §11 defers.

---

## 13. Provenance — how this design was validated

This design's security decisions were adversarially stress-tested through a five-stage chain
(all archived under `reviews/comparison-redteam/`):

1. **Adversarial panel** — three independent single-lens red-teams:
   [cryptography](../../../reviews/comparison-redteam/findings-01-cryptography.md),
   [usable-security](../../../reviews/comparison-redteam/findings-02-usable-security.md),
   [rendering/forensics](../../../reviews/comparison-redteam/findings-03-rendering-forensics.md).
2. **Synthesis** — [consolidated panel verdict](../../../reviews/comparison-redteam/findings-04-synthesis.md)
   (findings **S1–S22**, seams **SEAM-1..6**).
3. **Rebuttal** — a [first-principles counter-analysis](../../../reviews/comparison-redteam/rebuttal-01-orchestrator.md)
   challenging the panel's headline.
4. **Adjudication (local)** — an [independent third derivation](../../../reviews/comparison-redteam/findings-05-adjudication.md)
   under mandatory rigor rules.
5. **Adjudication (foreign)** — a [second independent adjudication by a different model](../../../reviews/comparison-redteam/findings-06-independent-adjudication.md).

**Net, two-model-converged outcome:** the panel's dramatic headline ("text-anchoring doesn't
survive → reinstate commitment because text is matched for 0 bits") is **false for the canonical
constrained-high-entropy substitution case** — forging text is a real 2²⁰–2²⁴-bit-per-cell
partial preimage. The genuine, philosophy-independent requirements are the machine-path and
meter fixes (S3, S6, S10, S12) plus human hardening (S2, S9). And commitment **does** belong in
the live default — not because text is weak, but because the live ceremony admits a last-mover
who could steer the check-order. This doc encodes that corrected, adjudicated position.

---

## 14. M2 — the guided walk (pinned specification)

This section pins the guided walk for implementation. It **supersedes the §7 sketch and the §4
preset names where they differ** — notably: presets are **Quick / Good / Complete**; gestalt
dimensions **contribute coverage**, not fail-fast only; attention probes are **opt-in for
Complete only**; commitment/seed/channel-auth move to the live ceremony (M3, §8). It is grounded
in the companion adversarial paper *Measuring the Glance*: the operative strength is a **curve**,
the casual glance is loose and parallel, and a difference is reliably *perceived* only when
attention is **directed** to a feature. The walk's job is to spend scarce attention well.

It covers the **single-user** walk (when the machine path bailed to `unknown` — an inconsistent
SVG, a raster, a >512-bit input — and the human verifies anyway) and the shared mechanics the
live ceremony builds on.

### 14.1 What a walk checks: text cells and gestalt dimensions

A difference can be checked along many **features**, of two kinds:

- **Text cells** — a cell's glyphs. *Local, lossless* (at ≤512 bits the text *is* the value),
  *input-driven* (steerable: an attacker can set them by choosing the input). They are the
  **certainty backstop** — matching one exactly is positive proof for that slice of the value.
- **Gestalt dimensions** — each one **dimension along which the figure-as-a-whole is
  characterized**, and each a deterministic function of SHA-512 of the *entire* value
  (*hash-driven / un-steerable*), so **a difference anywhere in the value avalanches every
  gestalt dimension at once** — together they are a **whole-value CRC**, and being un-steerable
  they are the *true* grind cost against the realistic constrained attacker. The checkable pool
  (all carried by ringable elements):
  - *Salient (read loosely in the parallel glance; focus to confirm):* **background color**
    (~2 bits); **color-bar pattern** — band order + heights (~4.6 bits for the order);
    **ellipse** — orientation / aspect / size (~7 bits, factored); **blank pattern** — which
    cells are holes.
  - *Positional CRC (need directed attention; higher positional bits):* **color-bar markers**
    (the two gutter circles' slot positions); **quartile marks** (the four triangles' cell
    positions, ~4·log₂N); **blank-map markers** (the plus = max, dot = min inside the one map
    cell).

  Two clarifications. The **blank pattern** (where the holes are) and the **blank-map markers**
  (where the map's plus/dot point) are *separate* dimensions — the map is the single
  lowest-indexed blank and *additionally* carries the min/max marks. The per-cell **surround
  texture** (24 box on/offs = `data-surround-bits`) is *machine-grade* — it is why `compareSvg`
  is strong, but 24 on/offs are below reliable eyeball discrimination, so it stays out of the
  human pool (offered only as a diligent extra in Complete). Nucleus *color* re-encodes the
  cell's text bits, so it is checked *as* the text, not as a separate dimension.

> **"Gestalt" means "characterizing the whole rather than the parts," along one dimension of
> analysis** — the way light/dark ratio and clumpiness are both properties of a bitmap, yet a
> viewer attends one *independently* of the other. It does **not** mean "absorbed automatically
> in one glance": a casual glance reads the gestalt loosely and in parallel and can miss any
> given dimension (crowding, overlay-tint coupling, masking — *Measuring the Glance* §5–6). Each
> gestalt dimension must have attention **directed** to it to be discriminated reliably — which
> is exactly why the walk focuses *every* feature, gestalt or text. (A dimension too diffuse to
> localise — overall "clumpiness", grid aspect ratio — is not a checkable feature; we check only
> dimensions carried by a *ringable element*, and a gross structural difference like a different
> grid shape is caught for free.)

### 14.2 The check plan

A walk executes a **check plan**: an **unpredictable, ordered subset** of features.

- **Mixed.** A plan combines text cells and gestalt dimensions (e.g. *2 text + color-bar order +
  ellipse*, or *4 text + color-bar order*). It always includes **≥ 2 text cells** as the lossless
  backstop — a gestalt match is only *within human tolerance* and cannot, alone, bless. The exact
  recipe per preset is a tunable knob, not a blocker.
- **Unpredictable selection + order** — from the **local CSPRNG** (single-user) or the
  **committed-then-revealed seed** (live remote, M3), so neither the user's habit nor a steering
  peer can pre-empt it. *This unpredictability is the anti-habituation mechanism*: the user can't
  pre-load a rote answer, they must look at whatever the ring lands on. (It retires planted
  decoys for Quick/Good; see §14.6 for the Complete-only probe.)
- **Weighted, not uniform — the gestalt dimensions are not fungible.** Per *Measuring the Glance*
  §5–6 the channels differ markedly in discriminability under directed attention (comparison JNDs):
  the ellipse ≈ 7 bits, the **local positional-CRC** channels (quartile marks, color-bar markers,
  blank-map) several bits each *and missed by the parallel glance* (so a directed check buys the
  most there), the color-bar order ≈ 4.6, the background only ≈ 2 (four values). Selection is
  therefore **weighted-random by a per-channel weight** — `ellipse 7, quartile-marks 6,
  colorbar-markers 5, blank-map 5, colorbar-pattern 4, blank-pattern 3, background 2` (modeled, not
  measured on people; tunable) — *not* uniform: the ellipse/CRC channels are checked far more than
  the background, while the order stays unpredictable. The channels are disjoint slices of the one
  SHA-512 digest (derivationally independent — §7.3), so their bits **sum**; the additive total is a
  conservative upper bound (perceptual coupling — overlay tint, crowding, masking — can only lower
  the human's joint and is unmeasured, paper §10), so no coupling discount is applied.
- The **familiar ends** (first/last cells) are **recognition-only, zero credit** — eligible for
  the random pool but never privileged, so an attacker who vanity-grinds the ends gains nothing.
- For **>512-bit** inputs the **fingerprint-middle (Crockford) cells are the mandatory anchor** —
  SHA-512-derived (hard to forge unconditionally) and single-case / homoglyph-clean; head/tail are
  recognition-only.

### 14.3 Directing attention (any feature)

Because the tool renders both figures, it knows the **exact geometry** of every element from the
render model (`describeChannels` + geometry). The §7.1 focus ring therefore **generalises to every
feature**: a computed highlight **around** (never over) the element that carries it — a cell, the
color-bar rect, the ellipse's contour/bounding box, a marker, a blank cell — drawn in the
**tool-controlled container** (fixed integer scale, host-transform-proof), anchored by re-measuring
live geometry. One feature at a time, on **both** figures. For a **gestalt dimension** the ring
lands on its carrying element and a short prompt names the dimension ("same colors, top to
bottom?"; "does the oval lean the same way?").

- **SVG / value reference** (we re-render it): both rings are precise.
- **Raster reference** (an image we can't geometry): we ring **our** side precisely and prompt the
  user to find the matching feature on the reference.

### 14.4 The entry is a binary; Quick/Good are milestones on a continuous scale

There is **no default**: the user **declares** how to check — but the choice is a **binary**, not a
three-way preset, and Quick/Good are no longer buttons. They are **milestones along the spot-check
scale** the user watches themselves cross.

- **Spot-check** — a single **continuous** walk: an unpredictable, weighted-ordered sequence the user
  climbs as far as they like. The coverage meter carries **Quick and Good tick marks**; as checks
  accumulate the user passes them. The walk does **not** stop at Good — the user keeps going for more
  coverage or presses **Done** to stop. (Anti-habituation = the unpredictable order; **no probe**.)
- **Complete** — the deliberate exhaustive read of **every cell, in order**, plus the **planted
  probe** on a large value (§14.7). Distinct from "a spot-check taken to 100%": only Complete
  guarantees every text cell *and* the probe. **It checks NO gestalt dimensions at ≤512 bits** — the
  text is lossless there, so verifying every filled cell already determines the whole value (its
  blanks and every gestalt dimension follow by construction); checking them would be busywork. A
  **>512-bit** Complete *does* add the gestalt CRC, because its displayed text is not lossless.
  Small → lossless certainty; large ≤512 → lossless read of all cells; huge → strongest possible read.
- **Size-aware:** a **small** value (≤ ~6 cells) offers **only Complete** — a spot-check of a handful
  of cells is degenerate (a subset is nearly the whole). Large/huge offer both.

The meter is a **bit-weighted coverage bar** — the fraction of the sequence's *discriminability bits*
confirmed, **not** a count of checks and **not** a `1-in-2^N` probability. Each feature advances it by
its own weight (a confirmed ellipse moves it far more than a confirmed background; a text cell ≈ 24
bits, §7.3), so the bar reflects *how much was actually covered*, not *how many operations ran*. It is
a conservative upper bound, labelled as such (human-error and perceptual-coupling discounts are
unmeasured). The verdict is **coverage-driven**: **below the Good milestone → PENDING** ("a sanity
look"); **at/above it, with the ≥ 2 lossless text floor met → NO-DIFFERENCE**. The Good milestone is a
bit threshold whose front guarantees the text floor + weighted gestalt CRC; the Quick tick is a purely
visual "you've done a peek's worth" mark (still PENDING). **Credit is per-feature bits and 0 for any
non-locally-generated input cell** (a programmable / vanity value can't reach Good through its text —
though a >512-bit one still can via the always-credited fingerprint-middle).

### 14.5 Reporting a check (by mode)

A focused check is reported **Matches / Differs**; *how* depends on the channel:

- **Single-user (visual, both figures in the pinned font, side by side):** **click**. No read-back —
  the pinned high-disambiguation font makes glyph differences (incl. base64url confusables) visible,
  and focusing supplies the attention, so homoglyph erosion ≈ 0.
- **Live + a trusted digital channel:** the parties **copy/paste** the value or comparison text and
  we run the **machine compare** — definitive, no walk. Strictly better than reading aloud whenever
  available. (Pasting the *value* is definitive; pasting *comparison text* is definitive ≤512, but
  only a strong-but-`unknown` match >512 — the text isn't lossless there.)
- **Live, voice-only:** **typed or spoken read-back**. **No NATO**; homoglyphs are *tolerated* and
  compensated by **one extra credited text cell** when the plan contains base64url cells (arbitrary
  text / DID / URN) — never for >512 (the Crockford anchor is clean) or homoglyph-safe alphabets
  (hex, base58, bech32, base32, Crockford, decimal, Ethereum).

### 14.6 Verdict state machine

States: **DIFFERENT** (terminal) · **PENDING** · **NO-DIFFERENCE** (coverage; affirmative-but-
probabilistic) · **IDENTICAL** (machine-only — **never** reachable from a walk; reserve `=`/green
for it).

- **Any feature reported Differs → re-look prompt.** *Maintained* → **DIFFERENT** (terminal,
  certain). *Retracted* ("misclick") → the feature is **re-queued as a fresh check**, never silently
  credited, so a hasty or pressured retraction can't become a free pass. (We never offer a "confirm
  your Matches" — that would coach a stamper.)
- **A gestalt dimension fails-fast to DIFFERENT** like any feature; a *match* contributes
  **coverage** (the whole-value CRC + un-steerable bits), but the **affirmative still requires the
  ≥ 2 lossless text cells** — only text certifies *same value*.
- **Each distinct feature counts once** (no re-credit for re-checking).
- **The verdict is live and coverage-driven, and NO-DIFFERENCE is *not* terminal.** As the user
  checks, the status is **PENDING** below the Good milestone and **NO-DIFFERENCE** at/above it (with
  the ≥ 2 lossless text floor met) — but the walk keeps going. The user can **keep checking past
  Good** (the meter climbs toward a full read) or press **Done** to stop and freeze the current live
  verdict. The walk **ends** on: a confirmed Differs (DIFFERENT), a probe missed twice
  (INCONCLUSIVE), **Done**, or every check completed. Stopping below Good freezes **PENDING** ("a
  sanity look, not a verification") — that is the old "Quick" outcome, now just a place on the scale.
- **Resets:** a **failed optional probe** (Complete) → warn + **reset the meter to 0 → PENDING**
  (graduated: a second miss ends the walk "inconclusive — do this with full attention"); a
  **preceding raster `unknown`** → the walk **starts at 0** (a degraded reference credits nothing,
  §6.3).

### 14.7 Anti-habituation

The primary mechanism is the **unpredictable mixed sequence** (§14.2) — relocation by surprise, not by
ceremony. On top of that, a **transparent planted difference** is **standard for Complete on a
large value** (more than ~10–12 cells, where the exhaustive read is long enough to breed
inattention) and used **nowhere else** (a small Complete is too short to need it; a **spot-check**
relies on its unpredictable order, so it carries no probe).

It is **disclosed, not a hidden decoy**: the tool tells the user it has planted **exactly one**
mismatch and asks them to find it. The outcomes are unambiguous:
- **found zero** → inattention (→ reset, §14.6);
- **found exactly one** → attention calibrated; the planted difference is discarded;
- **found more than one** → the values genuinely differ (a real mismatch avalanches into many).

Because it is disclosed, it adds confidence without the tool ever deceiving the user, and the
single planted difference doubles as the calibration baseline against which any *real* difference
stands out.

### 14.8 Messaging & mental model

How the walk is framed teaches a mental model and calibrates risk, so it is a **first-class part
of the design, not chrome**. Three ideas the copy must instill:

- **A glance can miss things; this walk *aims* your attention so you actually see.** (Counters the
  "I looked, it's fine" overconfidence the paper warns of.)
- **Text *proves* a slice of the value; the picture *covers* the whole** — a real difference lights
  up many features at once.
- **You declared the standard; here is exactly what it does and does not promise.**

Per-preset framing (illustrative; localized per the pill catalog):

- **Quick** — "A sanity peek, not a verification — this proves nothing on its own."
- **Good** — "We'll aim your attention at a handful of unpredictable features — some exact text,
  some whole-figure patterns. If anything differs, stop. A strong spot-check, not exhaustive."
- **Complete (large)** — "We'll walk every cell in a deliberate, unpredictable order, and we've
  planted **exactly one** difference — find it. Find none and you weren't really looking; find more
  than one and the values genuinely differ."
- **Complete (small)** — "This value is small enough to verify in full; we'll read every cell."

Verdict copy keeps the §3 discipline: reserve `=` / green / the word "identical" for a **machine**
result; a human walk yields **"no difference found across what you checked,"** never certainty.

### 14.9 Out of scope → M3 (the live ceremony, §8)

The **committed seed + reveal**, the **channel-authentication affirmation** (what the UX requires to
treat the out-of-band channel as authenticated), and the **copy/paste-vs-voice routing** for the
*remote* ceremony are M3. This section specifies the single-user walk and the shared mechanics
(plan, focus, presets, state machine) the ceremony reuses.

---

## 15. M3 — the remote voice ceremony (pinned specification)

This section pins the two-party remote ceremony for implementation. It is the outcome of a
first-principles re-derivation (recorded 2026-07) that **scopes §8 down substantially** — it does
**not** reverse any adjudicated decision, but it dissolves most of §8's machinery by correcting an
assumption §8 left implicit. Read §15.9 for exactly how it relates to §8.

### 15.1 Scope and threat posture (what changed from §8)

- **One-way authentication only.** The ceremony answers a single question for **one** party — *"does
  the value I hold match the value the person on the call actually has?"* Mutual confirmation is that
  same ceremony run twice, once in each direction; there is no joint protocol. The party running our
  tool is the **authenticator**; the other party is the **reader**.
- **The reader may have no software.** We assume only that the reader *has an entviz in front of
  them* — our render, a third-party render, a screenshot, or a printout. The **sole capability we can
  assume of the reader is that they can read glyphs off that artifact aloud.** They cannot compute,
  commit, generate entropy, or run a protocol. This is the assumption §8 left implicit, and it is
  what forces everything below.
- **What the ceremony proves is narrow and precise: *the party on the other end of this voice channel
  is looking at the same value I am.*** It does **not** bind that value to a *human identity*, and it
  does not require the reader to be someone the authenticator knows — or even to be a human. *Who* holds
  the matching value is a separate question the authenticator answers with additional context
  (recognizing a voice, a credential, a prior relationship); the protocol supplies the equality, not
  the identity. (This corrects an earlier framing that gated the ceremony on "do you recognize them" —
  an identity claim the protocol cannot make. See §15.10.)
- **The voice channel carries the whole guarantee, so its integrity is the load-bearing assumption.**
  Any digital channel between the parties (SMS, chat, email, a paste) is a **fat but unauthenticated
  pipe** an attacker can modify. A man-in-the-middle is ruled out only to the extent the **voice
  channel itself has integrity** — a MITM would have to control **both** the voice channel **and** the
  channel that delivered the reference value. Confidentiality is not required.
- **The live threat is a substitution/relay** (a value swapped in transit, or read-back injected on the
  voice channel, so the check passes against a value that is not the reader's). A **dishonest reader
  who lies about their own artifact** is *out of scope* — the endpoint-trust / reference-authenticity
  limit (§2.5, §2.4); the ceremony is sound only *conditioned on* the reader honestly reading what they
  see, and on the voice channel not being silently substituted.

### 15.2 What the ceremony does NOT contain — and why

Three pieces of §8 are **removed**, each for a concrete reason:

- **No seed.** The seed's only security job was to defeat a **last-mover steering** a *jointly-seeded*
  check-order. When only the authenticator selects checks, there is no joint seed and no last mover —
  the threat §8 named does not arise. The residual "unpredictability" (so the reader can't pre-forge
  the sampled cells) is supplied by the **authenticator freely choosing which cells to ask for, live**
  — selection-unpredictability need not survive a hostile *local* tool (unlike a value's entropy), so
  a human choice suffices.
- **No commit-and-reveal.** Commitment binds *two* contributions to one seed; with no joint seed there
  is nothing to commit. (It survives only for the hypothetical symmetric "walk-together" mode of
  §15.9, which we are not building.)
- **No synthetic SAS.** An earlier design carried a short Crockford *digest* read aloud. A digest is
  SHA-derived — **not printed on the entviz** — so reading it requires the reader to *compute* it,
  which a human with a picture cannot do. It is inapplicable to the baseline of §15.1 and is dropped
  entirely. Its job is done instead by reading **rendered** features (§15.3).

### 15.3 The primitive: authenticator-directed read-back of visible features

> The authenticator points at a **rendered feature**; the reader reads it aloud; the authenticator
> compares it to their own value. The machine covers the rest when a paste is available.

Everything the reader ever does is read glyphs off the artifact in front of them — **never** a gestalt
characterization ("which corners have triangles, what colors" is unusable over voice) and **never** a
computed value. The authenticator selects features **live and unpredictably** (their free human
choice is the anti-pre-forge mechanism). The paste never authenticates anything — it is an efficiency
accelerator (§15.6); the authentication is **100% the voice channel**.

### 15.4 When sampling is sound (the governing rule)

Reading a *subset* (a row, a column, the fingerprint cells) is sound **only when a difference cannot
hide in the cells not read.** That holds in exactly two situations, and nowhere else:

1. **Constrained values** (keys, hashes, CESR, UUIDs, safety-numbers, `did:key` — the common,
   security-critical case). Two distinct *usable* values differ in **~all** cells (forging a cell is
   the 2²⁰–2²⁴ partial preimage of §2.3/§7.3), so any sample catches a substitution.
2. **Hash-derived anchor cells** (the fingerprint-middle Crockford cells on >512-bit inputs). They are
   SHA-512 of the whole value, so **any** difference avalanches into them — a sample of them catches
   even a single-character near-collision.

Sampling is **unsound** for **programmable / attacker-authored values** that lack a hash anchor
(arbitrary text, a `did:web` domain, a URL): there an attacker can craft a one-cell near-collision
(`example.com` vs `examp1e.com`) that a sampled row/column misses. Those must be **read in full** or
routed to the paste path. The tool already distinguishes constrained from programmable by the
**parser-derived type** (the same signal that credits programmable cells 0 in the meter, §7.3).

### 15.5 Read-back strategy (size × type)

| value | read-back |
|---|---|
| **small** (≤ ~6 cells, any type) | read **all cells** (also the only sound option — nothing to sample) |
| **medium, constrained** | read a run of **4 consecutive cells** from a live/unpredictable start (reading order, may span rows — robust where a single row is too narrow), each named by its grid address ("row 1, column 2") |
| **medium, programmable** | read **all cells** (or paste + machine) — no sound sample exists |
| **big** (>512-bit, any type) | read the **fingerprint-middle Crockford cells** (hash-anchored, single-case, homoglyph-clean) |
| **paste available** (any) | machine does the exhaustive compare; authenticator asks for **1–2 cells** to bind it to the live person (a fingerprint cell if the value is programmable) |

Homoglyph handling follows §14.5: voice read-back **tolerates** confusables and compensates with
**one extra credited base64url cell** when the plan contains base64url cells; the Crockford anchors
and homoglyph-safe alphabets need no compensation.

### 15.6 The two modes

Both modes anchor authentication on the voice channel; they differ only in how much crosses it.

- **Paste-accelerated** (the reader can transmit their value as *text* over any digital channel):
  the authenticator pastes it into the existing acquisition field (§5), the machine does the exact,
  homoglyph-free whole-value compare, and the authenticator then issues **1–2 live voice challenges**
  to bind the pasted bytes to the reader's real artifact (hash-anchored feature if programmable).
  Strictly better than reading everything aloud — but *not* "paste and done": without the voice
  binding, a substituted paste (§15.1) passes the machine compare undetected.
- **Voice-only** (the reader has only a picture, or only a voice call): the value itself must cross
  voice, so the reader reads the features of §15.5 aloud and the authenticator checks each against
  their own value.

### 15.7 Roles, verdict, and evidence

- **Local-only evidence** (§8): the verdict lives **only on the authenticator's endpoint**. Because
  the ceremony is one-way, we surface **no peer verdict at all** — there is nothing to be tempted to
  trust from the other screen.
- **Verdict states reuse §14.6**: DIFFERENT (a confirmed mismatch, terminal, certain) · PENDING ·
  **NO-DIFFERENCE** (coverage-driven, affirmative-but-probabilistic). The ceremony **caps at
  NO-DIFFERENCE and never reaches IDENTICAL** — even in paste-accelerated mode, where the machine
  reports IDENTICAL for the *pasted* value, the load-bearing claim ("that pasted value is the reader's
  real value") rests on a human read-back, so the ceremony's affirmative is coverage, never `=`.
- Verdict copy keeps the §3 discipline and always carries the §15.1 conditional — scoped to what the
  protocol actually promises: *the other party is looking at the same value; this says nothing about
  who they are, and holds only if your voice channel has integrity.*

### 15.8 UI surface

The remote ceremony is a **top-level situational choice** peer to "I have something to check against"
(§4). It is presented as one of **two tabs** at the top of `<EntvizCompare>` — **"Compare
visualizations"** (the machine/reference path + guided walk) and **"Compare by voice"** (this
ceremony) — so the two situations read as equal siblings, both always reachable, sharing the one
"Yours" value and its size/shape toolbar. (An earlier draft put voice as a labeled button below the
acquisition field; tabs model the peer choice better and keep the voice path visible even with no
reference.) The voice tab opens on the §15.10 expectation-setting gate, then runs the guided read-back
(reusing the walk's focus-ring overlay on our own figure). `paste-bind` is selected automatically when
a pasted reference already machine-matched as `identical`; otherwise `voice-only`.

### 15.9 Relationship to §8 (scoping, not reversing)

§8's "commit-and-reveal is the default" is **correct for a symmetric, jointly-seeded "walk-together"
ceremony** — one shared check-order both parties follow in lockstep, where a last-mover genuinely can
steer. This addendum observes that such a ceremony (a) is impossible when the reader has no software
and (b) is the only topology that needs the heavy machinery — so it is **not the primitive.** The
shipped ceremony is the **unilateral, authenticator-directed** one above, which removes the steering
threat *by construction* (no joint seed) rather than by commitment. The symmetric walk-together mode
with its §8 commitment default remains available as a future option, deferred; nothing here weakens
it.

### 15.10 Equality, not identity (the refined guarantee)

A second refinement (recorded 2026-07) sharpens *what the ceremony claims*. It proves exactly one
thing: **the party on the other end of the voice channel is looking at the same value as the
authenticator.** It deliberately does **not**:

- **bind that value to a human identity** — the reader need not be known to the authenticator, need not
  appear on video, need not even be a human. *Who* holds the matching value is the authenticator's own
  judgment from outside context (a recognized voice, a credential, a prior relationship); the protocol
  supplies equality, and the human supplies identity.
- **rule out a man-in-the-middle unconditionally** — a MITM is excluded only insofar as the **voice
  channel has integrity**, because defeating the check requires controlling *both* the voice channel
  *and* the channel that delivered the reference value.

This retired the earlier **"do you recognize them — voice or face?" affirmation gate**, which
overclaimed: it dressed an identity assertion the protocol can't make as a precondition, coaching false
confidence. The gate is replaced by an **expectation-setter** that states the guarantee and its two
limits plainly, then asks only to *proceed*:

> *Comparing by voice checks one thing: whether the person on your call is looking at the same value as
> you. It can't tell you who they are — that's for you to judge — and it can only rule out a
> man-in-the-middle if your voice channel itself has integrity. Proceed?*

Practically this also **widens applicability**: the ceremony is meaningful over a voice-only call, with
a stranger, or with an automated reader, so long as the authenticator understands it is checking value
equality — not vouching for the other party.
