Execution-free brief for the read-only GitHub-connector lane: comprehension-
primitive inventory, question-layer gap survey, subject-readiness recommendation,
then gated capability-axis/ADR/corpus drafts. Proposal-only; serving stays frozen
(wrong=0, pinned lanes). Companion to the Claude execute/validate/commit lane.
Narrow product promotion boundary (`generate/derivation/product_bridge.py`)
wired into `generate/math_candidate_graph.py`: only complete pure-product
derivations with a product-target question and no known hazard surface lift
from the sealed pooled derivation reader into serving.
- Serving train_sample: 4/46/0 → 6/44/0, wrong=0; case 0050 still refused.
- Renumbered from the collided ADR-0194 (labeled-container, #499) to ADR-0195
and rebased onto current main.
CI: smoke + verify-pinned-lane-SHAs green on the merge commit.
GSM8K labels containers/regions with a trailing single-letter or short-numeric
label ('Jar A has 28 marbles', 'Section G has 10 cars', 'District 2 has 19
voters'); the initial-possession entity slot captured only 'Jar' and the label
broke the match. Adds a separate sibling pattern _INITIAL_HAS_LABELED_RE
(mirroring ADR-0136.S.4 localisation) that REQUIRES the label, so the global
_ENTITY is unchanged and bare subjects yield no duplicate candidate.
- Composes with ADR-0193 aggregate question: 'Jar A has 28 marbles. Jar B has
12 marbles. How many marbles are there in total?' -> 40.0.
- 0 real-corpus metric flip (honest substrate): the one real multi-container
aggregate additionally needs comparative + multiplicative + lowercase-ref.
- wrong=0 HOLDS full corpus (7,473 q); train_sample byte-identical 4/46/0;
synthetic-registry capability-axis gate + G5 lane green; smoke 67 passed.
- Label bounded by the possession verb: multi-word nouns ('Jar Apple') do NOT
match. wrong=0 held downstream by completeness + round-trip + disagreement.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Extends ADR-0131.G.5's total-across aggregate branch with the existential
verb frame 'How many <unit> are there <cue>?' over the SAME closed cue
vocabulary (no cue-set widening). The solver already sums entity=None
total-across; the wall was purely the question parser's verb-frame coverage.
- Composes end-to-end: 'Jamie has 28 marbles. Kyle has 12 marbles. How many
marbles are there in total?' -> 40.0 (load-bearing, not inert).
- 23 real GSM8K problems now parse the question (advance past the Q-wall to
the statement-parse wall).
- wrong=0 HOLDS on the full 7,473-q corpus; train_sample byte-identical
4/46/0; no metric delta (composition-wall lesson, third instance).
- Cross-ADR discipline: 'What is the total number of <unit>?' is DEFERRED,
not contradicted — ADR-0131.G.5 pins it as a refusal probe; promoting it
must amend that ADR's closed-cue contract. test_total_number_of_still_deferred
locks the boundary.
Firewall: completeness guard (ADR-0191) + question round-trip refuse conjoined
('dogs and cats') and derived ('animal legs') units. G5 lane + synthetic-registry
wrong=0 capability-axis gate green; smoke 67 passed.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The discrete_count matcher gated the counted noun on a CLOSED ratified set
(observed_counted_nouns): 'Betty has 24 marbles' matched, 'Randy has 60 mango
trees' / 'Sam has 12 red apples' did not — purely because the noun was unseen.
Open the single-anchor possession/acquisition path to an open noun phrase
(adjective* + 1-3 word head, bounded by a stop-word lookahead so it never
swallows a trailing PP), keeping every other narrowness layer (proper-noun
subject, verb whitelist, single numeric token, no clause-split). Closed
observed nouns still match (capitalized compounds preserved); compound
enumeration stays closed.
Safe because ADR-0191 moved the wrong=0 guarantee downstream: an open-vocab
mis-parse hits the completeness guard + round-trip + branch-disagreement.
Proof: full real corpus 61->494 discrete_count anchors (8x), wrong=0 HOLDS,
zero confabulations.
Substrate PR — 0 metric delta by design (train_sample byte-identical 4/46/0;
the problems still need composition downstream). Value: the foundation every
discrete_count flip consumes, and empirical proof open-vocab is firewall-safe.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(adr-0191): candidate-graph completeness guard — real-corpus wrong 5->0
The candidate-graph reader (serving) checked grounding + round-trip but had
no completeness obligation, so problems whose later clauses failed to parse
emitted a partial reading. Over the full 7,473-question real GSM8K train
split this confabulated 5 answers (wrong!=0) the 47-case train_sample hid;
2 were regressions from #488.
Add the missing admissibility leg (mirrors the derivation reader's verify.py):
every source quantity (all statements + question) must be consumed by the
chosen reading, else refuse. Refusal-only -> cannot create a wrong answer.
Number-sense is pack-authoritative (en_numerics_v1 parse_compound_cardinal +
lookup_multiplier + all 6 currency symbols) so it never disagrees with the
engine; aggregating initials expose consumed_value_tokens provenance.
Evidence: real-corpus wrong 5->0, correct held at 4; train_sample byte-
identical 4/46/0; G1-G5+S1+G3.1 green; smoke 67 passed; math_teaching_corpus
lane byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(adr-0191): committed full-corpus GSM8K microscope (standing wrong=0 + coverage instrument)
Promotes the throwaway tmp/ microscope that found the 5 confabulations into a
committed tool. Runs the canonical serving reader over any GSM8K corpus and
reports failures-first: correct/wrong/refused, every wrong answer by name,
refusal families, and the no-injection per-category coverage map that ranks
which injector to build next by real frequency.
Default corpus is the committed 47-case train_sample (always available);
--corpus path runs the full real split. This is the ADR-0191 follow-up: re-run
after every capability PR, not just train_sample — a flip is only real if it
does not widen the confabulation surface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The attempt/score/ledger half existed (run_practice -> ClassTally scored vs
gold); nothing consulted the gate to turn earned reliability into a ratifiable
proposal. Adds core/reliability_gate/propose.py (propose_from_ledger +
RatifiableProposal): for each class, license_for(PROPOSE) emits a proposal iff
its conservative Wilson floor (0 below N_MIN=10) clears theta=0.85. Refusals
never penalize; deterministic; PROPOSAL-ONLY (never a serving mutation).
propose_runner.py closes the loop end-to-end with an aggressive sealed scorer
(resolve_pooled): practice 95c/5w/50r -> ONE proposal (additive, reliability
0.8608>=0.85, 95/100); 5 wrongs tolerated but floor held; rest stayed sealed.
The gold-tethered autonomous contemplation: the engine earns the right to ASK,
not to SERVE. 11 failing-under-violation tests.
* feat(adr-0189): comparative reading — anchor-verb widening + multi-word units
The candidate-graph comparative extractor (ADR-0131.G.2) read only has/have +
single-word units, so real-GSM8K comparatives ('Brooke does three times as many
jumping jacks as Sidney') didn't parse — a dark statement in 17 places blocking
15 of the 47 refused train_sample cases, despite the ADR-0123 solver already
supporting compare_additive/compare_multiplicative.
Widens the anchor-verb set (reusing legacy vetted lemmas + does/collected/
gained/studied…), EXCLUDING polarity-inverting verbs (lose/spend/give/sell/win)
to preserve wrong=0; admits 1-2 word units via the existing multi-word
_unit_grounds branch. Feeds the existing solver unchanged.
wrong=0 proven: G2_comparatives 29/29, G3 20/0, G4 32/32, train_sample 3/47/0
byte-identical; polarity-inverting verbs proven refused (failing-under-violation).
Chain composes correctly in isolation (146 -> 438). Flips 0 cases ALONE — every
comparative case needs a composing partner (aggregation / multi-word-noun
injection); this ships the component, not yet a flip.
- generate/math_candidate_parser.py: _comparison_anchor_verb widening + 1-2 word
unit slots in the two multiplicative comparative regexes.
- tests/test_adr_0131_G2a_*: 5 tests incl. polarity-inversion wrong=0 guards.
- docs/decisions/ADR-0189: gap, change, wrong=0 evidence, honest scope.
* feat(adr-0189a): first metric move 3/47/0 -> 4/46/0 (case 0024, comprehension-composed)
Case 0024 now SOLVES (answer 438) by composing three general comprehension
capabilities feeding the unchanged ADR-0123 solver:
1. day-of-week count enumeration: Sidney = 20+36+40+50 = 146
(_day_enumeration_candidates; derived sum grounds via first count token,
mirroring _embedded_quantifier; closed to the 7 day names)
2. comparative reading (ADR-0189): Brooke = 3 x Sidney
3. activity question 'How many <unit> did <Entity> <verb>?' (_Q_DID_RE)
Plus do/does/did added to the CandidateInitial anchor whitelist (production-
possession), admitted only via the closed day-enumeration shape.
wrong=0 PROVEN across every lane: all 8 capability axes wrong=0 (G2_comparatives
29/29, G3 20/0, G4 32/32, G5/S1/S3/S4 all pass), train_sample 4/46/0 wrong=0,
verify_lane_shas exit 0 (no pinned lane changed), generate_claims --check OK.
872 tests pass; new tests are failing-under-violation incl. wrong=0 guards
(non-day comma list not summed; polarity-inverting comparative verbs refused).
Re-baselined report.json + train_sample_coverage_report.json (latter also clears
pre-existing reason drift) + CLAIMS.md to the new 4/46/0 metric. Decode-not-guess:
0024 solved by READING its structure, not storing an answer. Remaining pre-existing
failures (G3 committed-report, telemetry) unrelated, fail on pristine main.
- generate/math_candidate_parser.py: day-enum extractor + _Q_DID_RE + does-anchor.
- tests/test_adr_0189a_day_enum_activity.py: 5 tests (incl. end-to-end 0024=438).
- docs/decisions/ADR-0189a + report.json/coverage/CLAIMS re-baseline.
* 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.
* docs(adr-0184): scope the distinct-unit product rule — cut the product-of-all over-commit
The 47-refusal coverage diagnostic surfaced that the headline 3/47/0 (serving
recognizer) hides the sealed comprehension reader's real state: resolve_pooled over
the 50 real train_sample cases is 2 correct / 13 WRONG / 35 refused. The confuser
probe's wrong=0 was a misleading proxy — all 13 real wrongs are the whole-text
product-of-all, the unique complete candidate, committed unopposed.
Scopes the first lever, decided by MEASURING candidate refusal rules against the real
metric (correct up, wrong down on train_sample):
baseline 2 / 13 / 35
distinct-unit product (chosen) 2 / 8 / 40 <- cuts 5, zero coverage loss
product spans >1 clause 1 / 4 / 45 <- destroys correct 0003
drop all products 0 / 2 / 48
The distinct-unit rule: multiply/divide may compose DISTINCT units but a multiply
step whose operand repeats a non-empty unit already in the product (apples x apples,
cards x cards) is unit-incoherence -> refuse (unit^2 is never the answer). Empty-unit
operands exempt (0003 multiplies a blank-unit 0.75). Dimensional, not lexical
(ADR-0165-safe); refines verify.py clause 3 shared by self_verifies + classify.
Honest scope: 13->8, NOT 0. The remaining 8 are distinct-unit products in the wrong
shape (rate problems) = cue precision (ADR-0177 CP-2b), the next lever, NOT to be
faked with a per-case rule. Establishes the real scoreboard (resolve_pooled over
train_sample) and notes the ratification bridge (ADR-0175 Phase 5) as the separate
dependency for any of this to reach the serving headline.
Spec only; serving 3/47/0 untouched (verify is not on the serving path).
* feat(adr-0184): distinct-unit product rule — sealed reader real-GSM8K wrong 13->8
Cuts the over-eager product-of-all on real GSM8K. The sealed comprehension reader
(resolve_pooled over train_sample) was 2 correct / 13 WRONG / 35 refused; all 13 are
the whole-text product-of-all committed unopposed (0042->2.4M, 0048->19200,
0001->14400). This is the first lever measured against the REAL metric (resolve_pooled
over train_sample), not the curated confuser count.
Mechanism (verify._is_repeated_unit_product + classify_derivation downgrade):
a pure multiplicative chain whose operands repeat a non-empty unit forms unit^2
(apples x apples, cards x cards) -- never the answer; it is the product-of-all
multiplying independent groups. Such a product is classified `exempt`
(commit-INELIGIBLE), NOT removed. Empty units exempt (0003 multiplies blank-unit
0.75); divide exempt (feet/feet = legitimate count). Dimensional, not lexical
(ADR-0165-safe).
Implementation finding (folded into ADR §3.1): the naive version put the predicate
in the shared _base_reasons gate, which DROPPED the product and regressed the
confuser probe 1->3 -- the disguised-polarity 0001/0003 refuse only because the
coins x coins product DISAGREES with the coins + coins accumulation reading; dropping
it unmasked the additive reading as a unique wrong commit (80/30). The fix is the
downgrade: keep it as a commit-ineligible `exempt` candidate so it still forces the
disagreement. Pinned by test_downgrade_not_removal_preserves_disagreement_refusal.
Evidence (sealed lane; chat/ does not import verify -> serving frozen):
- resolve_pooled over train_sample: 2 correct / 8 wrong / 40 refused (was 2/13/35);
the 5 repeated-unit products (0001/0017/0042/0045/0048) now refuse, 0003/0021 kept.
- confuser probe: wrong unchanged (no 0001/0003 regression), positives still solve.
- serving train_sample 3/47/0 and practice (accumulation + search) 3/47/0
byte-identical; self_verifies/_base_reasons unchanged so search lanes are untouched.
- 171 derivation/pool/verify tests + 40 architectural invariants green.
Honest scope: 13->8, NOT 0. The remaining 8 (0011/0016/0018/0019/0025/0028/0032/0047)
are distinct-unit products in the wrong shape (rate problems) = cue precision
(ADR-0177 CP-2b), the next lever -- not to be faked with a per-case rule. Carries the
corrected ADR-0184 (supersedes the spec-only #484).
The lookback review of the four-PR stack (EX-6/pooling/prior-state/anchor-skip)
found the ADR-0182 spec under-predicted and under-scoped 0016. Records actuals
without editing the original spec (retained for provenance):
- Status -> Accepted/Implemented (PRs #476, #480, #481).
- §8 Realized results: confuser wrong spec-predicted 5->4->3, ACTUAL 5->2->1->0.
Pooling also caught disguised-polarity (bonus); 0016 needed anchor-skip PLUS
intra-clause accumulation (a conjunction split the spec framed as anchor-skip
alone) and yielded the bonus 0017 *solve*; the prior-state guard (#480) is a
distinct question-time lever sharing resolve_pooled.
- Folds in the lookback findings: solid items, the closed test-coverage gap
(anchor-skip refuse branches, #481 4205605), no live wrong=0 hazard (multi-actor
product-commit predates anchor-skip), and the open 0010 multi-referent spurious
(a graduation question, not a wrong).
Serving 3/47/0 byte-identical throughout. Doc only.
Regenerated the docs/decisions/README.md ## Index table from each ADR's own
title + Status line, naturally sorted by ADR id (handles 0114a, 0119.1,
0131.G.3.1, 0136.S2, etc.). The index had drifted to 51 of 229 ADRs; it now
lists every ADR through 0183. Only the Index table was replaced; the Current
frontier / chain notes / reasoning-capable-domains / session-log narrative is
untouched (it remains a curated, historically-scoped account, not the index).
Records the fork for getting words from audio WITHOUT a serving-time learned
model: (A) words-as-text, or (B) deterministic formant/phonetic decode + taught
vocabulary. Status: Proposed (stub) — deferred, not a committed design. Captures
the problem, the 0-param/decode-not-borrow/refuse-over-fabricate/reviewed-growth
constraints, and the open questions a full ADR must answer. Exists so the
serving path doesn't silently reach for Whisper. Cross-linked from
docs/audio_pipeline_overview.md §9.
Adds §9 subsection: a teacher is bootstrap scaffolding for the teaching phase,
not a production component (not ML distillation — CORE has no weights). Serving
path must never call a teacher. Production is Whisper-free conditioned on a
lawful runtime path: (A) words-as-text, or (B) matured deterministic
audio→lexeme decode. Flags the trap: teaching with a model does not auto-transfer
word-recognition into a 0-param engine. 'The teacher teaches; the lawful path
serves.'
Living ledger of architecture/model/dependency footprint. Headline: CORE is a
0-parameter, 0-weight architecture (substrate is algebra, not weights), enforced
by the teacher versor-invariance test. Tracks: learned params (0), optional
teacher lanes (4 declared, all inert, no model size chosen), 10 Python runtime
deps (datasets dominates), 8 Rust crates, 0 weight files. Includes a Whisper
size reference for when/if a lane is wired, and an update-on-PR + changelog
discipline. Lives at docs/model_dependency_size_tally.md.
A from-the-ground-up reference for the audio modality: waveform → canonicalize
→ frames → acoustic lexer → typed AudioIR → operators/rotors → (32,) versor,
with every spec/constant grounded in the code (PR-2..PR-6 stack).
Includes the three-way clarification of learned models: embeddings (never),
audio specs (CORE's own DSP, not any model), text transcript (Whisper/NeMo as
typed labels only). Documents the substrate-vs-evidence split, the verbatim
teacher policy, current inert/gated status, and the 'scrutinise the consumer'
flag. Cross-links ADR-0180/0181 + spec + eval-plan. Lives at
docs/audio_pipeline_overview.md.
A confuser's `expected: refuse` does not mean unanswerable. Every confuser carries
its true gold (`answer_numeric`) and is a solvable coverage target; `refuse` means
"the reader can't yet comprehend this category, so refusing is the honest outcome —
committing a WRONG value is the defect." Example: 0001 "buys a toy for 30 coins …
how many left?" is plain 50-30=20; it is refuse only because today's reader takes
`buys` as a gain.
Adds §2.1 (graduation protocol): when a general mechanism reads a category correctly
(validated on train_sample + the category, wrong=0 preserved), those cases graduate
refuse -> solve and committing their gold becomes a win. Reframes the `spurious`
verdict as "solved-before-graduation" (a graduation signal, with pair-consistency as
the genuine-reading-vs-surface-match discriminator), not automatically a defect.
Notes that no v1 category is degenerate, and how a truly-unanswerable case would be
labelled (answer_numeric: null + degenerate: true) so the two senses of "refuse"
stay distinguishable.
Spec only; no corpus/runner change (today's commits are wrong-reading, so spurious
stays flagged until a category genuinely graduates).
The confuser probe's two distractor-quantity wrongs (0014 ->300, 0016 ->3840)
have no clean tight fix: the `for` cue licenses both the 0014 distractor product
and the correct train-0021 product, and a target+foreign-unit refusal rule breaks
the canonical train-0003 boxes x erasers product. Telling a real multiplier from a
distractor by cue is the cue-precision problem ADR-0177 measured as not-yet-solvable
-- a tight rule here would be a reactive surface patch (the overfitting trap).
This ADR scopes the general, non-reactive alternative: let the wrong=0 DISAGREEMENT
rule do the refusing. Pool self-verifying candidates across composers so a blunt
product reading and a competing additive reading of the same problem disagree ->
refuse; legitimate products (0021/0003) have no competing reading, so they still
commit -- the structural property that distinguishes them, expressed without a cue.
Scoping surfaced two things a one-line plan would have missed:
- Completeness FORCES the distractor into every reading, so naive pooling changes
nothing. The mechanism needs a narrow, commit-INELIGIBLE completeness exemption
for isolated-foreign quantities -- it can only buy a refusal, never an answer, so
the commit-path completeness guarantee (ADR-0175's 9->2 multi-step fix) is intact.
- MS-1 Target exposes the union of body unit-shapes, NOT the question's asked-for
unit (verified). The exemption is therefore keyed to a reading's used-operand
units, not an asked unit that does not exist.
- 0016's distractor sits in the anchor-position clause, so it needs distractor-aware
anchor selection too: 0014 is the guaranteed win (wrong 5->4); 0016 lands (->3)
only when anchor-skip is built. Doc declines to claim both before that is proven.
Spec only, no code. Sealed lane; serving 3/47/0 untouched.
§1.5.3: content-addressed re-sort obligation is vacuous at compute_trace_hash
(folds vault_hits count, not contents); amend to apply at recall() result set
+ any future contents-bearing hash.
§2.2: Merge Kernel must content-address equal-score recall ties (multivector +
provenance bytes), not arrival/deque-index order — the general-path analog of
ADR-0181 §2.2's audio merge key.
Both sharpen the substrate ahead of the vault.rs LocalArena/SemilatticeDelta
refactor; neither blocks it. Sourced: docs/audit/ADR-0180-t1-t4-findings.md.
Flip ADR-0173 Proposed -> Accepted. This is the W0 gate for the
workbench-UI ratification wave: it amends ADR-0160's read-only stance
narrowly to admit operator-driven invocation of the three existing,
replay-gated Tier 1.5 handlers (apply_lexical_claim, apply_frame_claim,
apply_composition_claim) as a local keyboard accelerator over ADR-0161
Surface C. No UI code; implementation remains gated to the W1..W4
acceptance gates in the ADR.
Also lands the reconciled W3 ratification-corridor brief. It supersedes
the stale W3 section of WORKBENCH-UI-WAVE-BRIEF-PACK.md, which assumed a
'frontend zero' state (W1/W2 already merged via #295/#299/#329/#327/
#328/#303) and prescribed a Zustand store this repo never adopted. The
reconciled brief names the load-bearing facts the old one glossed:
- /ratify is advisory-only today (routes, never applies); W3's core
backend task is the advisory->in-process flip.
- live proposals carry shape_category='uncategorized', so the safe
category/polarity must be operator-supplied and allowlist-gated, not
hard-coded -- the exact silent-wrong-category path case 0050 guards.
Verification this session: read-only workbench v1 confirmed working
(API serves real deterministic data, honest 404s, traversal rejected,
ratify advisory/no-mutation; 31 python + 104 UI tests green; pnpm build
green).
No runtime/algebra/eval paths touched. Docs only.
Follow-on to ADR-0163 §F that corrects the metric exposed by the overfitting
finding (96/150 synthetic flips vs 1/50 real; the 0002 cable/fraction problem read
as accumulation -> 996). Specs a small, hand-curated, real-sourced set of hard
negatives + near-miss confusers (disguised-polarity verbs, pseudo-accumulation
fractions, multi-referent/multi-actor, distractor quantities, temporal/question-
scope, comparative-referent, unit confusers) with minimal-pair construction.
Scored the opposite way from a coverage lane: success = wrong=0 on confusers +
pair-consistency + honest refusal frontier, NEVER solved-count. Held-out by
contract (not a training-to-fit target); the CP ledger consumes the wrong attempts
as the hard negatives the templated corpus lacked. Guardrails forbid reactive vocab
growth and template expansion.
Grounded scope for GB-3b, the next Gap B move. Measured opportunity: 46/150
additive practice cases are sum-of-all==gold, all currently refusing, all the
single-referent accumulation shape ('X has N. He buys M more.' -> N+M). They
refuse correctly (GB-3a's multi-clause guard / the Alice+Bob hazard); the safe
ones differ by referent identity + a change-verb cue, which GB-3b reads.
Scope covers: the reading (anchor + change steps), the wrong=0-critical referent
model (minimal no-new-actor guard; cross-references ADR-0164.2/.3 + ADR-0174's
multi-actor hazard; deliberately does NOT resurrect the retired gender-blind
resolver), the proof-carrying wrong=0 obligations, the cue-precision coupling
(change cues should finally be reliable, closing CP-2a), the increments, and
honest flip expectations (big chunk in practice-additive; modest train_sample;
serving stays 3/47/0 until ratification).
ADR-0180 §1.5.4 + CLAUDE.md work-sequencing item 5 require these four
properties green on main before any core-rs/src/vault.rs change. They are
also the foundation ADR-0181 PR-5 (audio Delta-CRDT wiring) rides on.
T-1 set-equality of vault writes under shuffled ingest (+ idempotent
re-ingest at the content-addressed layer)
T-2 trace-hash invariance to vault order, + recall result-set invariance
to insertion order (the genuinely-failable half)
T-3 versor_apply non-commutativity (negative guard)
T-4 ProjectionHead.project purity across calls and threads
Findings (docs/audit/ADR-0180-t1-t4-findings.md):
- compute_trace_hash folds only vault_hits (a count), NOT vault contents, so
ADR-0180 §1.5.3's "re-sort vault state in content-addressed order" is
currently vacuous at the trace-hash layer; the live order-invariance
obligation is at recall() (result-set + count). Recommend amending §1.5.3.
- equal-score recall ties are index-sensitive; the Merge Kernel needs a
content-addressed tiebreak (mirrors ADR-0181 §2.2 merge key). Recommend
amending §2.2.
- append is genuinely semilattice-eligible; versor_apply is non-commutative.
7 passed; smoke suite green. No runtime/core mutation — tests + audit only.
CP-2a populates the CP-1 ledger from gold-labelled candidate readings and reports
per-pattern reliability — the measurement the cue-precision thesis rests on. Plus
the function-word unit filter, whose value this measurement makes concrete (clean
unit_shape labelling).
What landed (all sealed; serving 3/47/0 byte-identical):
- generate/cue_precision/trainer.py — train_from_cases(cases, enumerators): folds
gold-labelled candidate chains into the ledger via record_case. Decoupled (the
candidate enumerators are injected, so the package still imports nothing from
search). candidates_for dedupes a reading shared by two enumerators.
- generate/derivation/multistep.py — extracted the enumeration half of search_chain
into public candidate_chains(problem_text); search_chain now delegates (verified
byte-identical: ms3 tests + practice counts unchanged). CP-2 needs the readings
the search weighs, not just the one it resolves.
- generate/derivation/extract.py — function-word unit filter (_NON_UNIT_WORDS):
blanks spurious function-word units ($0.75 each -> "", 3/4 of -> "") that
corrupt same-unit detection and unit_shape. Closed lexeme set, ADR-0165-safe.
- evals/gsm8k_math/practice/v1/cue_precision_report.py — trains over 200 sealed
cases (50 train_sample + 150 ADR-0163-F additive) with the real enumerators and
prints the per-pattern reliability table.
- tests/test_adr_0177_cp2a_training.py — trainer obligations (credit/dedupe/
determinism/empty) via synthetic enumerators; real-measurement well-formedness;
search_chain parity.
Load-bearing finding (recorded in ADR-0177): over 200 cases EVERY (cue,op,unit_shape)
pattern floors at ~0.0 reliability (best: for-multiply-cross_unit 0.0116 at 2/34).
The blunt product/sum-of-all readings are almost always wrong vs gold, so the
conservative floor correctly trusts nothing. => CP-2b (trust reliable cues) is
blocked on candidate GENERATION, not the ledger: candidate readings must get less
crude (clause/referent structure, ADR-0178 GB-3b) before any cue earns reliability.
Cue-precision and compositional structure are coupled; structure comes first.
Verification: 107 targeted tests green (CP-2a/CP-1/extract/ms3/GB-1/2/3/MS-1/2) +
architectural invariants; serving CLAIMS.md sha unchanged; practice 4/1/45 and
0/1/149 unchanged. Inert: trains/reports only, consulted by no search/gate.
Maps the AssetOverflow audio-compiler proposal onto the existing
sensorium ProjectionHead boundary (ADR-0013) and the Delta-CRDT
sharded substrate (ADR-0180).
Core mapping decision: the audio chunk boundary IS the CRDT delta
boundary. In-chunk rotor composition (compile_events) is the explicit
serialization barrier ADR-0180 §1.5.2 requires for non-commutative
versor_apply; the resulting AudioCompilationUnit is the order-invariant
delta written at the only semilattice-eligible layer (vault/store).
The compiler's checksum chain supplies the content-addressed merge key
(canonical, ir, projection sha256) that ADR-0180 §1.5.3 demands, making
idempotence structural and the sequential==concurrent trace-hash proof
checkable.
Adds:
- docs/decisions/ADR-0181-audio-compiler-delta-crdt.md (decision + mapping)
- docs/plans/audio-compiler-spec.md (typed AudioIR, operator table,
manifest, numeric determinism, delta interface)
- docs/plans/audio-compiler-eval-plan.md (corpus, gates, A-1..A-6 CRDT
proof obligations, teacher-migration policy)
Docs only; no runtime/core mutation. PR-2..PR-6 substrate work sequenced
in the ADR, with PR-5 gated on ADR-0180 §1.5.4 (T-1..T-4) green on main.
* docs(handoff): parallel-work plan post-GB-3a (CP-1 / scale / EX-3 tracks)
Three disjoint-file tracks dispatchable in parallel with Claude's serial Gap-B
line; records the hard constraint that GB-3b/4/5 are serial on compose.py.
* docs(adr-0180): propose CRDT-sharded vault concurrency substrate
Drafts ADR-0180 (Proposed) for a Delta-CRDT sharded substrate to support
forthcoming multimodal ingestion (vision, kinematics) without serializing
on a global Vault lock.
§1.5 grounds the proof obligation against the existing single-threaded
Python ingest path (sensorium → ingest/gate → field → vault/store →
compute_trace_hash) and enumerates four pre-refactor test obligations
(T-1..T-4) that must be green on main before any change to
core-rs/src/vault.rs lands, per CLAUDE.md work-sequencing item 5.
§1.5.5 explicitly fences out: approximate recall (exact CGA recall is
non-negotiable), hidden background execution (Merge Kernel must be
explicitly mounted with telemetry), and MLX/UMA hardware optimization
(deferred to a follow-up ADR; CPU-only Rust path lands first).
Proposed only — no code changes to core-rs, sensorium, or field.
The mandated lookback review before GB-3 (CLAUDE.md §Lookback Review Discipline)
confirmed the audit's hazards H1/H2/H3 were LIVE: compose_sequential summed
same-unit quantities from the whole problem, merging unrelated referents/scopes
and admitting wrong structures whose value happened to ground:
H1 (second actor's apples) -> 6+4+2 = 12
H2 (comparative on other actor) -> (6+4)*2 = 20
H3 (later depletion event) -> 6+4+3 = 13
Root cause is the audit's G1/D2 drift: GB-2a re-extracts from the whole text and
ignores GB-1's clause structure. The fix is the GB-3 increment — make the composer
clause-scoped (consume segment_clauses), refusing when the licensed structure spans
clauses, because this slice cannot model referents:
- quantities must live in exactly ONE clause (0 or >1 -> refuse);
- a comparative outside that clause -> refuse (unmodelled referent binding).
All three hazards now refuse; all 7 GB-2 single-clause structures preserved
(list-sum, three-item, sum-then-scale, and the mixed-unit/disagreement/too-few
refusals). tests/test_adr_0178_gb3_referent_guard.py would fail against the
pre-guard code (12/20/13), so the obligation is proven, not decorative.
Scope/safety:
- compose_sequential is sealed substrate, not wired to a scorer -> serving
byte-identical 3/47/0 (lane-SHA 8/8, generate_claims --check OK); practice
unchanged 4/1/45. No new test failures (2 pre-existing on main).
- ADR-0178 amended: GB-2 relabelled GB-2a (list slice, drift G1 recorded);
GB-3 split into GB-3a (this referent guard, landed) and GB-3b (constructive
cross-clause chaining, next).
#449 shipped an inert, duplicated, unvalidated pack off-brief; closed it but kept
the on-thesis idea (ground the nouns GSM8K problems use) with the real scope it
would need to be beneficial.
Reconciles ChatGPT's four independently-branched extraction PRs (#451/#452/
#453/#454) into one coherent generate/derivation/extract.py. They each rewrote
the same file + same new test off main, so they conflicted pairwise and needed
integration, not a merge.
Integrated (span-tracked, most-specific-pass-first so numbers are never double
counted):
- EX-1 word-numbers (#452): reuses WORD_NUMBERS; tens-one hyphen compounds;
factor-bearing half/third/quarter excluded.
- EX-4 list-unit inheritance (#451): bare numeric list with one trailing unit.
- EX-5 sentence-final numbers (#454): bare final number with empty unit.
Deferred: EX-3 multi-word units (#453). Its greedy lowercase span reads
"6 apples and 4 apples" as unit "apples and", regressing GB-2's
test_same_unit_list_sums, and still can't recover real multi-word units from
0024-class text ("jumping jacks on"). Needs a tighter rule; see
docs/handoff/AUDIT-ADR-0179-EX-RECONCILE.md.
Verification (sealed lane only; chat/ does not import this module):
- Serving frozen: lane-SHA 8/8 match, generate_claims --check OK -> 3/47/0
byte-identical, wrong=0 held.
- Sealed practice improved 4/2/44 -> 4/1/45: case 0025 flips wrong->refused.
EX-1 reads "three", so completeness sees a quantity the 6x50 chain omits and
refuses the spurious 300 (gold 1200) instead of committing it.
- No new test failures (3 pre-existing on main).
Also fixes stale test drift from EX-2 (#447): TestDecimalGroundingGapIsDeferred
asserted decimals still refuse, but #447 made $0.75-class resolve to 864.
Renamed to TestDecimalGroundingResolves and updated to assert the flip.
Honest scope note: EX-4 does NOT unblock real case 0024 (its PR test used a
fabricated bare-list paraphrase). TestRealCase0024StillBlocked pins the true
boundary.
The actual coverage lever (ADR-0177 established cue-precision is its gate+prune,
not the unlock). Gap B = which quantities group via which ops in what order.
The reframe that resolves the rich-search-vs-uniqueness tension: structure-from-
READING, not enumeration. The text encodes its own structure — every gold case
fits a sequential clause-by-clause read (0021 one clause 15x10x3; 0003 48 ->x24
->x0.75; 0024 sum ->x3; 0033 seq chain + branch). So Gap B is comprehension: read
the problem, build the derivation structure as you read, hold alternatives on
ambiguity, reevaluate on lookback. This IS the project's original 'word-to-word
with lookback + problem-solving throughout' articulation, and the synthesis of
reader (0174) + solver/gate (0176) + learning (0177) + packs.
Decision: a comprehension-guided sequential composer — clause-local bounded
sub-derivations combined across clauses via relational cues (per/each/of->multiply,
and-list->sum, comparative->scale, more/older->add, fewer/less->subtract), held-
hypothesis on ambiguity + eliminate/reevaluate (REPOINT ADR-0174's inert reader
substrate from parse to STRUCTURE — where it finally becomes load-bearing), scored
by the 0176 self-verification gate + uniqueness, guided by 0177 cue-precision.
Locality bounds the search; relational cues constrain the cross-clause op so
uniqueness can RESOLVE (not just refuse). Coverage rises only as far as the reading
constrains structure; irreducible ambiguity refuses (wrong=0).
wrong=0 obligations: per-step self-verification, irreducible-ambiguity-refuses,
completeness+uniqueness over the whole structure, no-spurious-structure, determinism,
sealed. Honest hard parts: relational-cue precision (co-dependent with 0177 +
data-starved), clause segmentation, referent binding, branch/DAG (0033 quantity
reuse, GB-5), scale. This is the comprehension core — largest remaining capability;
serving lift materialises here, incrementally. Sub-phases GB-1..GB-6.
The lever MS-1->MS-3 proved: learn which (cue->op) readings are reliable from
practice eliminations, closing ADR-0175's self-verification 'necessary-not-
sufficient' gap before Phase 5.
Mechanism: per-(cue, op, unit_shape) reliability ledger (reuses ADR-0175 ClassTally
+ conservative_floor), fed by gold-labelling search candidate chains. Three uses:
U1 self-verification TRUST (near-term value: makes the Phase 5 proposal gate honest;
cold ledger => refuse, no junk proposals), U2 search guidance, U3 disagreement
resolution (coverage lever, hard-gated: margin+theta, ties refuse).
Load-bearing honesty (the bottleneck): a pattern earns POSITIVE signal only from a
gold-MATCHING chain; current blunt shapes match gold for ~4/50 cases, so the ledger
is starved (all-blame) AND structure failures (Gap B) pollute cue->op credit (a
correct op penalised for a product-of-ALL structure error). Plus data starvation
(50 cases << N_min). So cue-precision is COUPLED to richer guided shapes (Gap B) +
scale; it co-evolves (Gap B supplies gold-matching candidates -> cue-precision earns
signal -> prunes Gap B's search). It is the TRUST substrate + pruning engine, NOT
the coverage unlock by itself.
Recommended sequencing: CP-1/CP-2 (mechanism + self-verification trust, near-term
correctness value) now; richer guided shapes (Gap B) next as the flip lever; scale
makes it compound. wrong=0 obligations: cold=>no regression, ties refuse, theta-gated
serving, credit-noise can't flip serving (floor+N_min+margin+ratification+gold-tether),
determinism. Sub-phases CP-1..CP-4.
The dominant remaining lever for serving lift (79% need mul, median 3 steps;
single-step search + completeness flips only 0021). Grounded in gold step
structures: derivations are CHAINS with intermediate results as operands;
quantities come from body AND question (0033's '25'); several need comparatives
(half/N-times).
Decision: bounded, deterministic, TARGET-GUIDED multi-step grounded derivation
search, gated by ADR-0175's strengthened self-verification (grounding ∧ cue ∧
unit ∧ completeness) + uniqueness + a new question-target match. Sealed practice.
Two new ideas beyond 0175: question-targeting (turn the question into a target =
search-pruning + stopping criterion) and multi-step chaining (intermediates as
derived operands). Sub-phases MS-1..MS-5, wrong=0-first (gate/target before broad
search). Invariant #2 extended to chains. Honest hard part: search explosion +
uniqueness refuses most -> target-pruning + cue-guidance + depth-bound are the
tractability levers; low coverage initially; comparatives pack is a prerequisite;
serving lift still waits on 0175 Phase 5 ratification. Reuses solver +
question extraction + round-trip primitives.
Solid: 4 invariants proven by failing-under-violation tests; 84 green; seal
verified. No live hazards (9 search wrongs are sealed eliminations).
Drift: (1) 3a gate is PARTIAL vs spec — round-trip + no-contradiction not wired;
3b's necessary-not-sufficient finding follows -> a self-verification-strengthening
phase must precede Phase 5. (2) class taxonomy: Phase 2 uses gold operation-class
not capability-axis. (3) minor defensive-branch test gaps (no risk).
Lookback/cross-reference audit before any code. No hard blockers. Corrects 3
overclaims of reuse and records the audit:
- reliability ledger is NEW (calibration/ is a grid-search param tuner, not a ledger)
- 0174 eliminate/reevaluate/contemplate is reading-coupled -> 'repoint to solving'
needs generalization, not drop-in reuse
- teaching corridor evidence (MathReaderRefusalEvidence) is reader-refusal-coupled
-> solver-practice proposals need a new evidence type
Constraints recorded: wrong=0 pinned on serving lane by ~25 tests + train_sample
runner -> practice MUST be a separate lane (phasing updated). Alignments noted:
ADR-0165 (lexemes-not-grammar) fulfilled by thin-front-end/thick-solver split;
INV-07 governance = no-self-authorization pre-wired; MAX_TOTAL_BRANCHES precedent
for bounded deterministic search; seal exists.
Resolves Open Question #1. conservative_floor(s,k) = one-sided Wilson lower
bound over COMMITTED trials (k=correct+wrong; refusals excluded so coverage
never penalizes reliability). Constants: z=2.576 (single global pessimism
knob), N_min=10. Range [0,1) — never returns exactly 1.0. float64 rounded
half-to-even to 1e-9 for cross-backend replay. z (estimator skepticism) and
per-class theta (action's required reliability, human-set) are independent
dials; engine touches neither. Worked cost-to-clear table + asymmetry example
included.
Proposed ADR + session derivation doc capturing the 2026-05-28 design
discussion that took GSM8K Phase 5b from 'build another matcher' to a
self-calibrating problem solver.
Session doc (docs/sessions/SESSION-2026-05-28-...): the full journey —
problem (per-shape matchers can't compound; 79% need mul, 0% single-step),
dead-ends (brute-force spurious matches; 0021 is the only single-sentence
case and it's idiosyncratic), and the four pivots that converged on the
solution.
ADR-0175 (Proposed): the decision —
- two regimes: serving (wrong=0, unchanged) vs sealed practice
(attempt-and-eliminate; wrong is the learning signal)
- proof-carrying seal: practice never writes serving; ratification only
- deterministic attempt/refuse gate: reliability(C) / theta_required >= 1
(NOT RL; regimes collapse the reward side so only reliability is quantified)
- per-class calibration ledger of replayable COUNTS + conservative lower
bound; human-set theta ceilings raised only on evidence
- checkability ladder (gold > convergent self-verification > consistency-only),
privilege proportional to reversibility; provenance + gold tether against
correlated self-delusion
- diagnostic refusal routes skill vs knowledge vs ambiguity; three
compounding stores (vault/packs/pruning); self-proving acquisition narrows
human input without bypassing the gate
- five proof-obligation invariants (wrong=0 on serving, no spurious banking,
determinism, no self-authorization, retractability)
Supersedes the matcher-oriented ADR-0174 5b sub-phases; repoints the
0174 eliminate/reevaluate/contemplate substrate from reading to solving.
Open question: shape of conservative_floor + N_min.
Grounded in a ground-truth measurement of the 47 train_sample refusals
against GSM8K's own <<a*b=c>> calculator annotations:
- 37/47 (79%) use multiplication; 43/47 use mul-or-div; 0/47 single-step
- multiplication is the foundational general capability (breadth = the
anti-overfitting signal), but necessary-not-sufficient: no case flips
from an operation matcher alone
Solver already supports {add,subtract,transfer,multiply,divide,apply_rate,
compare_additive,compare_multiplicative}; the gap is the reader->injector->
Operation front-end (matcher extracts 0 anchors on real sentences).
Sub-phase sequence (biggest-chunk-first, measure-the-flip-gated):
- 5b.1 single-sentence multiplicative aggregate (cleanest proof, ~2-4)
- 5b.2 shallow 2-3 step composition (25/47, the real chunk)
- 5b.3 deep multi-step 4-7 (22/47) under held-hypothesis elimination
Generality guard: flipped cases must hold under ADR-0114a perturbation/OOD.
Explicitly NOT widening discrete_count injector (overfitting + wrong=0 hazard).
The recognizer/candidate-graph path is the single canonical reader.
Retires the flag-gated incremental-reader dispatch that admitted 0/50 on
train_sample and only added a dead fall-through:
- remove _try_comprehension_reader, _try_reader_for_question, _tokenize_sentence
and both dispatch blocks from generate/math_candidate_graph.py
- delete generate/comprehension/lifecycle_runtime_adapter.py (402 LOC,
used only by the question-reader dispatch)
- drop the comprehension_reader_questions config flag and the parse_and_solve
/ _score_one_candidate_graph config threading
- remove the --use-reader runner plumbing + flag-ON/OFF delta report from
the train_sample runner; refresh report.json (drops stale use_reader field
and a stale refusal-reason; verdicts unchanged at 3/47/0)
- remove the now-dead use_reader field from teaching/coverage.py
CoverageReport + the core teaching coverage CLI flag
- delete tests/test_reader_coexistence.py (flag-ON/OFF premise dissolved);
fix 3 ADR-0174 build_report calls and 2 subprocess invocations
lifecycle.py and audit.py are KEPT — they are load-bearing for the ADR-0172
math-contemplation teaching corridor (audit_problem -> teaching/math_*),
which a pre-deletion trace surfaced. The parent ADR's plan to delete
lifecycle.py was wrong; only its GSM8K scoring dispatch was inert.
Net -1,038 LOC (code + tests). Behavior-preserving:
- train_sample 3/47/0, byte-identical verdicts to pre-5a baseline
- determinism holds; smoke/packs/runtime/cognition/teaching lanes green
- contemplation corridor + lifecycle/audit tests pass
Pre-existing (NOT introduced here; reproduce on base with changes stashed):
5 out-of-curated-lane stale committed-artifact / stale-assertion failures
(test_math_evidence_e2e, test_adr_0126_runner_wiring, G3/coverage_probe
report-match, test_refusal_taxonomy_lane rebuild).
Pre-implementation investigation (lookback discipline) found the original
Phase 5 text inverted against shipped code:
- math_parser.py already out of runtime + candidate-graph scoring path
- lifecycle.py admits 0/50 (inert parallel parser, not the reader to promote)
- correct>=25 is a semantic gate structural collapse cannot meet
Decision (Invert + split): recognizer/candidate-graph path is the canonical
reader; lifecycle.py is retired. Phase 5a = structural retirement (net -LOC,
3/47/0 byte-identical, wrong=0). Phase 5b = semantic narrowness removal (the
real lift, own sub-phases, per-layer wrong=0 obligations).
All findings from the 2026-05-28 Phase 1-3a lookback review addressed
in one commit on the Phase 3a branch:
Wrong=0 hazard defense (the load-bearing fix):
- generate/math_candidate_graph.py: Phase 3a wiring now collects the
set of distinct proper-noun subjects seen in prior context. When
more than one exists, refuses with no_antecedent_ambiguous trace
event rather than guessing the most-recent (which was gender-blind
single-binding — wrong attribution in multi-actor problems).
- Refusals from the statement loop now preserve _statement_trace via
reader_trace in CandidateGraphResult (pre-existing latent issue:
Phase 2/3 trace events were dropped on early statement refusal).
- New tests assert: ambiguous case refuses with correct trace; single-
actor case still resolves normally.
Test coverage backfills (closes the 13 untested predicate-name gaps):
- TestCheckConstraintsInitialPredicateNames — 3 tests asserting the
exact predicate name on initial.value_grounds / initial.unit_grounds
/ initial.entity_grounds failure paths.
- TestCheckConstraintsOperationPredicateNames — 3 tests asserting
operation.verb_grounds / operation.value_grounds / operation.unit_grounds
failure-predicate-name parity.
- TestCheckConstraintsComposedInitialPath — 4 tests for the RAT-1
composed_initial path which was entirely untested in Phase 2
(parity manually verified during lookback review; now automated).
ADR amendment (honest doc vs impl drift):
- docs/decisions/ADR-0174-held-hypothesis-comprehension.md: appended
'Implementation Notes' section documenting:
- reevaluate signature differs from spec text (shipped is more
composable; treat as amended)
- Phase 2 wires per-candidate, not per-token (per-token is Phase 5)
- Lookback recompute is candidate-level, not token-level
- Hypothesis.constraint_state is never populated by Phase 2
- Multi-actor pronoun hazard defense rationale
- Honest LOC accounting: Phases 1-3a net +1,500 lines (Phase 5
delivers the projected net removal)
- Test coverage backfill summary
Cosmetic:
- lookback.py:297 unreachable raise — added # type: ignore[unreachable]
with comment explaining defensive future-proofing for Phase 3b.
Acceptance verified:
- 124/124 Phase 1+2+3a + reader tests pass (was 95/95 before backfills)
- Smoke 67/67, packs 141/141
- train_sample 3/47/0 preserved (wrong=0 invariant held)
- Multi-actor hazard live-tested: parse_and_solve refuses the
Alice/Bob/She case with no_antecedent_ambiguous trace event
See CLAUDE.md §Lookback Review Discipline and memory
feedback-lookback-review-discipline for the doctrine that surfaced
all of these issues at the right time.
ADR-0174 Phase 3a — substrate for held-hypothesis lookback.
Score unchanged at 3/47/0 (this PR is correctly-engineered
infrastructure; eval impact gated on ADR-0163.x recognizer expansion
documented in the follow-up brief).
Adds generate/comprehension/lookback.py:
- VALID_REFINEMENT_KINDS, VALID_UNRESOLVED_SLOTS — closed sets
contracted with reader_trace consumer
- PronounResolution refinement dataclass (pronoun + resolved_to +
evidence_source, all validated)
- Refinement Union (Phase 3b will widen with CompoundClauseExpansion)
- ReevaluateResult dataclass with admit/eliminate consistency
- reevaluate(hypothesis, refinement) operator — applies refinement,
re-runs check_constraints, returns refined Hypothesis or None.
- _rebuild_candidate_with_resolved_actor — rebuilds
CandidateOperation / CandidateInitial replacing the semantic actor
field (op.actor / initial.entity) while preserving matched_actor_token
/ matched_entity_token as the pronoun (so grounding still passes
against the held statement's source span).
Modifies generate/recognizer_match.py:
- _try_extract_discrete_count_anchor: pronoun-subject statements now
emit anchors with subject_role=<pronoun> + requires_pronoun_resolution
marker, rather than refusing at the _REFUSED_SUBJECT_TOKENS check.
The other narrowness layers (clause split, verb whitelist) still
refuse; only the pronoun layer changes.
Modifies generate/math_candidate_graph.py:
- After inject_from_match, when any parsed_anchor carries
requires_pronoun_resolution, the candidates are held as Hypothesis
objects with unresolved=('actor_pronoun',). The lookback path then
resolves via the existing _discourse_prior_subjects map and runs
PronounResolution refinements through reevaluate. Resolved
hypotheses flow into per_sentence_choices as if the regex parser
had produced them; unresolved hypotheses drop cleanly (refusal-
preferring). Emits 'lookback' JSON trace events with
outcome ∈ {admitted, eliminated, no_antecedent}.
Tests:
- tests/test_adr_0174_phase3_lookback.py — 17 acceptance tests
covering operator semantics on Operation/Initial, dataclass
invariants, closed-set constants, end-to-end wiring on synthetic
problems, and wrong=0 preservation on train_sample.
Phase 3.1 follow-up brief:
- docs/handoff/PHASE-3.1-FOLLOWUP-RECOGNIZER-EXPANSION.md documents
the empirical finding that the train_sample bottleneck is
verb-coverage (recognizer scope, ADR-0163.x) not lookback
(ADR-0174 scope). 11 verbs identified for HITL contemplation pass.
Recommends sequencing: Phase 3a now (substrate), ADR-0163.x verb
expansion next, Phase 3b after coverage matures.
Acceptance verified:
- 17/17 Phase 3a tests pass
- 95/95 existing tests pass (Phase 1 + Phase 2 + brief_11 + reader_phase2)
- Smoke 67/67, packs 141/141, lanes 8/8
- wrong=0 preserved, score unchanged 3/47/0 (intentional per brief)
Stacks on Phase 2 (PR #420). Rebases onto main after #416 + #420 land.
Phase 3b (compound-clause held hypotheses) is prerequisite for Phase
4 (in-loop contemplate) to have anything to operate on. Combined
scope rather than separate briefs because:
- Phase 3b alone: 0 lift on train_sample (multi-actor defense
refuses, solver gaps prevent admission)
- Phase 3b + Phase 4: 2-4 case lift via gendered-pronoun resolution
- Phase 3b + 4 + solver multi-qty (separate ADR): 6-8 case lift
First concrete Phase 4 use case: gendered pronoun resolution via a
new en_core_names_v1 pack. Turns the multi-actor defense from
refuse-on-ambiguity into admit-via-evidence when an unambiguous
gendered name exists for one antecedent.
Architecture overview, Phase 3b extractor design, Phase 4
contemplate adapters (vault > packs > audit_history), wrong=0
hazard surfaces, sequencing (3b then 4 stacked), truth tests, and
deliberately-excluded scope (solver work, verb expansion, per-token
apply_word integration — all separate ADRs).
HYPOTHESIS_CAP raise from 4 to 8 needed for Phase 3b (case 0040
has 5 anchors). Documented in scope.
References: ADR-0174 (held-hypothesis comprehension), CLAUDE.md
§Lookback Review Discipline, memories for multi-actor pronoun
hazard and case-0050.
Adds second mandatory hazard test to VE-A/B acceptance criteria:
multi-actor pronoun ambiguity must trigger no_antecedent_ambiguous
refusal per ADR-0174 Phase 3a defense.
Verb expansion widens the cases that reach Phase 3a lookback wiring;
without this test the multi-actor wrong=0 hazard could fire silently
in production. Surfaced by 2026-05-28 Phase 1-3a lookback review.
References: project-adr-0174-multi-actor-pronoun-hazard memory,
CLAUDE.md §Lookback Review Discipline.
Operationalises the recommendation from PHASE-3.1-FOLLOWUP-RECOGNIZER-EXPANSION.md
into three independent dispatchable PRs:
- VE-A: acquisition widening (gain, earn, save, accumulate, acquire) — Opus
- VE-B: new depletion class (donate, give, lose, spend, eat) — Opus
- VE-C: refusal evidence for non-arithmetic verbs (instrumentation only) — Sonnet
Each PR includes:
- Hazard pinning: explicit case 0050 test must pass after widening
- Lift evidence: at least one train_sample case moves refused → correct
- Phase 3a substrate fires: the lifted case shows a 'lookback' trace event
- wrong=0 preserved across train_sample AND case 0050
Operator decisions needed before dispatch: which specific lemmas to
admit per class, whether to introduce the depletion class at all, and
whether to ship VE-C as evidence groundwork.
Verb classification rationale per lemma documented in the brief.
Hazard surfaces called out per lemma (delta-of-attribute for 'gained',
direction inversion for 'lost', monetary-vs-time ambiguity for 'saved').
No timelines; operator dispatches when ready.
Extends ADR-0164's incremental comprehension reader from single-committed
state to held-hypothesis state, adding lookback re-evaluation and in-loop
contemplation. Diagnoses why the ADR-0164 reader is wired but inert
(all-or-nothing refusal at first unknown token / unexpected category).
Architecture: apply_word produces ProblemReadingState.open_hypotheses
(small ranked set, HYPOTHESIS_CAP=4 initial). Three operators per token:
EMIT (extend compatible hypotheses), ELIMINATE (constraint violations
remove hypotheses immediately), HOLD (uncommitted hypotheses survive at
lower confidence). At finalize(), |survivors|=0 refuses, |survivors|=1
admits, |survivors|>=2 invokes in-loop contemplate() over vault + packs
+ audit history. Ambiguity contemplation cannot resolve refuses cleanly,
preserving wrong=0.
Collapses three parallel parsing systems into one held-hypothesis
reader: removes regex parser runtime path (math_parser.py),
per-category injector dispatch table (recognizer_anchor_inject._INJECTORS),
and duplicate per-sentence-choices scaffolding. Net ~1,900 lines
removed; reader grows by ~600 for hypothesis state + reevaluate +
contemplate.
Preserves ADR-0164 lexicon and category set, ADR-0165 regex scope,
ADR-0150/0152/0155/0161 HITL corridor, binding graph + solver
substrate, capability-axis lanes, replay-equivalence gate. Trust
boundary for in-loop contemplation: read-only over vault/packs/audit;
ratification still rides offline HITL.
Status: Proposed. Six phases (no timelines) gated on wrong=0 and
capability-axis 100% at each transition. Five open questions resolved
before Phase 1 PR.
C1: delete generate/math_versor_arithmetic.py and its 3 tests (ADR-0139
add-only arithmetic spike; no runtime consumers, no pipeline wiring,
follow-on lift paused per module docstring).
C3: gitignore engine_state runtime artifacts (manifest.json,
recognizers.jsonl, discovery_candidates.jsonl). Module code
(engine_state/__init__.py) remains tracked; generated checkpoint
files should not be.
C5: document reader zero-delta root cause in train_sample/v1/README.md.
Both Phase 2 (whole-problem) and Phase 1 (question-only) reader paths are
called but inert because all 47 refusals are statement-level NO_INJECTOR
gaps, not question-sentence gaps. Reader unblocks when injector coverage
expands (C2 work). report.json use_reader flag corrected to reflect last run.
C6: add deprecation header to generate/math_parser.py pointing at
generate.math_candidate_graph.parse_and_solve as the live path.
C2/C4 briefs: docs/handoff/CLEANUP-C2-run-lane-migration.md and
docs/handoff/CLEANUP-C4-compositions-compile.md added as operator
dispatch docs for the medium-scope wiring tasks.
Phase 1 and Phase 2 of the ADR-0164 reader are correctly implemented but
contribute zero eval admissions today. The bottleneck is lexicon coverage
(unknown verbs/nouns) and explicit Phase 2.1 scope gates (fractions), not
the all-or-nothing dispatch policy.
Produces COMPREHENSION-READER-AUDIT.md answering the five Brief C questions:
call trace, cognition-lane usage (none), bottleneck analysis, ADR promise
audit, and three falsifiable options (operationalize/relabel/retire).
Recommendation: relabel (status update) now; lexicon expansion next as the
highest-leverage first step toward actual eval lift.
Also updates ADR-0164 status from "Proposed" to "Partially implemented" with
a Current Status table and next-lift-path summary.
After RAT-1's architecture audit exposed five systematically
underbuilt components, A (the 4 missing injectors) is being taken by
this agent. The other four are parallel-safe and each gets its own
dispatch brief:
B (Opus) — contemplation produces ratifiable claims (medium)
C (Sonnet) — comprehension reader audit + decision (small→medium)
D (Sonnet) — core teaching coverage CLI (small)
E (Codex) — lexical ratification auto-compile (tiny)
Each brief carries: operator profile, branch name, base, dispatch
line, reads required FIRST, outcome inventory, hard requirements,
tests, truth test. Anti-regression invariants enumerated. All
parallel-safe — no shared file conflicts.
The dispatch DAG names ALL FIVE pieces with A in-flight as a peer.
Final PR of the matcher-extension wave. Ships:
1. tests/test_me5_all_categories_integration.py — 4 new tests:
- test_all_three_canaries_admit_through_full_pipeline: stages a
pack with all three SAFE_COMPOSITION_CATEGORIES entries +
ratifies, runs Maria/Sam/Tom canaries through matcher →
inject_from_match, asserts admission for all three
- test_partial_pack_only_admits_present_categories: refusal-
preferring when only one category is ratified
- test_all_safe_categories_have_extension_admission: pins that
SAFE_COMPOSITION_CATEGORIES is exactly the three covered
categories (breaks if future ADR widens without matcher)
- test_falsifies_uniformly_suppresses_across_categories:
polarity discipline holds across all three matchers
2. docs/handoff/ME1-ME5-MILESTONE.md — wave milestone doc:
- architecture diagram (audit → ratify → compile → load →
match → consult → admit)
- SAFE_COMPOSITION_CATEGORIES coverage matrix
- invariants preserved across the entire stack
- scope boundary (what does NOT fire yet — RAT-1 follow-up)
- recommended next dispatch
3. Test registration in core/cli.py packs suite.
Across the full ME-1..ME-5 stack:
- 5 stacked PRs (#400/#401/#402/#403/#404)
- 1 foundation PR (#398 — consumption wiring)
- 114 new tests, all green
- packs suite 127 passed
- core eval gsm8k_math --split public → 150/150, wrong=0
- All three SAFE_COMPOSITION_CATEGORIES have matcher extensions
Anti-regression invariants preserved across the entire stack:
- wrong == 0 on public split
- Case 0050 hazard pin (parametrized over all three categories)
- ADR-0166 — no new eval lanes
- ADR-0167 partition — no cognition imports
- ADR-0169 mutation boundary — registry is a gate, not arithmetic
- All matcher detection paths byte-identical
- engine_state/* never committed
- SAFE_COMPOSITION_CATEGORIES enforced at write AND load
- polarity falsifies honored uniformly
Live train_sample admission requires operator-seeded ratifications
(RAT-1 follow-up). Wiring is end-to-end correct, verified by ME-5
integration tests.
Memory: milestone-me1-me5-matcher-extensions-complete saved.
Stacks on PR #403 (base: feat/matcher-extension-subtractive).
Two pre-public-release refreshes:
1. docs/position_paper.md §4 — refresh refusal taxonomy
The previous table listed an older taxonomy (fraction_operand /
compound_comparative / etc.) that does not match the current
candidate-graph eval output. Replaced with the actual taxonomy
emitted by the train_sample runner — refusals are categorized by
shape (recognized_but_uninjectable + per-ShapeCategory) which ties
directly to the architecture's recognizer/injector concepts.
Also refreshed §5 "Honest Gaps" to describe the active frontier in
terms of injector coverage (the registry-driven composition path)
rather than parser-level grammar extensions. The 47/50 refusal
count, 3/50 correct count, and wrong=0 invariant are unchanged.
2. COMMERCIAL_LICENSE.md — enumerate subsystems explicitly
The previous "vault memory / versor engine / epistemic teaching
loop / ingest / admissibility gate / holonomy encoder" listing
predated the math-domain composition wave. Expanded the list to
cover the full current architecture:
- versor engine + Cl(4,1) algebra
- vault + exact CGA recall
- ingest gate
- admissibility gate + Forward Semantic Control
- holonomy encoder
- epistemic teaching loop (ADR-0055..0057)
- contemplation loop + decomposer (ADR-0080, ADR-0172)
- math-domain ratification handlers (ADR-0167..0169)
- composition-pattern registry + registry-driven injector
- audit-as-teaching-evidence corridor
- identity + safety pack subsystems
- language-pack compiler + verified manifest checksums
- Logos articulation + three-language vocabulary manifold
- Rust algebra backend
- any derived or successor implementation
Explicit "enumerated subsystems are illustrative, not limiting"
clause covers any future module + ADR-ratified contract under the
same terms. New subsystems are covered automatically.
LICENSE (the non-commercial form) is unchanged — it already covers
"Software" broadly. CLAIMS.md is auto-generated and was verified
current (regeneration produces no diff).
Follow-up to PR #398 — lights up the dormant consumption path by
extending _match_rate_with_currency to publish composition_shape +
pre-composed CandidateInitial in parsed_anchors.
Scope: one matcher extension (_match_rate_with_currency) for the
currency-per-unit composition shape ("$X each"). Other composition
shapes (multi-quantity, additive, subtractive) deferred to ME-3/4/5
follow-ups.
Subject binding decision pinned: Option A (refuse when same-sentence
subject is absent). Forbids Option B (placeholder subject —
fabricates attribution). Defers Option C (cross-sentence subject)
to its own ME-2 brief.
Honest consequence: case 0019 stays refused in THIS PR (requires
Option C). Truth-test #1 replaced with a synthetic "Maria bought 3
vet appointments at $400 each" canary that has same-sentence
subject. The flywheel turns one revolution: ratify → compile →
load → consume → admit, end-to-end on the synthetic canary.
Operator profile: Opus (load-bearing wrong=0; pre-composed
CandidateInitial; case 0050 mandatory pin).
Test surface enumerated (4 test files, 12+ tests):
- test_matcher_extension_currency_per_unit (8 narrowness tests)
- test_matcher_extension_case_0050_hazard_pin (mandatory)
- test_matcher_extension_end_to_end_admission (truth test)
- test_matcher_extension_train_sample_baseline_preserved
- test_matcher_extension_public_split_preserved
6-row truth test pinned. Anti-regression invariants enumerated.
Forbidden surface includes Option B + new SAFE category entries +
solver mutation.
Recommended next dispatch sequence: ME-1 → ME-2 (case 0019) →
ME-3/4/5 (remaining composition shapes).
Production-line dispatch form of the consumption-wiring brief in
PR #396. One bundled PR recommended (CW-1 + CW-2 share pack-compile
+ manifest extension + case 0050 pin). Split only if CI cycle time
forces.
CW-1 — Frame consumption:
language_packs/compile_frames.py + generate/comprehension/frame_registry.py
+ reader wire + manifest frame_checksum extension.
CW-2 — Composition consumption:
language_packs/compile_compositions.py +
generate/comprehension/composition_registry.py + injector wire in
generate/recognizer_anchor_inject.py + manifest
composition_checksum extension. SAFE_COMPOSITION_CATEGORIES
enforced at load (defense in depth); polarity "falsifies"
suppresses injection (not silently "affirms").
Truth test pinned as binding (6-row table). PR is not done unless
case 0019 admits, case 0050 stays refused, train_sample moves from
3/47 → ≥4/46, wrong==0 holds, public split unchanged (150/150),
empty-registry runtime byte-identical to today.
Operator profile: Opus (load-bearing wrong=0 surface; case 0050
mandatory pin; same rigor as CC-2).
Names the structural gap discovered in the first end-to-end
CompositionClaim ratification (2026-05-27 post-#393):
ratification handler writes JSONL artifacts cleanly, but no runtime
code reads compositions/*.jsonl or frames/*.jsonl. Two of three
sub-types ship the ratification half of the loop without the
consumption half.
State:
lexicon/ writer ✓ reader ✓ (LexicalClaim — closed)
frames/ writer ✓ reader ✗ (FrameClaim — half-open)
compositions/ writer ✓ reader ✗ (CompositionClaim — half-open)
Proposes one bundled PR (CW-1 + CW-2) mirroring the proven
generate/comprehension/lexicon.py::load_lexicon pattern:
CW-1 — Frame consumption: pack-compile frames/*.jsonl into a
runtime-loadable artifact; new load_frame_registry()
loader; reader wire.
CW-2 — Composition consumption: pack-compile compositions/*.jsonl;
new load_composition_registry(); injector wire in
generate/recognizer_anchor_inject.py.
Hard requirements: SAFE_COMPOSITION_CATEGORIES allowlist enforced
at both write and load (defense in depth); polarity "falsifies"
honored; manifest checksum extended per CLAUDE.md "Semantic Pack
Discipline"; empty-registry runtime byte-identical to today.
Truth-test pinned: success is the EVAL DELTA, not the artifact
append. PR is "done" when case 0019 (the canary I ratified) admits
under train_sample, train_sample moves from 3 correct / 47 refused
to ≥4 correct / 46 refused, case 0050 stays refused, wrong==0 holds.
This brief is orthogonal to the workbench UI wave (W1..W4); both
can ship in parallel. Recommended operator: Opus (load-bearing
wrong=0 surface; same rigor as CC-2).
W0 of the workbench-UI wave per WORKBENCH-UI-WAVE-SCOPING.md.
Pure docs PR; no UI code.
Decision: the workbench is a local keyboard accelerator for the
existing local-CLI ratification surface (ADR-0161 Surface C), not a
fourth ratification surface and not a new trust boundary. Every
workbench-driven ratification action invokes the same Python
entrypoint as the corresponding CLI invocation, with identical
preconditions, exceptions, and append-only JSONL effects.
Amends ADR-0160 v1 read-only stance narrowly: admits driving the
three Tier 1.5 handlers (apply_lexical_claim, apply_frame_claim,
apply_composition_claim) and the existing core teaching review path
through the workbench API. Honors ADR-0161's surface set unchanged
(workbench is part of Surface C, not a new surface). Honors ADR-0162
no-go list and pins the keyboard contract referenced in §7.
Five open questions from the scoping brief resolved:
Q1 in-process via existing Python entrypoints
Q2 single-operator (multi-operator deferred)
Q3 same chat/telemetry.py JSONL sink, new event kinds
(operator_ratify / operator_reject / operator_defer /
operator_navigate); no parallel log
Q4 fonts and icons bundled locally; no CDN
Q5 dist/ gitignored; CI verifies build, does not commit artifact
Ratification record extended with ratifier_kind: "workbench" — audit
forensic discriminant only, not a permission gate. proposal_id + to
remain the only load-bearing replay fields (ADR-0161 §5 unchanged).
Compatibility audit covers ADR-0146, 0150, 0152, 0160, 0161, 0162,
0167, 0168/0168.1, 0169/0169.1, 0172. Forbidden surface enumerated:
no remote operator, no CORS relaxation, no new mutation paths, no
auto-ratify, no batch ratification, no engine_state writes outside
the existing checkpoint path, no parallel workbench-events log, no
mobile/responsive form factors (ratification stays on Surfaces A+B
for mobile per ADR-0161).
Acceptance gates pinned per W1..W4 of the scoping brief.
Names the wave shape before any implementation. Audits what doctrine
has already settled (ADR-0160 stack pins + read-only trust boundary;
ADR-0162 design system + 15-component must-ship list + no-go list),
catalogs the current state (backend complete, frontend zero), surfaces
the five operator pain points sourced from the 2026-05-27 demo and the
CompositionClaim brief pack, and proposes a five-wave sequence:
W0 (docs) — Trust-boundary ratchet ADR (ADR-0173 working title)
admitting operator ratification through the workbench,
scoped to existing Tier 1.5 handlers (Lexical / Frame /
Composition) and pinning the keyboard contract.
W1 (base) — workbench-ui/ scaffold per ADR-0162 Branch 1.
W2 (read) — ProposalQueue + ProposalCard + ProposalDetailPanel.
W3 (act) — RatificationCommandPanel + handler dispatch (the
throughput multiplier).
W4 (verify)— TraceDrawer + ReplayTheater + EvalCenter.
Guardrails enumerated: CLAUDE.md docs discipline, ADR-0162 no-go list,
determinism, trust boundary, wrong==0, case 0050 hazard pin,
keyboard-first, accessibility, local-first, no engine_state writes
outside the checkpoint path.
Open questions (in-process vs out-of-process ratification, single- vs
multi-operator, telemetry path, font/icon bundling, build artifact
location) listed for W0 resolution. No code in this brief.
* docs(tier1.5+tier2): FrameClaim handler + Tier 2 W5 schema brief pack
Two parallel briefs ready for dispatch:
- F1: FrameClaim ratification handler (Opus) — implements ADR-0168 +
ADR-0168.1; turns 2 of 8 Tier 1 proposals into real GSM8K
admissions on ratify
- T2-W5: MathReaderInferenceProposal schema (Sonnet) — Tier 2 substrate
with two-arm test-and-learn type contract
Zero file overlap between briefs; safe to dispatch concurrently. W6+
deferred until FrameClaim is live (Arm 2 known-good preservation needs
a working handler to verify against).
* docs(briefs): correct pack + workbench paths in Tier 1.5/Tier 2 brief
Operator dispatching F1 (PR #389) discovered:
- Pack path is language_packs/data/en_core_math_v1/, not packs/en_core_math_v1/
- Workbench dispatch table lives in workbench/readers.py, not workbench/api.py
Both operators landed on the correct locations regardless. Folding the
corrections into #387 directly so future operators reading the brief
get the real paths.
The brief pack referenced `teaching/audit_evidence.py` in 3 spots
(A2 schema field, A2 read-required list, B2 algorithm step 3a). The
actual module on main is `teaching/math_evidence.py` (carries
`MathReaderRefusalEvidence` per ADR-0167).
Sonnet (A2 / PR #380) discovered the discrepancy and correctly used
the real module. This patch corrects the brief so Wave B operators
(B1 / B2) do not hit the same gap on dispatch.
No runtime change. Pure docs.
Coordination artifact for ADR-0172 Tier 1 dispatch. Six PRs in the
ADR (W0, W0.1, W1, W2, W3, W4) collapsed into a 4-wave DAG with two
concurrent briefs per wave A and B.
Operators are dispatched in their own UIs by pointing at the
section heading — "read §Brief A1" etc. Bundling options A/B/C
inside the doc let the operator choose CI economy vs wall-clock.
No runtime change. Pure docs.
* docs(ADR-0172): math-domain corpus-decomposition mechanism (Learning Arc analog)
Scoping ADR for the math-domain analog of cognition's
`teaching/contemplation.py` corpus-decomposition loop (Learning Arc
milestone 2026-05-25).
## What this ADR scopes
A mechanism that reads the math audit corpus and emits
`MathReaderRefusalShapeProposal` records — structural commonalities
across N refusal cases, paired with the candidate mechanism change
that would resolve them (matcher extension, injector sub-shape,
vocabulary addition, frame reclassification).
Today the operator does this decomposition by hand (reads
audit_brief_11.md, identifies the commonality across 21 DCS
refusals, scopes the matcher/injector extension, files a focused PR).
ADR-0172 shifts the decomposition to the engine, with HITL
ratification preserved.
## Sequencing — explicit
ADR-0172 ships AFTER ADR-0170 (injector contract widening),
ADR-0168 (FrameClaim handler), and ADR-0169 (CompositionClaim
handler — reserved). Without those substrates, the decomposer can
identify patterns but cannot route them to a ratification handler
that knows how to materialize them. Cognition's learning arc
followed this same sequencing: substrate first, then decomposer.
## Why this matters
ADR-0167 LexicalClaim shipped the math-domain wire from refusal →
evidence → operator-ratification. ADR-0172 closes the gap to the
engine-decomposes loop — the moment cognition's learning arc
qualitatively shifted from "engine refuses + operator authors" to
"engine teaches itself through reviewed correction."
The Learning Arc memory entry (2026-05-25) names that moment as
when measurable progress accelerated. ADR-0172 makes the math-domain
trajectory toward the same loop explicit in the queue.
## Hard invariants preserved
- wrong=0 by construction (proposals are evidence-only)
- ADR-0166: no new eval lanes
- No teaching-store / pack mutation
- No non-deterministic mechanism (rule-based grouping, not learned
classification)
- Cross-domain partition (ADR-0167 W2-C) preserves cognition
contemplation behavior
No code, no test, no eval, no pack change in this PR.
## Cross-references
- ADR-0056/0057 — cognition contemplation/proposal substrate (template)
- ADR-0167 + FOLLOWUPS §1 — parent evidence wire
- ADR-0168 + ADR-0168.1 — FrameClaim (ratification target)
- ADR-0169 (reserved) — CompositionClaim (ratification target)
- ADR-0170 — injector contract widening (substrate prerequisite)
- Memory: Learning Arc Milestone 2026-05-25 — the moment to recreate
- Thesis: decoding, not generating — the principle preserved
* amend(ADR-0172): add Tier 2 — intensional contemplation with test-and-learn loop
Per operator feedback during ADR-0172 review: the corpus-decomposition
mechanism should not only emit explicit rules (extensional) but also
develop inference (intensional) — recognizing structural equivalence
classes across surface variations without enumerating them.
## Tier 2 — intensional contemplation
Engine recognizes that 'Sam has 5 apples' and 'Sam collected 5 apples'
carry the same canonical proposition structure, without an explicit
verb-list extension. Emits MathReaderInferenceProposal records that
name structural equivalence classes rather than enumerable rules.
This is the thesis word the original draft missed: rationalization.
Tier 1 ratifies rules; Tier 2 ratifies inference.
## Test-and-learn loop
Tier 2 proposals carry held-out test evidence:
1. Decomposer surfaces hypothesis
2. Held-out subset of corpus reserved
3. Bridge applied to held-out cases; admissibility gates run
4. Outcome scored (positive / negative / neutral)
5. Negative-evidence proposals auto-rejected before HITL
6. Operator reviews proposal + test result, not bare claim
This makes Tier 2 thesis-coherent: engine decodes a structural
pattern, tests it against unseen corpus cases, surfaces the test
result. Wrong=0 cannot leak through — held-out test failures reject
internally.
## Updated implementation outline
Tier 1 wave: W1-W4 (schema, decomposer, CLI, workbench integration)
Tier 2 wave: W5-W9 (schema, equivalence-class recognizer, test-and-learn
loop, HITL integration, bridge application path)
## Hard invariants preserved at both tiers
- wrong=0 by construction (Tier 1: evidence-only proposals; Tier 2:
held-out test rejects wrong-admitting bridges internally)
- ADR-0166: no new eval lanes
- No non-deterministic mechanism (rule-based grouping + deterministic
test-and-learn, not learned classification)
- Cross-domain partition preserves cognition contemplation behavior
* amend(ADR-0172): split Tier 2 test-and-learn into two-arm confirmation
Per operator feedback during ADR-0172 review: 'confirm against known
facts/prior solutions' is the missing arm. The Tier 2 test-and-learn
loop now has BOTH:
- Arm 1 (negative / wrong=0 on held-out refusals) — already drafted
- Arm 2 (positive / known-good preservation) — NEW
Arm 2 inherits ADR-0057's replay-equivalence contract: any
inferential bridge that would change a currently-correct outcome is
REJECTED INTERNALLY before reaching HITL, even if the new outcome is
defensible. Existing truth survives; new truth is gated.
Both arms must PASS or be neutral. Either arm rejecting → proposal
does not reach the operator. This makes the engine's reasoning
provably conservative: it confirms against truth it already knows AND
truth it hasn't yet decided.
The 5-step proposal lifecycle is updated to reflect both arms +
test-set partition + per-case verdict tables in the emitted proposal.
No code change. No runtime effect.
* amend(ADR-0172): add foundational reasoning-articulation substrate
Per operator feedback: for the engine to infer/test/learn from
feedback, it must first be able to ARTICULATE its own reasoning in a
structured, persistent, replayable form.
Articulation is the project thesis's 5th anchor ("listen → comprehend
→ recall → think → articulate → learn from reviewed correction →
replay"). Today CORE articulates SURFACE (templated realizer output)
but does not articulate REASONING — the inference chain that took the
engine from refusal corpus to hypothesis to proposal.
Without reasoning-articulation, none of the three loops can work:
- Loop 1 (self-test) has nothing to record about what it tested or why
- Loop 2 (HITL review) sees a black-box conclusion, not inference chain
- Loop 3 (feedback) has no specific step the operator can target with
a rejection rationale
## Substrate: ReasoningTrace schema
Every proposal carries a typed, content-addressable
ReasoningTrace recording each inference step:
ReasoningStep:
step_kind: observation | grouping | abstraction | hypothesis |
test_design | test_application | test_result | conclusion
input_pointers: prior steps + evidence rows
claim: human-readable assertion at this step
justification: why the engine made the claim
output_payload: type-discriminated by step_kind
The trace is byte-identical across replays of the same corpus +
verdict history. Inherits CORE's existing determinism discipline.
## Sequencing
Articulation ships FIRST (new W0 wave) — it is the prerequisite for
Tier 1 and Tier 2 and Loop 3. Each downstream wave emits or consumes
ReasoningTraces.
## Hard invariants preserved
- Deterministic-replay (trace byte-identical under same inputs)
- ADR-0057 replay-equivalence (trace IDs stable across reruns)
- No non-determinism added (rule-based step emission, not learning)
- ADR-0166: no new eval lanes
No code, no test, no eval, no pack change in this PR.
Three concrete cleanup items from the day's work, per the
cleanup-as-you-find memory principle.
## 1. Remove inject_rate_with_currency stub
PR #369 (A2 rate_with_currency) shipped a function that always returns
() with an extensive docstring documenting the Rate-not-in-SentenceChoice
schema gap. The function is dead at runtime — `_INJECTORS.get(category)`
returning None has the same downstream behavior as the function
returning (). The 16 tests pinned the empty-tuple return; the case-0050
hazard pin is duplicated in test_recognizer_skip_wrong_zero.py and
test_brief_11b_step2_lexicon.py.
The schema gap is now properly documented in ADR-0170 (PR #372). A
dispatch-table comment at the removal site retains the at-code pointer
to that ADR for anyone wiring a new injector.
Removed:
- `inject_rate_with_currency` function in generate/recognizer_anchor_inject.py
- Its `_INJECTORS` dispatch table entry
- Its `__all__` export
- tests/test_injector_rate_with_currency.py (371 lines, 16 tests)
## 2. Remove docs/handoff/GPT55-MOBILE-DISPATCH.md
Single-session travel-time scaffolding. The 5 tasks it named are
complete or superseded by ADR-0170's findings. Pure historical artifact.
## 3. Remove docs/handoff/WAVE-NEXT-INJECTORS.md
Superseded by docs/handoff/WAVE-NEXT-REVISED.md, which captures
everything load-bearing from the original brief in its A1–A4 findings
table. The "kept for history" justification didn't survive scrutiny:
the document was misframed (over-promised lift; misframed schema work
as injector work). Lessons captured in REVISED + ADR-0170.
Updated cross-references:
- WAVE-NEXT-REVISED.md: removed the "supersedes ... kept for history"
pointer; tightened cross-reference list
- ADR-0167-FOLLOWUPS.md §7: rewrote pointer to name ADR-0170 + REVISED
as the live plan rather than "the original is retained"
## Test plan
- 219 tests passed across G.2/G.4/G.5/S1/Brief 11/B1/B11A/wiring/partition/DCS-D.2
- evals/gsm8k_math/train_sample/v1/report.json untouched (regen
surfaces a separate stale-baseline test issue — out of cleanup scope)
- No runtime behavior change
## Net impact
- 5 files removed (~1200 lines)
- 1 file modified for explanatory comment (~30 lines)
- 2 doc files updated to remove dangling cross-references
- 0 behavioral change
DCS-S1 (proper-noun possession sub-shape expansion) investigation
revealed that the recognizer-injector path's `CandidateInitial`-only
return type is a substrate-level constraint blocking four Wave-Next
sub-shape categories — not just one.
## Two artifacts
1. **`docs/handoff/DCS-S1-FINDING.md`** — investigation result. Of
the 21 DCS-refused GSM8K cases, zero are pure S1-only blockers.
Acquisition-verb expansion (`collected`, etc.) conflicts with
ADR-0131.G.1's branch-disagreement discipline. The right fix is
the DCS injector emitting `CandidateOperation(add)`, but the
`inject_from_match` return type doesn't allow that.
2. **`docs/decisions/ADR-0170-injector-contract-widening.md`** —
scoping ADR. Names the contract change, the four categories it
unblocks (DCS-S1 acquisition, A1 currency, A3 multiplicative,
A4 temporal), the three load-bearing rules it must preserve
(ADR-0131.G.1, SentenceChoice union, admissibility gates), and
a 5-step implementation outline.
## Pattern recognised
Wave-Next surfaced four schema gaps. All four trace to the same
constraint: per-category injectors can only emit `CandidateInitial`.
The right next-capability work is ADR-0170 ratification, then a
small no-behavior-change PR widening the contract, then per-injector
follow-up PRs against the widened contract.
That is the actual lift-per-risk path for GSM8K Round-1 closure.
## Test plan
Docs-only. No code, no test, no eval, no pack change.
## Cross-references
- ADR-0163.D.2 — original parsed_anchors → solver-state ADR
- ADR-0131.G.1 — branch-disagreement discipline ADR-0170 preserves
- ADR-0167 — parallel teaching-corridor mechanism (independent)
- ADR-0167-FOLLOWUPS §7 — Wave-Next findings backlog
- WAVE-NEXT-REVISED — parent plan; ADR-0170 is the upstream blocker
- PR #369 — A2's schema-refusal artifact (first observation of gap)
The Wave-Next injector dispatch (A1-A4) surfaced findings that
invalidate three of the four briefs' lift assumptions:
- A1 currency_amount — sandbox-blocked; real lift potential intact
- A2 rate_with_currency — schema gap (Rate not in SentenceChoice
union); PR #369 documents the gap with concrete 4-step plan
- A3 multiplicative_aggregation — Operation(multiply) spec wrong;
correct emission is CandidateInitial(outer×inner); zero GSM8K cases
match canonical narrow form anyway
- A4 temporal_aggregation — needs apply_rate primitive not in algebra
Three of four are schema-extension ADRs masquerading as injector work.
Only A1 is a true injector + sandbox-fix scenario.
Deliverables:
1. `docs/handoff/WAVE-NEXT-REVISED.md` (new) — supersedes
WAVE-NEXT-INJECTORS.md. Pivots to DCS sub-shape expansion as the
actually-tractable next wave (21-case bucket, existing v1 injector,
#366 spec on main). Captures the three schema gaps + A1's
preserved lift potential for separate ADR work.
2. `docs/handoff/ADR-0167-FOLLOWUPS.md` §7 (new) — points to the
revised plan and summarises the four findings inline.
WAVE-NEXT-INJECTORS.md retained for history.
No code change. No runtime effect. Docs-only.