* docs(adr-0186): sealed candidate-graph injector lane (resume ADR-0170 W2-W5 under ADR-0175 seal)
Topology audit found two disjoint GSM8K readers: the candidate-graph reader
owns the official 3/47/0 metric and already has divide/multiply/compare; its
wall is the recognizer->injection coverage gap (ADR-0170 W2-W5 backlog), not
arithmetic. The derivation reader (resolve_pooled) is a separate sealed organ
that cannot reach the goal without an unbuilt Phase-5 bridge.
ADR-0186 reconciles ADR-0170's injector roadmap with ADR-0175's serving seal:
develop W2-W5 injectors behind a default-off 'sealed' flag on inject_from_match,
measured on a new report_sealed.json, with frozen 3/47/0 byte-identical until a
reviewed Phase-5 promotion. wrong=0 gated on both paths.
Live-loop instrumentation found a schema-vs-extraction split: rate_with_currency
and temporal_aggregation matchers already extract full anchors (blocked on the
CandidateRate union / apply_rate primitive), while discrete_count/currency/
multiplicative are blocked on matcher extraction. And no refused case is one
injector away - every case is multi-statement, so the unit of measurable
progress is a target case's complete injector+composition set. Sequencing:
seal mechanism first, CandidateRate schema next, then a first complete
target-case unlock.
* feat(adr-0186): sealed candidate-graph injector lane mechanism (default-off)
Adds the seal mechanism for developing ADR-0170 W2-W5 injectors without
mutating the frozen serving metric:
- inject_from_match(match, sentence, *, sealed=False): when sealed=True,
consults the new _SEALED_INJECTORS table first; default-off never touches it.
- _SEALED_INJECTORS: empty at land (this PR ships the mechanism; the first
sealed capability is the CandidateRate schema, ADR-0186 §5.3).
- parse_and_solve(text, *, sealed=False): threads the flag to the per-statement
injection site. The seal is injector eligibility, not a forked reader.
wrong=0 guarantees (tests/test_adr_0186_sealed_injector_lane.py, failing-
under-violation): empty seal is a strict no-op; a registered sealed injector
admits ONLY under sealed=True and is invisible to the frozen path; train_sample
report stays 3/47/0. Verified: coverage probe 3/47/0 byte-identical, smoke +
architectural invariants green, 288 candidate-graph/recognizer tests pass (2
pre-existing failures unrelated, confirmed on unmodified main).
* docs(adr-0185): mark superseded by ADR-0186 (premise refuted by topology audit)
The topology audit proved ADR-0185's premise ('the engine cannot divide') is
true only of the derivation reader, which is disjoint from the candidate-graph
reader that owns the official 3/47/0 metric. The goal organ already divides;
its wall is injection coverage. ADR-0185 is retained as a record, not
implemented; cross-referenced from ADR-0186 §header.
9.3 KiB
ADR-0185 — Division reading (rate / partition): the first genuine multi-step capability, eliminate-then-solve
⚠️ SUPERSEDED (premise refuted) — 2026-05-29. This ADR's central premise, "the engine cannot divide," is true only of the derivation reader (
generate/derivation/*,resolve_pooled), which the 2026-05-29 topology audit proved is disjoint from the reader that owns the official3/47/0metric. The goal organ — the candidate-graph reader (generate/math_candidate_graph) — already divides, multiplies, and does comparative arithmetic; its wall is the recognizer→injection coverage gap, not arithmetic. A division reading added to the derivation reader therefore cannot move3/47/0. See ADR-0186 §1 for the topology finding. This ADR is retained as a record only; it is NOT implemented. Any division work belongs in the candidate-graph path under ADR-0186's seal.
Status: Superseded by ADR-0186
(premise refuted; not implemented). Originally: Proposed (spec only — no code). Follow-on to
ADR-0184 (which cut the dimensionally-
impossible products, 13 → 8) and ADR-0182
(pooling + commit-ineligibility).
One line. The engine can multiply and add/subtract but cannot divide — and ~⅔ of the remaining real-GSM8K failures need division (rate, ratio, partition). Add a division reading (cue-licensed, gated by self-verification, pooled), used eliminate-first (a grounded
÷reading disagrees with the flat product → the wrong refuses) then solve (the÷reading commits when it is the unique grounded reading).
1. The metric this serves (and why the headline is the wrong one)
serving = 3/47/0 is the conservative recognizer, frozen until a Phase-5
ratification (ADR-0175).
The metric this ADR moves is the sealed comprehension reader over the real
train_sample (resolve_pooled): currently 2 correct / 8 wrong / 40 refused
(post-ADR-0184). Progress = correct ↑ ∧ wrong ↓ toward ≥10 / 0. Every claim
below is measured there, never on the curated confuser count.
2. What the microscope proved about the remaining 8 wrongs
The 8 (0011 0016 0018 0019 0025 0028 0032 0047) are all the flat product-of-all
committed unopposed. Classifying each by what its gold computation needs:
| need | cases |
|---|---|
| division (rate / ratio / partition) | 0011, 0016, 0018, 0028, 0047 |
| percentage / decimal | 0019, 0028, 0032 |
comparative arithmetic (N more/less than M) |
0016 |
| multi-step grouping | 0025 |
extraction gaps (currency $100,000 → 100, units coconut/different) |
0011, 0028, 0047 |
Division is the single largest need (5 of 8), and it is also the most common missing op across the 47 refused. The engine today has no division reading at all — the only readings it forms are additive accumulation and multiplicative products.
3. Why there is no flat shortcut (the two traps measured and rejected)
Eliminate-first is right, but it must not overfit or trade away a correct answer. Both flat shortcuts were measured and rejected:
| candidate | result | verdict |
|---|---|---|
remove per from multiplicative cues |
8 → 7 (zero loss on the 50) | overfit — on real GSM8K 12 eggs per carton × 5 cartons = 60 is a legitimate rate×quantity multiply; per removal would lose it. Tuning to the sample. |
| downgrade cross-clause flat product → exempt | 8 → 2, but correct 2 → 1 |
loses 0003 (a correct rate-chain) — trades a real solve to kill guesses. |
The distinguisher between the 8 wrong products and the 2 correct ones (0003
boxes×erasers×$, 0021 pounds×reps×sets) is the structure — rate-binding,
comparative, division — which is exactly what the engine cannot read. So there is no
non-overfitting flat rule; the only honest path is to read the structure.
4. The mechanism — a division reading
A composer that proposes a grounded ÷ candidate, licensed by a division cue
(lexeme-level, ADR-0165-safe — names the markers, does not parse grammar):
- rate / ratio:
X per Y,X / Y,how many X per Y→X ÷ Y. - partition:
split / divide / share N into M,N split among M,packs … into M (groups/bags)→N ÷ M.
The candidate runs through the unchanged self-verification gate (grounding ∧ cue ∧ unit ∧ completeness) and the pool. Two roles, in order:
- Eliminate (first). A grounded
÷reading that competes with the flat product forces a disagreement → refuse (the wrong product stops committing). Even an incomplete÷reading (isolated-foreign / partial) enters as a commit-ineligibleexemptcandidate and still forces the refusal (the ADR-0182/0184 pattern). - Solve (then). When the
÷reading is the unique grounded reading, it commits — coverage.
4.1 The per cue-semantics correction (done right)
per is currently in MULTIPLICATIVE_CUES, which is wrong: X per Y denotes a
rate X/Y (division), not X×Y. But §3 showed removing it overfits. The correct
fix is reclassification, not removal: per licenses the new division
composer (the rate numerator/denominator), and the multiply that scales by a rate
(rate × quantity) is licensed by the quantity structure, not by per. This makes
per-products refuse-or-divide instead of blindly multiplying, without losing
legitimate rate×quantity multiplies (which the division reading + a scaling step
recover). Validated on train_sample + the confuser probe, not the 50-sample alone.
5. Honest scope — division alone will not flip most of the 8
This is the load-bearing caveat. Division is necessary but not sufficient for the 8 because they are entangled with other missing capabilities:
0016needs comparative arithmetic (2 more than 5 → 7) andper-as-rate before14 ÷ 7 = 2.0011/0028need currency extraction ($100,000,$2) before any op.0019/0032need percentage reading.
So division's immediate train_sample effect is mostly elimination (flat
products refuse where a ÷ reading disagrees), with solves arriving only as the
companion capabilities land. The obligation (§7) is therefore framed as wrong ↓
(never ↑) with correct held or rising — not an inflated solve claim. Claiming
division alone flips these would be the overfitting trap (feedback-synthetic-corpus- overfitting-trap).
6. The roadmap this opens (eliminate-then-solve, each measured)
Division is capability 1 of the multi-step reading the goal requires. Sequenced by
leverage × foundational-ness, each gated by wrong=0 and measured on train_sample:
- Division reading (this ADR) — rate/ratio/partition +
perreclassification. - Comparative arithmetic —
N more/less than M → M±N(a reading-correctness fix; unblocks0016and is general across GSM8K). - Currency / decimal extraction richness —
$100,000,$2,1%(the extraction gaps that garble0011/0028). - Percentage reading —
N% of X,N% less/more(0019/0032). - Multi-step chain shapes —
anchor − sum,sum-then-scale, division-then-± (the mixed-op chains gold actually uses). - Ratification bridge (ADR-0175
Phase 5) — the hard dependency: until a reviewed promotion path exists, none of
this moves the serving
3/47/0headline, however good the sealed reader gets.
7. wrong=0 obligations (before any merge of the implementation)
train_sample: sealed-readerwrongmust drop, never rise;correctheld or rising. Failing-under-violation: the cases a÷reading refuses (disagreement) are pinned; any case it commits is gold-checked.- Confuser probe:
wrongdoes not regress; positives still solve. A division composer must not let any curated case commit a wrong÷. - Serving frozen: the composer is sealed (
chat/does not import it);3/47/0byte-identical, lane-SHA 8/8. - No overfitting: every cue is a general division marker validated beyond the
50-sample; the
perreclassification is justified by cue semantics, not by a sample delta. No per-case rule. - Commit-ineligibility: an incomplete
÷reading may force a refusal but never commit alone (full completeness still required to commit — the ADR-0175 guarantee).
8. Why this obeys the standing principles
- Decode, don't guess (thesis): a division reading comprehends rate/partition structure; it does not store a flat product. It teaches the engine to find better.
- Eliminate-first: the
÷reading's first job is to make wrong flat products refuse (disagreement), before it earns any commit. - wrong=0 > coverage (ADR-0175): obligations gate on
wrong ↓, not solve count. - ADR-0165: cues are lexeme markers, not grammar templates.
- No contradiction with in-use ADRs: extends the pool/gate, changes no serving path, reuses the commit-ineligibility mechanism rather than inventing a new gate.