# 2026-07-11 — Correction: identity-linking market refusal is a PREDICATE, not an assumption

Anchored independently (see `calibration/ANCHORS.md`). This document CORRECTS one parenthetical
claim in `calibration/identity_linking_2026-07-11.md` and fixes the operative rule. It does not
modify that file — its OpenTimestamps-anchored digest
(`51d7f3bdf086d12c8b553f95480a5c53b2f67d38d5e1fbb95dcec6a5565287d8`) remains byte-identical. Nothing
here touches a lock, `locks.log`, `certified_results`, or any statewide client-facing number.

## What was wrong

The 2026-07-11 methodology said, of the person layer:

> **us_arizona and us_sc have no identity links** (no confirmed account there carries an exact
> cross-handle bio reference to another confirmed account), so their equal-weight companion numbers
> are provably unchanged — pinned by test.

The parenthetical is an empirical claim, and it was **falsified on the first live run**: real AZ
bios DO carry exact cross-handle references. Running identity linking over `us_arizona` created
**18 `identity_link` rows (9 `person_entity` rows)** before the guard existed. They were found and
**deleted**; a read-only re-verification on 2026-07-11 confirms `us_arizona` and `us_sc` now hold
**zero** `identity_link` and **zero** `person_entity` rows (and zero orphaned persons globally).

## The operative rule (this correction)

Market refusal is a **PREDICATE evaluated against ledger state**, not a name list and not an
assumption that a market "has no references." A market is **refused** from identity linking (and,
per the standing rule recorded in BUILD_NOTES, from every prod-touching dome-scale module) when
EITHER:

1. **deploy-guard predicate** — it has a LOCKED prediction with no certified result yet (it is
   inside an open lock-to-result window). This refuses `us_arizona`.
2. **scored-pairs predicate** — it has SCORED calibration pairs (a certified result recorded and
   scored; its case-study numbers are settled). This refuses `us_sc`.

Implemented in `sonar/calibration/frozen_markets.py` (`refusal_reason` / `is_market_refused`),
replacing the dome-scale-1 name-only refusal `{us_arizona, us_sc}`. Both predicates are pinned by
test (`sonar/tests/test_frozen_markets.py`), and identity linking's refusal is pinned on real
predicate conditions (`sonar/tests/test_identity_linking.py`). Because refusal is now driven by the
same ledger state that freezes a market's numbers, the guarantee is **structural and general**: any
market whose numbers become anchored/settled is refused automatically, with no code change and no
reliance on an assumption about its bios.

## Why this is stronger

An assumption ("this market has no links") can be falsified by data, as it was. A predicate over the
freeze state cannot: a market with a result-pending locked prediction, or with scored pairs, is
refused regardless of what its bios contain. The equal-weight companion for `us_arizona` and `us_sc`
is therefore provably unchanged by construction, not by luck.
