* 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.
29 KiB
ADR-0172 — Math-Domain Corpus-Decomposition Mechanism (Learning-Arc Analog)
Status: Proposed (scoping ADR; no runtime change in this PR) Date: 2026-05-27 Author: Shay Parent: ADR-0167 (audit-as-teaching-evidence) Related: ADR-0055/0056/0057 (cognition contemplation/teaching-chain proposal corridor), ADR-0168 (FrameClaim), ADR-0169 (CompositionClaim — reserved), ADR-0170 (injector contract widening), Learning Arc milestone 2026-05-25 Gating rule: ADR-0166
Context
The cognition learning arc (teaching/contemplation.py +
teaching/proposals.py, completed 2026-05-25) closed this loop:
refusal → audit row → contemplation decomposes corpus →
engine PROPOSES teaching chain → operator ratifies → corpus extends →
next session admits what was previously refused
The load-bearing word is PROPOSES. The engine reads its own
cognition corpus, decomposes it into discovery candidates with
polarity / domains / evidence / sub-questions, and emits
TeachingChainProposal records for HITL review. The operator's
cognitive load is review, not invention. That was the moment the
project shifted from "engine refuses + operator authors" to "engine
teaches itself through reviewed correction."
The math domain has the same shape of data:
evals/gsm8k_math/train_sample/v1/audit_brief_11.json— 47 refusals grouped byrefusal_reason × missing_operator- Structural commonalities are visible in the taxonomy: 21 cases hit
discrete_count_statementwith similar narrowness-violation patterns; 9 hitpre_frame_filler_sentence; 8 hitmulti_quantity_composition; etc. - ADR-0167 wires refusal evidence into the existing contemplation
corridor as
MathReaderRefusalEvidence
What math does not have is the analog of cognition's contemplation decomposition: a mechanism that reads the refusal corpus and emits structural proposals (matcher extensions, injector sub-shapes, possession-verb additions, narrowness-rule relaxations).
Today the operator does this work by hand: read audit_brief_11.md,
identify the structural commonality across N refusals, scope the
matcher/injector extension, file a focused PR. That works but it's
operator-as-decomposer, not engine-as-decomposer.
Foundational substrate — reasoning articulation
Before Tier 1 ships any proposal, the engine must be able to articulate its own reasoning in a structured, persistent, replayable form. Articulation isn't surface output (the realizer already does that); it's the chain of inferences that took the engine from refusal corpus to hypothesis to proposal.
The project thesis names articulation as the fifth step in the anchor sequence — listen → comprehend → recall → think → articulate → learn from reviewed correction → replay deterministically. Today CORE articulates surface (templated sentences via the realizer). It does not yet articulate reasoning.
Without reasoning-articulation, the three loops cannot work:
- Loop 1 (self-test) has nothing to record about what it tested, what intermediate claims it made, or why
- Loop 2 (HITL) reviews a black-box conclusion ("propose X") rather than the chain of inferences that produced X
- Loop 3 (feedback) has no specific reasoning step the operator can target with a rejection rationale; "rejected because step 3 was unsound" requires reasoning step 3 to exist as a record
ReasoningTrace schema
Every proposal the decomposer emits carries a ReasoningTrace —
a typed, content-addressable record of the inference steps that
formed it.
ReasoningTrace:
trace_id: str # content hash of the step sequence
steps: tuple[ReasoningStep, ...]
ReasoningStep:
step_index: int
step_kind: Literal[
"observation", # raw evidence pulled from the corpus
"grouping", # structural clustering of observations
"abstraction", # equivalence-class claim
"hypothesis", # candidate canonicalization / rule
"test_design", # what would falsify the hypothesis
"test_application", # bridge applied to held-out / known-good
"test_result", # scored outcome
"conclusion", # the proposal that emerges
]
input_pointers: tuple[str, ...] # IDs of prior steps / evidence rows
claim: str # human-readable assertion at this step
justification: str # why the engine made this claim
output_payload: object # type-discriminated by step_kind
Each step is content-addressable. The trace is byte-identical across replays of the same corpus + verdict history. Proposals carry the trace verbatim; operator reviews the trace, not the conclusion alone.
Why this is a substrate, not a Loop 4
Loops 1/2/3 each consume the reasoning trace:
- Loop 1 emits a hypothesis step + test-design step + test-result steps as part of self-checking
- Loop 2 (HITL review) displays the trace as the artifact under review
- Loop 3 (feedback) attaches operator verdicts to specific step indices, so "rejected because step 3 was unsound" becomes machine-readable
Without the trace substrate, none of the loops have anything to operate on. The trace is the what gets articulated; the loops are what happens with the articulation.
Articulation requirements for Tier 1 and Tier 2
Tier 1 (extensional) emits traces with observation → grouping → hypothesis → conclusion steps. No tests are run; the proposal is a
named rule, justified by structural commonality.
Tier 2 (intensional + test-and-learn) emits traces that include
test_design → test_application → test_result steps for both arms.
The trace is the full record of what was tested, against what
subset, with what outcome, before the conclusion was reached.
Replay-equivalence under articulation
The reasoning trace is part of the proposal's deterministic-replay contract (ADR-0057). Same corpus + same verdict index + same decomposer code → byte-identical reasoning traces. This means:
- An old proposal can be re-derived from scratch and the trace must match byte-for-byte
- Operator verdicts pinned to specific step indices remain valid across replays because the step indices themselves are stable
- A future decomposer change that breaks trace-byte-identity is detected by the existing replay-equivalence gate
The articulation substrate inherits CORE's existing determinism discipline — it does not introduce new non-determinism surface.
Decision — two tiers
Specify a math-domain corpus-decomposition mechanism that operates at two distinct rungs of engine-developed structural understanding.
Tier 1 — Extensional contemplation (rule-proposal)
Produce MathReaderRefusalShapeProposal records from the audit
corpus. Each proposal:
- Names a structural commonality across ≥2 refusal cases
- Names the candidate mechanism change that would resolve the commonality (matcher extension, injector sub-shape, vocabulary addition)
- Carries the audit-row evidence pointers (case IDs, refusal reasons, parsed_anchors)
- Routes to HITL review through the existing
teaching/proposals.pyratification flow - Is evidence-only: never auto-applied; ratification requires explicit operator action
This is the math-domain analog of cognition's
teaching/contemplation.py::contemplate(). It does not duplicate the
cognition mechanism; it sits alongside it, partitioned by
candidate.domain == "math" (the discriminator W2-C shipped in #351).
Tier 1 emits rules: "extend possession_verbs to include collected",
"add crayons to observed_counted_nouns", "widen DCS clause-split
exception for trailing 'of'-clauses." Each proposal materializes as
discrete code/pack changes after HITL approval.
Tier 2 — Intensional contemplation (inference)
Produce MathReaderInferenceProposal records that name a learned
structural equivalence across surface variations — not an explicit
rule, but a contemplated recognition that N sentences with surface
differences carry the same canonical proposition structure.
For example, Tier 2 would surface a proposal like:
inference_id: math.inferential.acquisition_to_initial_state
structural_claim:
"<ProperNoun> <verb-of-coming-to-possess> <count> <noun>"
is canonically equivalent to
"<ProperNoun> has <count> <noun>"
for the purpose of initial-state extraction.
evidence_pointers: (5+ refusal cases across `collected`, `acquired`,
`received`, `bought` — verb varies, structure is
invariant)
ratification_effect:
Add an *inferential bridge* in the reader/injector such that any
sentence matching the structural claim canonicalizes to the
reference form before extraction — without listing the specific
verbs.
wrong_zero_assertion:
Canonicalization preserves admissibility gates downstream; the
bridge only changes what reaches the gate, not whether the gate
fires. Multi-branch decision rule still refuses on disagreement
between bridged and unbridged extraction.
Tier 2 is qualitatively different from Tier 1: it operates on structural equivalence classes rather than on enumerable rules. It is what makes capability scale — instead of authoring N FrameClaim handlers for N verb categories, the engine recognizes the verb-category dimension itself as a structural axis and proposes a canonicalization bridge that handles the whole axis at once.
This is what the project thesis ("decoding ... capacity to find, comprehend, and rationalize") asks for. Tier 2 IS rationalization: recognizing canonical form within surface variation.
Tier 2 test-and-learn loop — two-arm confirmation
Tier 2 does not emit raw inferential claims. It emits claims paired with empirical test evidence, deterministically generated by the engine before HITL review. The test has two arms — both must hold for a proposal to surface to the operator:
Arm 1 — Negative check (held-out wrong=0): does the hypothesis admit cases the engine has never seen without raising wrong>0?
Arm 2 — Positive check (known-fact preservation): does the hypothesis change ANY currently-correct outcome? Cases that admit correctly today MUST continue to admit with the same answer under the bridged extraction. A hypothesis that would alter a known-good outcome — even to a different-but-defensible value — is rejected. The engine confirms against prior solutions, not just against unseen data.
This is the mechanism that lets the engine confirm against known facts. ADR-0057's replay-equivalence contract is the inherited substrate: the engine's inferential bridges must pass the same replay-equivalence check ratified teaching chains pass.
For each proposed MathReaderInferenceProposal:
- Hypothesis emission. The decomposer surfaces a structural equivalence class from the refusal corpus.
- Test-set partition.
- Held-out subset (e.g. 30% of the refusal corpus) — reserved from the evidence pointers that formed the hypothesis. Drives Arm 1.
- Known-good set — every case currently in the admitted-with- correct-answer state across the canonical lanes. Drives Arm 2.
- Inferential application. The hypothesis is applied to both
sets: bridged extraction → existing admissibility gates
(
_initial_admissible,roundtrip_admissible, multi-branch decision rule) → solver → verifier. - Two-arm outcome scoring.
- Arm 1 (held-out):
- Bridge admits cases at the same answer as ground-truth → positive evidence
- Bridge admits cases at a different answer than ground-truth → REJECTED INTERNALLY (would raise wrong>0)
- Bridge produces no admissions → neutral evidence
- Arm 2 (known-good):
- Every currently-correct case still correct under the bridge → PASS
- Any currently-correct case changes answer (even to a defensible value) → REJECTED INTERNALLY (would violate replay-equivalence)
- Both arms must PASS or be neutral. Either arm rejecting → proposal does not reach HITL.
- Arm 1 (held-out):
- Proposal-with-evidence emission. The proposal carries the structural claim AND both arms' test results AND the replay-equivalence hash AND the case-by-case verdict tables. HITL review verifies an empirically-tested hypothesis, not a bare claim.
The two-arm structure is what makes Tier 2 thesis-coherent. The engine isn't generating a hypothesis and asking the operator to bless it — it is decoding a structural pattern, testing against both unseen refusals and prior known-good outcomes, and presenting the operator with the test results as part of the proposal. The operator adjudicates the tests + the interpretation, not a bare claim.
This is also the wrong=0 safety net for Tier 2: any inferential bridge that produces a wrong admission in either arm is rejected internally before reaching HITL. The operator only ever sees proposals whose both arms confirm. Wrong>0 hazards cannot leak through Tier 2; nor can replay-equivalence violations of prior ratified work.
Tier 2 feedback-incorporation loop (Loop 3)
The two-arm self-test (Loop 1) plus HITL ratification (Loop 2) is not the full thesis loop. The project's anchor sequence — listen → comprehend → recall → think → articulate → learn from reviewed correction → replay deterministically — names a third loop: learn from the operator's verdict itself.
The engine must index every operator decision on a proposal and incorporate it into future decomposition. The verdict carries provenance:
ProposalVerdict:
proposal_id: str
proposal_shape_signature: str # structural feature hash
decision: Literal["ratify", "reject", "refine"]
reviewer: str
decided_at: str # ISO timestamp
reason: str | None # optional free-text rationale
refinement_payload: object | None # when decision == "refine"
When the decomposer surfaces a new candidate proposal, it consults the verdict index:
- Approved shape signatures — reinforce: similar future shapes surface with higher confidence
- Rejected shape signatures — de-prioritize or suppress: future shapes structurally similar to a known-rejection do not re-surface unless the structural-similarity threshold is breached (configured per category)
- Refined shape signatures — capture the gap between proposed and accepted; future decomposition tightens toward the refined form
Concrete example. The decomposer proposes:
"Extend possession verbs to include {collected, donated, gained}"
The operator ratifies {collected, donated} and rejects gained
with reason: "gained is a delta-of-attribute (weight, age), not an
acquisition-result; admitting it as initial possession risks wrong>0
on questions that ask total state."
Loop 3 indexes this as:
approved_shape: ACQUISITION_RESULT_VERB
members: {collected, donated}
signature: structural hash of (verb_class="acquisition", produces="possession")
rejected_shape: DELTA_OF_ATTRIBUTE_VERB
members: {gained}
signature: structural hash of (verb_class="delta", produces="state_change_not_possession")
rejection_reason: encoded as structural feature
Next time the corpus contains acquired (acquisition-result shape),
the decomposer surfaces it with higher confidence. If the corpus
contains lost (delta-of-attribute shape), the decomposer flags it
as structurally similar to a known-rejection and either de-prioritizes
or suppresses.
This is not gradient descent. It is structured feedback memory: deterministic, replayable, auditable. Operator verdicts have full provenance; the index is content-addressable; the decomposer's consultation of the index is itself recorded in the proposal's provenance trail.
Why Loop 3 is load-bearing for the thesis
Without Loop 3, every session starts from zero on proposal-shape quality. Operators repeatedly reject the same shapes because the engine never learns the rejection structure. Over time, operator review becomes mechanical: the same kinds of bad proposals keep arriving. That violates the "learn from reviewed correction" anchor.
With Loop 3, the engine's proposal quality compounds. Each ratified proposal teaches it what good shapes look like for this corpus; each rejection teaches it what shapes the operator considers wrong-class. Operator effort over time shifts from "reject the same five things again" toward "review novel shapes the engine hasn't seen before." That is the thesis loop running.
Tier 2 wave update — Loop 3 deliverables
The wave outline below adds dedicated PRs for Loop 3 mechanics (verdict schema, structural-feature indexing, decomposer consultation). These ship as part of Tier 2, after Loop 1 and Loop 2 are operational — the verdict signal needs proposals to exist before it has anything to index.
Why both tiers, not just Tier 2
Tier 1 is concrete and tractable today. Tier 2 requires Tier 1's infrastructure (evidence pipeline, HITL queue, ratification handlers) to land first. Cognition followed this pattern: explicit teaching chains shipped before the contemplation-decomposer that proposes them. The mechanism needs somewhere to land; that has to be built first.
Tier 1 also produces real lift along the way — each ratified proposal lifts GSM8K cases. Tier 2's lift is harder to predict and more architecturally ambitious. Shipping Tier 1 first means the project always has visible progress even if Tier 2 takes longer to design.
Why this is not already what ADR-0167 ships
ADR-0167 LexicalClaim (shipped) handles one-word ratifications:
the operator sees a lexicon_entry refusal and approves a pack
addition. The engine doesn't propose the word; the operator picks it.
FrameClaim (ADR-0168, queued) and CompositionClaim (ADR-0169, reserved) handle structural pattern ratifications but still require the operator to choose which pattern from the audit data.
ADR-0172 is the rung above: the engine reads the audit corpus, recognizes a structural pattern across N refusals, and proposes the pattern itself. The operator's role shifts from "find the pattern, choose the category, ratify the claim" to "review the engine's proposed pattern, accept or reject."
This is exactly the rung the cognition learning arc occupied when it graduated from per-claim ratification to corpus-decomposition proposal.
Why this matters for capability
Without ADR-0172, every math sub-shape PR costs:
- Operator time to read 21 refusal reasons and find the commonality
- Operator design judgment to scope the matcher/injector extension
- One focused PR per sub-shape, hand-authored
With ADR-0172, the engine surfaces candidate sub-shapes from the audit data. The operator reviews ~5 proposals per session instead of authoring ~5 sub-shape PRs. The proposals carry their own evidence, so review is structural verification rather than data archaeology.
This compounds: as the corpus grows (each refused case is a data point), the proposal mechanism gets more signal. Cognition saw this empirically — the learning arc accelerated after the first few ratifications because the corpus had more structural redundancy for the decomposer to recognize.
Provisional MathReaderRefusalShapeProposal shape
MathReaderRefusalShapeProposal:
proposal_id: str # deterministic hash of evidence + proposed change
domain: Literal["math"] # ADR-0167 W2-C discriminator
shape_category: ShapeCategory # which recognizer / refusal class
structural_commonality: str # human-readable description
evidence_pointers: tuple[MathReaderRefusalEvidence, ...] # ≥2 cases
proposed_change_kind: Literal[
"matcher_extension", # widen narrowness rule
"injector_sub_shape", # new emission pattern in injector
"vocabulary_addition", # lexicon/pack entry (subsumes LexicalClaim)
"frame_reclassification" # verb category change (subsumes FrameClaim)
]
proposed_change_payload: object # type-discriminated by kind
wrong_zero_assertion: str # explicit reasoning for why the change preserves wrong=0
replay_equivalence_hash: str # ADR-0057 contract
Each proposed_change_kind maps to a downstream ratification handler
(the W2-D-shaped artifacts ADR-0167 et seq. produce). The proposal
itself is purely descriptive — it does not modify anything.
Six open questions (must resolve in implementation ADR/PR)
-
Decomposition algorithm: how does the engine recognize structural commonality? Naive approach: group audit rows by
(refusal_reason, missing_operator)and emit one proposal per group above a minimum-evidence threshold (e.g. ≥3 cases). Deeper approach: cluster on extracted-anchor shape (e.g. all "proper noun + acquisition verb + integer + observed_noun" cases form one group regardless of which case_id they belong to).Recommendation: start naive. The cognition decomposer started that way too. Deeper clustering can ship later if the naive proposals don't carry enough signal.
-
Minimum evidence threshold: how many refusals must share a pattern before a proposal is emitted? Cognition uses 2+; that's probably right here too. Lower threshold = more noise; higher = misses real patterns.
-
De-duplication: how does the mechanism avoid re-proposing a pattern the operator already rejected? Cognition tracks rejected proposals; the same record needs to exist for math. Likely parallel to ADR-0167 W2-B's
claim_signaturemechanism. -
Wrong=0 evidence in the proposal: each proposal must carry an explicit
wrong_zero_assertion— a written claim about why the proposed change preserves the invariant. This is the structural analog of cognition's "polarity + evidence" fields. The operator's review focuses on validating this assertion. -
Cross-domain partition: per ADR-0167 W2-C, the contemplation queue is partitioned by domain. ADR-0172 emits only
math-domain proposals; the cognition contemplation continues to emit onlycognition-domain proposals. The wire is parallel, not unified. -
Frequency: when does the decomposer run? On every audit regeneration? On operator command? On a CLI lane (
core eval math-contemplation)? Cognition runs on-demand via CLI. That's the right starting pattern.
ADR-0166 three-question test
- Q1 — Capability: A corpus-decomposition mechanism for the math
domain, parallel to cognition's
teaching/contemplation.py. The capability is mechanism, not measurement — it does not directly lift any GSM8K case but it changes who decomposes the corpus (operator → engine). - Q2 — Lane: Existing
audit_brief_11.jsonis the input data. No new canonical lanes (ADR-0166 still gates). A new test lane intests/test_math_contemplation_decomposition.pywould pin proposal-emission for known refusal shapes. - Q3 — Invariant:
wrong == 0preserved by construction — proposals are evidence-only. The wrong=0 surface is the ratification handler (W2-D-shaped artifacts already exist for LexicalClaim; FrameClaim/CompositionClaim queued). ADR-0172 does not introduce new admission paths.
Sequencing — explicitly post-FrameClaim/CompositionClaim
ADR-0172 ships AFTER:
- ADR-0170 (injector contract widening) — provides the substrate
for sub-shape ratification handlers to emit
CandidateOperation. - ADR-0168 (FrameClaim handler) — provides one of the ratification targets a math proposal can emit toward.
- ADR-0169 (CompositionClaim handler — reserved) — second ratification target.
Without those substrates, ADR-0172 has nothing to propose against. The decomposer can identify patterns but can't route them to a ratification handler that knows how to materialize them. Sequencing matters here precisely the same way it mattered for cognition: the substrate (claim types, ratification handlers) had to land before the decomposer could pay off.
Implementation outline (subsequent PRs, not this one)
Substrate wave (ships first — required by Tiers 1 & 2 + Loop 3)
- W0 —
ReasoningTrace+ReasoningStepschemas inteaching/math_reasoning_trace.py, canonical-bytes serialization, content-addressable trace IDs, deterministic step-sequence ordering - W0.1 — Trace replay-equivalence test: same corpus + same verdict history → byte-identical trace
Tier 1 wave
- W1 — Schema:
teaching/math_contemplation_proposal.pywithMathReaderRefusalShapeProposaldataclass (carriesReasoningTrace)- canonical-bytes serialization
- W2 — Decomposer:
decompose_audit(audit_path) -> tuple[MathReaderRefusalShapeProposal, ...]with naive grouping by(refusal_reason, missing_operator)and min-2 evidence threshold - W3 — CLI lane:
core eval math-contemplationruns the decomposer and emits proposals toteaching/math_proposals/proposals.jsonl - W4 — Integration: workbench (ADR-0160) renders math proposals alongside cognition proposals; e2e test
Tier 2 wave (sequenced after Tier 1)
- W5 — Schema:
MathReaderInferenceProposaldataclass with evidence + both-arm test-result fields - W6 — Structural-equivalence-class recognizer: clusters refusal cases by parsed-anchor shape, surfaces candidate canonicalization bridges
- W7 — Two-arm test-and-learn loop: held-out subset (Arm 1) + known-good preservation (Arm 2); both must PASS or be neutral
- W8 — HITL integration: proposals reach the workbench only if both arms confirm; otherwise auto-rejected internally
- W9 — Inferential-bridge application path: how a ratified bridge materializes in the reader/injector (likely a canonicalization pre-pass before extraction)
Loop 3 wave (feedback-incorporation; sequenced after Tier 2 W8)
- W10 —
ProposalVerdictschema + persistent index inteaching/math_proposal_verdicts/index.jsonl - W11 — Structural-feature hash for proposal shapes; verdict indexer hooks the existing ratification flow so every approve/reject/refine writes to the index
- W12 — Decomposer consultation: pre-emission check against the verdict index; rejected-shape similarity threshold (configurable per category)
- W13 — Operator-facing rationale capture: when a verdict is "reject" or "refine", the operator's stated reason is encoded as a structural feature attached to the rejection signature
- W14 — Replay-equivalence test for Loop 3: same audit corpus + same verdict history → byte-identical future proposal stream
Each impl PR is small, focused, regression-tested. Cognition's parallel machinery is the template for Tier 1; Tier 2 is genuinely novel and will need its own design ADR before W5 ships.
What ADR-0172 does NOT do
- It does not propose any non-deterministic mechanism (decomposition is rule-based grouping, not learned classification).
- It does not add new eval lanes (ADR-0166 still gates).
- It does not weaken wrong=0 — proposals are evidence-only, never auto-applied.
- It does not change cognition's contemplation behavior (parallel
wire, partitioned by
domaindiscriminator). - It does not mandate that any specific proposal ship — operator retains full HITL authority.
Relationship to the Learning Arc
The Learning Arc milestone (2026-05-25) closed the cognition loop: refusal → audit → engine-decomposes → proposes → HITL → ratified corpus → next session admits.
ADR-0167 LexicalClaim partially closes the math loop: refusal → audit → engine-emits-evidence → operator-decomposes → operator-picks → HITL → ratified pack → next session admits.
ADR-0172 closes the gap: refusal → audit → engine-decomposes → proposes → HITL → ratified mechanism → next session admits.
That is the math-domain Learning Arc. The thesis test (thesis-decoding-not-generating) holds: the engine is decoding the structure of its own failures, not generating new content.
Cross-references
- ADR-0055 — four-tier inter-session memory (the substrate this proposal-mechanism extends)
- ADR-0056 — cognition contemplation loop (the direct template)
- ADR-0057 — replay-equivalence contract math proposals must inherit
- ADR-0167 — the parent evidence-wire ADR
- ADR-0167-FOLLOWUPS §1 — the sub-type handlers ADR-0172 proposes against
- ADR-0168 + ADR-0168.1 — FrameClaim scoping (one of the ratification targets)
- ADR-0170 — the substrate widening that unblocks injector-extension proposals
- [Memory: Learning Arc Milestone 2026-05-25] — the moment the cognition learning arc closed; this ADR is the math-domain analog
- [Thesis: decoding, not generating] — the principle this mechanism preserves