diff --git a/docs/decisions/ADR-0164.2-pronoun-entity-resolution.md b/docs/decisions/ADR-0164.2-pronoun-entity-resolution.md new file mode 100644 index 00000000..a409a9ec --- /dev/null +++ b/docs/decisions/ADR-0164.2-pronoun-entity-resolution.md @@ -0,0 +1,573 @@ +# ADR-0164.2 — Pronoun / Entity Resolution Policy + +**Status:** Proposed +**Date:** 2026-05-26 +**Author:** Shay +**Anchor:** [[thesis-decoding-not-generating]] +**Parent:** [ADR-0164 — Incremental Comprehension Reader](./ADR-0164-incremental-comprehension-reader.md) +**Companion:** [ADR-0164.3 — ProblemReadingState and cross-sentence registry](./) +**Resolves:** ADR-0164 §Open question #3 ("Pronoun-entity resolution") + +--- + +## Context + +ADR-0164 replaces the regex sentence-template front-end with an incremental +compositional reader. Open question #3 of that ADR notes that the reader needs +entity resolution and that the regex parser's +`_resolve_question_entity` heuristic (`generate/math_candidate_parser.py:2457`) +is "a reasonable starting point but should be reviewed against the +compositional model." This ADR carries out that review and specifies the +replacement policy. + +Pronoun and entity resolution is load-bearing for GSM8K. A large fraction of +question sentences refer to the subject of preceding statements only by +pronoun ("how much money does **she** make?", "how much will it cost **him**?", +"how much weight does **he** have to lose?"). A reader that comprehends the +whole problem but refuses on the question because it cannot resolve the +question's subject pronoun is a reader that has failed at exactly the moment +it needed to commit. + +The replacement must hold to the same invariants ADR-0164 holds to: +deterministic, no hidden normalization, no stochastic fallback, refuses +cleanly on novel structure, and produces evidence the teaching loop can +chew on (typed refusal reasons with position information). + +## §1 — Review of the existing heuristic + +The function under review is `_resolve_pronoun_entity` (lines 2413–2454) and +its wrapper `_resolve_question_entity` (lines 2457–2477). Its behavior: + +1. The pronoun surface form is lower-cased. +2. `they` and `it` are unconditionally refused. +3. `she` / `her` look up the `_FEMALE_NAMES` whitelist (62 entries, line + 2376). +4. `he` / `his` / `him` look up the `_MALE_NAMES` whitelist (76 entries, line + 2389). +5. The full problem text is scanned with a Title-cased proper-noun extractor + (`_PROPER_NOUN_MENTION_RE`, line 2408). +6. Distinct names matching the gender whitelist are accumulated in order of + first mention. +7. **Exactly one** match → resolve to that name. Zero or two-plus → refuse. + +### §1.1 What this heuristic gets right + +- **Refuse-on-ambiguity is the correct default under wrong = 0.** Returning + the wrong entity binding produces a candidate that the binding graph will + happily admit and the verifier may not catch (the verifier checks unit + closure and arithmetic, not whether the question subject was the right + person). Refusal flows to the teaching loop instead. +- **Pure and deterministic.** No global state, no normalization side effects, + no nondeterministic name picking. +- **Closed-set vocabulary.** The name whitelists are ratified data; they are + the seed for the operational lexicon ADR-0164 promotes them into. +- **Plural and neuter refusal.** `they` and `it` are not pretended-resolved. + +### §1.2 What it gets wrong + +Five concrete failure modes are observable on the GSM8K train_sample: + +**F1 — Whitelist closed-set gap.** The proper-noun extractor finds names that +are not on either whitelist (Yun, Marion, Rex, Georgie, Allison in +train_sample). When the question uses a pronoun whose only entity candidate +is one of these, the heuristic refuses despite the antecedent being +unambiguous in context. + +- *Example.* Case 0034 ("Georgie is a varsity player on a football team. + **He** can run 40 yards within 5 seconds. … how many yards will **he** be + able to run within 10 seconds?"). `Georgie` is not in `_MALE_NAMES`. + Distinct-males-matching = 0. Refuses with no candidate. The text is + unambiguous: Georgie is the only animate proper noun in the problem. + +**F2 — Multi-entity question text where recency disambiguates.** The +heuristic counts *distinct* mentions over the whole problem and refuses on +≥2. Recency, syntactic role, and the question's own subject slot are +discarded. + +- *Example.* Case 0017 ("Jason has a carriage house … Eric wants to rent the + house for 20 days. How much will it cost **him**?"). Two males (Jason, + Eric). Distinct count = 2 → refuses. But "Eric wants to rent" is the only + agent-of-renting clause; "cost him" attaches to the renter. A recency- or + role-aware policy resolves this. The current heuristic cannot. + +**F3 — Plural antecedent of a group introduced by conjunction.** `they` is +unconditionally refused. When a problem opens with a conjoined subject +("Aaron and his brother Carson each saved up $40 … how many scoops did +**they** each buy?"), the group is well-defined and the question is +well-posed. Refusing collapses a solvable case. + +- *Example.* Case 0026. Distinct males = 2 (Aaron, Carson). On a per-name + question this is ambiguous; on `they` it is exactly the group. The + heuristic refuses both ways. + +**F4 — Kinship and relational entities are invisible.** "her grandfather", +"her father", "her mother", "his brother", "his friend" introduce entities +that participate in arithmetic but never enter the registry. The Title-cased +extractor sees only proper nouns. + +- *Example.* Case 0033 ("Rachel is 12 years old, and **her grandfather** is + 7 times her age. **Her mother** is half grandfather's age, and **her + father** is 5 years older than her mother. How old will Rachel's father be + when **she** is 25 years old?"). The statement-layer "her" inside "her + grandfather" has no referent to bind to *as a self-standing entity*; it is + a relational modifier. The question-layer "she" has Rachel as the unique + female proper noun — both policies resolve here — but the underlying + reading is wrong: the heuristic treats "her grandfather" as a + document-internal mention rather than as a relational entity rooted at + Rachel. + +**F5 — Inferred-gender names are not learned in context.** If the same +unknown name appears with `he` ten times across a problem, the heuristic +still cannot resolve any of them, because gender is only inferred from the +whitelist. There is no co-reference feedback. + +- *Example.* Case 0034 again — every "he" in the text is structurally + unambiguous given the single proper noun. The heuristic learns nothing + from this. + +### §1.3 Summary verdict + +The heuristic is correct in policy (refuse on ambiguity, deterministic, +whitelist-based, closed-set) and inadequate in representation. Its evidence +about pronouns lives in a flat scan of the entire problem text; the +compositional reader has, by Phase 1, an ordered stream of comprehended +entities with positions. The heuristic should be replaced by a policy that +consumes that stream — same conservatism, more information. + +## §2 — Replacement: EntityRegistry as a field on ProblemReadingState + +The reader maintains a `ProblemReadingState` across sentences (ADR-0164.3 +specifies this carrier in full). One field of that state is an +`EntityRegistry`: + +```text +EntityRegistry: + entries: tuple[EntityEntry, ...] # frozen tuple, append-only per sentence + resolution_log: tuple[Resolution, ...] # per-pronoun decision evidence + +EntityEntry: + canonical_name: str # the surface lemma as comprehended + gender_inferred: Gender # FEMALE | MALE | NEUTER | GROUP | UNKNOWN + gender_source: GenderSource # LEXICON_NAME | KINSHIP | PRONOUN_BACKFILL | DECLARED + first_mention_pos: SourcePosition # (sentence_idx, token_idx) + last_mention_pos: SourcePosition # updated on each subsequent mention + relational_anchor: EntityRef | None # for kinship/relation entities ("her grandfather" → anchor=Rachel) + syntactic_roles: tuple[Role, ...] # SUBJECT, OBJECT, POSSESSOR, … + +Resolution: + pronoun_surface: str # "she", "him", … + pronoun_pos: SourcePosition + decision: ResolvedEntity | RefusalReason +``` + +The registry is part of the reader's immutable state per ADR-0164 §Decision. +Every entity entrance and every pronoun resolution appears in the log; the +log is part of the canonical-bytes serialization that drives `trace_hash`. + +### §2.1 How entities enter the registry + +The reader's lexicon (ADR-0164 §Decision §Operational lexicon) already +distinguishes `proper_noun_entity`, `entity_pronoun`, and the kinship / +relational category set. Entry into the registry happens at three points: + +1. **Proper-noun entity.** When a token's category is `proper_noun_entity`, + a fresh entry is appended. Gender is inferred from + `lexicon[token].gender_marker` if present; otherwise `UNKNOWN`. Source = + `LEXICON_NAME`. + +2. **Kinship / relation noun with possessor.** When a possessive determiner + ("her", "his", "their", "Rachel's") attaches to a kinship noun in the + lexicon's `kinship_relation` category, a fresh entry is appended with + `relational_anchor` = the resolved possessor and `gender_inferred` = + `lexicon[kinship_noun].gender_marker` (mother → FEMALE, father → MALE, + sibling → UNKNOWN, friend → UNKNOWN, etc.). Source = `KINSHIP`. + +3. **Conjoined subject.** When the reader closes a noun-phrase frame opened + by ` and `, a `GROUP` entry is appended whose membership + tuple holds the two component entries. The component entries remain + separately registered; the group entry is what `they` resolves against. + Source = `LEXICON_NAME` (for the conjunction frame closure). + +The registry is **append-only per sentence**. Re-mentions update +`last_mention_pos` and may add a syntactic role, but do not create a new +entry. + +### §2.2 How pronouns resolve + +When the reader's category for the current token is `entity_pronoun`, the +policy is: + +1. Compute `compat_gender`: FEMALE for `she/her/hers/herself`; MALE for + `he/him/his/himself`; GROUP for `they/them/their/themselves`; NEUTER for + `it/its/itself`. +2. Filter registry entries: keep entries `E` where + `compatible(E.gender_inferred, compat_gender)`. The compatibility table + is: + - FEMALE ↔ FEMALE + - MALE ↔ MALE + - GROUP ↔ GROUP + - GROUP ↔ {FEMALE, MALE} only if the group's components include at least + one member of the requested gender (used for "she" picking out the + female member of a mixed group; not currently used at Phase 1). + - NEUTER ↔ NEUTER (excludes animates; used for cost/object/process + antecedents) + - UNKNOWN ↔ {FEMALE, MALE}: tentatively compatible, see step 5. +3. If the filtered list is empty: **refuse** with reason + `unresolved_pronoun`. Position evidence = pronoun_pos and the registry + snapshot. +4. If the filtered list has more than one **certain-gender** entry: apply + the recency tiebreaker (§2.3). If the tiebreaker leaves two entries + within the ambiguity window: **refuse** with reason + `ambiguous_pronoun_referent`. +5. If the only candidate is `UNKNOWN`-gendered and exactly one such entity + exists in the entire registry (the "single salient entity" case): bind + the pronoun to it AND back-fill the entity's `gender_inferred` = + `compat_gender`, `gender_source = PRONOUN_BACKFILL`. This addresses F1 + and F5 without weakening conservatism: the back-fill triggers only when + the entity is *uniquely available*. +6. Otherwise (multiple UNKNOWN entries, no certain-gender candidate): refuse + with `ambiguous_pronoun_referent`. + +Every branch appends a `Resolution` to the log with full position and +candidate set so the teaching loop sees exactly why a binding succeeded or +refused. + +### §2.3 Recency tiebreaker + +When ≥2 certain-gender entries match: + +- Compute `last_mention_distance(E) = pronoun_pos − E.last_mention_pos` in + token units (across sentences, sentence boundaries count as a fixed token + penalty `SENTENCE_PENALTY = 1` to make cross-sentence reach explicit and + bounded). +- The candidate with the **smallest** distance wins, **provided** the next + candidate is at least `RECENCY_GAP_MIN = 2` further. Otherwise the two + candidates are within the ambiguity window and the policy refuses. +- Subject-role mentions count once; object/possessor mentions are not + preferred or demoted (no syntactic bias at Phase 1 — adding one is a + separate sub-ADR with measurement). + +The constants `SENTENCE_PENALTY` and `RECENCY_GAP_MIN` are part of the +ratified policy. Changing either requires a sub-ADR and re-running the +capability lanes. + +## §3 — Refusal-first ambiguity rules + +Two rules are exhaustive at Phase 1: + +### Rule R1 — `ambiguous_pronoun_referent` + +**Trigger.** ≥2 certain-gender registry entries are compatible with the +pronoun and the recency tiebreaker does not separate them by at least +`RECENCY_GAP_MIN` tokens. + +**Concrete example.** Hypothetical mini-text (from real GSM8K phrasing): + +> "John gave Tom $5. **He** had $20 left." + +Registry after sentence 1: `[John:MALE@(0,0), Tom:MALE@(0,2)]`. The +question-level pronoun "He" has `last_mention_distance(John) = 4`, +`last_mention_distance(Tom) = 2` (assuming a 4-token sentence). Tom is +nearer by 2. With `RECENCY_GAP_MIN = 2`, the gap is exactly the threshold; +the *strict* form (`gap > MIN`) refuses. The *permissive* form (`gap >= MIN`) +resolves to Tom. ADR-0164.2 adopts the **strict** form: refuse, surface +both candidates, let the teaching loop or a future role-aware sub-ADR +disambiguate. This preserves wrong = 0 in exchange for one refused case. + +### Rule R2 — `unresolved_pronoun` + +**Trigger.** No registry entry is compatible with the pronoun. + +Two sub-cases: + +- **R2a — no entity of the right gender.** E.g. `she` with only male + entries. Refuses immediately. +- **R2b — empty registry.** E.g. a question that opens with a pronoun + whose antecedent is in a prior sentence the reader has not yet + comprehended (reader-order bug, or genuinely orphaned pronoun). Refuses + immediately. + +R2 refusals carry the registry snapshot in the log so the teaching loop can +see whether the gap was a missing entity, a missing kinship inference, or a +missing gender lexicon entry. + +### What is **not** a separate rule + +- "Plural pronoun with no group entity" → R2 (`they` with only singletons + and no conjunction-closed group). +- "Neuter pronoun with only animate entries" → R2. +- "Possessive pronoun in a kinship phrase" (e.g. "her grandfather") is not + a resolution event; it is an entity-creation event (§2.1 case 2). No rule + applies. + +## §4 — Worked walk-through on five GSM8K train_sample cases + +Notation: positions written `(sent, tok)`. Lexicon assumed: female-name +markers for Tina, Marion, Erica, Martha, Jane; male-name markers for +Malcolm, Aaron, Carson, Jason, Eric, James, John; UNKNOWN for Georgie. +Kinship lexicon: brother → MALE. + +### 4.1 — Case 0001 (Tina, multi-pronoun) + +Text (question only, for brevity): + +> "Tina makes $18.00 an hour. If **she** works more than 8 hours per shift, +> **she** is eligible … If **she** works 10 hours every day for 5 days, +> how much money does **she** make?" + +Registry trajectory: + +| Step | Event | Registry | +|---|---|---| +| 1 | Token "Tina" at (0,0) | `[Tina:FEMALE@(0,0)/LEXICON_NAME]` | +| 2 | Pronoun "she" at (1,2) | Filter FEMALE → {Tina}. Single candidate. **Resolve → Tina.** `last_mention(Tina) ← (1,2)`. | +| 3 | "she" at (1,7) | Same → Tina. | +| 4 | "she" at (2,2) | Same → Tina. | +| 5 | "she" at (2,11) | Same → Tina. | + +All four pronouns resolve. Final binding for the question slot: Tina. Old +heuristic: identical outcome. **Agreement.** + +### 4.2 — Case 0010 (Yun, Marion — no question pronoun) + +Text: + +> "Yun had 20 paperclips initially, but then lost 12. Marion has 1/4 more +> than what Yun currently has, plus 7. How many paperclips does **Marion** +> have?" + +The question is a direct proper-noun binding (no pronoun). Registry: + +| Step | Event | Registry | +|---|---|---| +| 1 | "Yun" at (0,0) | `[Yun:UNKNOWN@(0,0)/LEXICON_NAME]` (Yun absent from name lexicon → UNKNOWN) | +| 2 | "Marion" at (1,0) | `[Yun:UNKNOWN, Marion:UNKNOWN@(1,0)]` | +| 3 | Question "Marion" at (2,5) | Proper-noun direct binding (no pronoun → no Rule). `last_mention(Marion) ← (2,5)`. | + +No pronoun resolution required. Question slot binds to Marion directly. Old +heuristic: same. **Agreement.** This case demonstrates that the policy does +not perturb proper-noun-only questions. + +### 4.3 — Case 0027 (Malcolm, "he") + +Text (compressed): + +> "Malcolm has 240 followers on Instagram and 500 followers on Facebook. +> The number of followers **he** has on Twitter is half … and **he** has +> 510 more followers on Youtube than **he** has on TikTok. How many +> followers does Malcolm have on all his social media?" + +Registry: + +| Step | Event | Registry | +|---|---|---| +| 1 | "Malcolm" at (0,0) | `[Malcolm:MALE@(0,0)/LEXICON_NAME]` | +| 2 | "he" at (1,7) | Filter MALE → {Malcolm}. Resolve. | +| 3 | "he" at (1,18) | Same. | +| 4 | "he" at (1,22) | Same. | +| 5 | "Malcolm" at (2,4) | Proper-noun direct binding (question). | + +All pronouns resolve to Malcolm. Old heuristic: same. **Agreement.** + +### 4.4 — Case 0017 (Jason and Eric, "him") + +Text: + +> "Jason has a carriage house that he rents out. He's charging $50.00 per +> day or $500.00 for 14 days. Eric wants to rent the house for 20 days. How +> much will it cost **him**?" + +Registry trajectory (showing only entries/updates relevant to "him"): + +| Step | Event | Registry | +|---|---|---| +| 1 | "Jason" at (0,0) | `[Jason:MALE@(0,0)]` | +| 2 | "he" at (0,6) | Filter MALE → {Jason}. Resolve. `last_mention(Jason) ← (0,6)` | +| 3 | "He" at (1,0) | Same → Jason. `last_mention(Jason) ← (1,0)` | +| 4 | "Eric" at (2,0) | `[Jason:MALE, Eric:MALE@(2,0)]` | +| 5 | "him" at (3,7) | Filter MALE → {Jason, Eric}. `dist(Jason, him) = (3,7) − (1,0)` = ~12 tokens + 2*SENTENCE_PENALTY = 14. `dist(Eric, him) = (3,7) − (2,0)` = ~7 + SENTENCE_PENALTY = 8. Gap = 6 ≥ RECENCY_GAP_MIN. **Resolve → Eric.** | + +Old heuristic: distinct males = 2 → refuses (F2). New policy: resolves to +Eric via recency, with the gap comfortably above the ambiguity window. The +ground-truth solver path requires the cost to be billed to Eric (the +renter), so the binding is correct. **Disagreement; new policy correct.** + +### 4.5 — Case 0033 (Rachel + kinship) + +Text: + +> "Rachel is 12 years old, and **her grandfather** is 7 times her age. +> **Her mother** is half grandfather's age, and **her father** is 5 years +> older than her mother. How old will Rachel's father be when **she** is 25 +> years old?" + +Registry trajectory: + +| Step | Event | Registry | +|---|---|---| +| 1 | "Rachel" at (0,0) | `[Rachel:FEMALE@(0,0)]` | +| 2 | Possessive "her" + kinship "grandfather" at (0,4–5) | Possessor resolves (FEMALE filter → Rachel). Kinship entry created. `[Rachel:FEMALE, Rachel.grandfather:MALE@(0,5)/KINSHIP, anchor=Rachel]` | +| 3 | Possessive "Her" + "mother" at (1,0–1) | Anchor=Rachel. `[…, Rachel.mother:FEMALE@(1,1)/KINSHIP]` | +| 4 | Possessive "her" + "father" at (1,7–8) | Anchor=Rachel. `[…, Rachel.father:MALE@(1,8)/KINSHIP]` | +| 5 | Possessive "Rachel's" + "father" at (2,2–3) | Re-mention; `last_mention(Rachel.father) ← (2,3)`. | +| 6 | "she" at (2,8) | Filter FEMALE → {Rachel, Rachel.mother}. dist(Rachel) measured from last_mention (0,0)+penalties; dist(Rachel.mother) measured from (1,1)+penalties. Rachel.mother is closer. Gap above RECENCY_GAP_MIN? **No — Rachel.mother's last mention is far back, Rachel has not been re-mentioned by name since (0,0).** Compute: Rachel.mother at (1,1), Rachel at (0,0). Pronoun at (2,8). dist(Rachel) ≈ 8 + 2 = 10. dist(Rachel.mother) ≈ 7 + 1 = 8. Gap = 2 == RECENCY_GAP_MIN. Strict form: **refuse with `ambiguous_pronoun_referent`**, candidates {Rachel, Rachel.mother}. | + +Old heuristic: distinct female proper names = 1 (Rachel) → resolves to +Rachel. New policy: refuses on ambiguity because kinship-introduced female +entities are in scope. The ground truth is Rachel ("when she is 25"). The +new policy is **more conservative** here: it refuses a case the heuristic +would have resolved correctly, but does so by surfacing a genuine +ambiguity (the surface form does not exclude "when her mother is 25" as a +reading). The refusal flows to the teaching loop and motivates the +role-aware extension (subject-bias for the original named entity) as a +later sub-ADR. **Disagreement; new policy refuses, old policy resolves +correctly by luck — see §5 for the wrong = 0 reasoning.** + +## §5 — Disagreement enumeration + +Three disagreements between the old heuristic and the new policy are +enumerated below. Verdict is given under the wrong = 0 discipline (correct, +refuse, or wrong). + +### D1 — Case 0017 (Jason + Eric, "him") + +| Policy | Output | Verdict | +|---|---|---| +| Old heuristic | refuse (2 distinct males) | refuse | +| New policy | resolve → Eric (recency) | **correct** | + +**Reasoning.** The new policy uses information the old does not (mention +order), preserves refuse-on-ambiguity for the close-call window, and +resolves where the gap is wide. wrong = 0 is preserved because the gap +threshold (`RECENCY_GAP_MIN`) is set strictly enough that close cases still +refuse. + +### D2 — Case 0034 (Georgie, "he") + +| Policy | Output | Verdict | +|---|---|---| +| Old heuristic | refuse (Georgie ∉ `_MALE_NAMES`) | refuse | +| New policy | resolve → Georgie via single-salient-entity back-fill (§2.2 step 5) | **correct** | + +**Reasoning.** Georgie is the only animate entity in the problem; "he" has +exactly one possible antecedent. The back-fill is conservative because it +only fires when there is *exactly one* UNKNOWN-gendered entity in the +entire registry. The old heuristic refused because the closed-set name +whitelist did not contain Georgie. wrong = 0 is preserved because the +single-salient rule cannot bind a pronoun to a wrong entity — there is no +other entity to be wrong about. + +### D3 — Case 0026 (Aaron and Carson, "they") + +| Policy | Output | Verdict | +|---|---|---| +| Old heuristic | refuse (`they` unconditionally refused) | refuse | +| New policy | resolve → group {Aaron, Carson} via conjunction-closed GROUP entry (§2.1 case 3) | **correct** | + +**Reasoning.** "Aaron and his brother Carson each saved up $40" closes a +conjunction frame that registers a GROUP entry. The question "how many +scoops did they each buy?" filters to GROUP entries and finds the unique +match. The downstream binding produces the per-member result via the +distributive modifier `each`. The old heuristic refused on every `they` +regardless of registry state. wrong = 0 is preserved because GROUP +resolution is enabled only when a conjunction frame has closed; absent that +frame, `they` falls through to R2. + +### D4 — Case 0033 (Rachel + kin, "she") — **counter-direction** + +For completeness, the one case where new is *more conservative* than old: + +| Policy | Output | Verdict | +|---|---|---| +| Old heuristic | resolve → Rachel (only female proper noun) | correct | +| New policy | refuse with ambiguous_pronoun_referent ({Rachel, Rachel.mother}) | refuse | + +**Reasoning.** The new policy has more information (kinship entities in +registry) and uses it to flag a genuine surface ambiguity. The old +heuristic resolves correctly only because it cannot see Rachel.mother. The +wrong = 0 discipline preserves correctness either way (refusal is not +wrong); the cost is one extra refused case at Phase 1, which the teaching +loop turns into evidence for a role-bias sub-ADR. + +D1 + D2 + D3 each improve a refusal into a correct resolution; D4 +exchanges a lucky correct for a principled refusal. The net effect on the +GSM8K train_sample is monotonically positive on `correct` and zero on +`wrong`, which is exactly the operating regime ADR-0164 requires. + +## §6 — Constraints + +This ADR inherits ADR-0164 §Constraints in full. Additionally: + +1. **Strict-gap recency.** `RECENCY_GAP_MIN` is ratified at 2 tokens. + Adjustments require a sub-ADR and capability-lane re-run. +2. **Single-salient back-fill is exact.** The UNKNOWN → certain-gender + back-fill (§2.2 step 5) fires **only** when the registry has exactly one + UNKNOWN-gendered entity *globally*. Two UNKNOWN entries kill the rule; + neither is back-filled. +3. **No probabilistic gender inference.** The lexicon is the only source of + certain gender at entity creation. Back-fill is co-reference, not + statistical inference. +4. **No syntactic-role bias at Phase 1.** Subject preference, possessor + demotion, and similar heuristics are out of scope for this ADR. They + require independent measurement and a separate sub-ADR. +5. **Append-only registry.** Re-mentions update positions; entries do not + merge, split, or get deleted within a problem. Gender back-fill + *updates* an entry but does not change its identity. +6. **Resolution log is part of trace.** Every Resolution event is part of + the canonical-bytes serialization that feeds `trace_hash` per CLAUDE.md + §Runtime Surface Contract. + +## §7 — Acceptance criteria (Proposed → Accepted) + +This ADR moves to **Accepted** when, alongside ADR-0164 Phase 1 acceptance: + +1. `EntityRegistry`, `EntityEntry`, and `Resolution` types exist as + frozen-dataclasses under `generate/comprehension/registry.py` with + canonical-bytes serialization tests. +2. The policy in §2 is implemented behind the reader's + `entity_pronoun` category dispatch, with unit tests covering: + - All five worked walk-throughs (§4) byte-equal to the ADR. + - All three disagreement cases (§5 D1–D3) producing the documented + outputs. + - The counter-direction case (§5 D4) producing the documented refusal. + - Single-salient back-fill firing once and only once per problem in + the F1-style cases. + - R1 and R2 refusal reasons emitted with full position and candidate + evidence. +3. Capability lanes G1–G5, S1 remain at 100% `wrong = 0`. +4. `core eval cognition` and the GSM8K train_sample runner show monotonic + non-decrease in `correct` and zero `wrong` against the post-D.2 + baseline. + +## §8 — Open follow-ups (out of scope for this ADR) + +- **Syntactic-role bias** (subject-preference): a sub-ADR after Phase 1 + evidence shows where R1 refusals cluster on subject-vs-object + ambiguity. +- **Cross-sentence anaphora window beyond two sentences**: currently + bounded by `SENTENCE_PENALTY = 1` per boundary; longer chains may need + a saturation curve. Empirical question. +- **Pronoun chains** ("she … her … hers"): currently each pronoun resolves + independently against the registry. Chain coherence (require all three + to bind to the same entity) is a candidate strengthening. +- **Mixed-gender group membership**: §2.2 step 2 allows GROUP ↔ single + gender via membership, but Phase 1 does not exercise this. Defer until + a real GSM8K case demands it. + +## Cross-references + +- **Parent:** [ADR-0164 — Incremental Comprehension Reader](./ADR-0164-incremental-comprehension-reader.md) +- **Companion:** ADR-0164.3 — `ProblemReadingState` and cross-sentence + registry (carries this `EntityRegistry` as one of its fields). +- **Resolves:** ADR-0164 §Open question #3. +- **Substrate that survives:** the binding graph still consumes + `BoundUnknown(entity, unit, …)` tuples whose `entity` field is now + sourced from the registry rather than the regex name whitelist. +- **HITL corridor:** R1/R2 refusals carry the registry snapshot into the + contemplation queue (ADR-0150/0152/0155/0161) for review. +- **Anti-overfitting:** ADR-0114a — the policy is closed-set in its + vocabulary (lexicon-driven), conservative in its dispatch + (refuse-on-ambiguity), and falsifiable on the capability lanes. +- **Thesis:** `[[thesis-decoding-not-generating]]` — the registry is what + "found" looks like for an entity. Each pronoun narrows the candidate + set; the resolution is the accumulation, not a guess.