21 KiB
GSM8K Workstream A Gate A2a — unit partition / chunking primitive ratification
Date: 2026-06-17 Workstream: A (GSM8K typed-operation ladder) Gate: A2a — first partition/chunking injection slice (roadmap Gate A2 family) Status: Ratified for implementation (BEFORE any code changes) Scope lock: Fixed-size unit chunking with source-grounded total + chunk size, exact integer quotient, statement-only v1. One narrow recognizer-injector bridge mirroring Inc3 rate and Gate A1 comparative discipline.
Prerequisites (met on main @ e134e53c):
- PR #801: Inc3 evidence closure (
rate_with_currencyrecognized_no_injection= 0 on live runner). - PR #803/#805: Gate A1 multiplicative comparative ratification + implementation (
comparative_with_unitno-injection = 0). - PR #806: Post-Gate-A1 frontier microscope (
docs/analysis/gsm8k-post-gate-a1-frontier-microscope-2026-06-17.md). - PR #807: Lane-SHA hygiene (no GSM8K serving change).
Implementation branch (future): feat/gsm8k-workstream-a-gate-a2a-unit-partition-injection
Gate naming (fixed — do not conflate):
| Name | Meaning |
|---|---|
| Gate A2 (roadmap) | Partition / chunking family |
| Gate A2a (this doc) | Narrow v1: total measure → fixed-size chunks → chunk count |
| Gate A1b / Comparative-A2 | Additive comparative — not roadmap Gate A2 |
| PR-6d aggregate partition | Combine facts → split equally into N containers — distinct primitive, already landed off-serving |
1. What exact Gate A2a slice is selected?
Unit partition / chunking: a total continuous (or count-measure) quantity is split into fixed-size chunks with an explicit unit on the chunk size, producing an exact integer chunk count.
The deliverable is a recognizer-anchor injector for a ratified serving category ShapeCategory.UNIT_PARTITION (taxonomy label exists today; not a live _MATCHERS / _INJECTORS entry) that emits a typed partition operation when a closed v1 template matches and all slots are source-grounded. The slice closes the recognizer matched but produced no injection frontier for the one confirmed DCS misroute (dcs_misroute_unit_partition on case 0002), without widening discrete-count injection.
Confirmed code facts:
ShapeCategory.UNIT_PARTITIONand_is_unit_partitionexist inevals/refusal_taxonomy/shape_categories.py(taxonomy only).- EX-6 hyphen-bonded unit extraction (
25-foot) is landed ingenerate/derivation/extract.py. VALID_OPERATION_KINDSingenerate/math_problem_graph.pyhas nounit_partitiontoday; baredivideapply stores quotient under divisor unit, which is unsafe for feet÷feet→sections without aresult_unitcontract.- PR-6d
_Partitioningenerate/quantitative_comprehension.pyis aggregate-then-divide into N equal containers — different semantics from fixed chunk size (see §5).
This is first partition subfamily only — not full Gate A2 (inverse residual, yield questions, fractional partition, composition wall).
2. Lead exemplar
Case gsm8k-train-sample-v1-0002 (canonical seed pin in tests/test_gsm8k_post_gate_a1_frontier_microscope.py).
Full problem:
Jan buys 1000 feet of cable. She splits it up into 25-foot sections. She gives 1/4 of that to a friend. She then puts half of the rest in storage. How much does she keep on hand? → 15
Gate A2a v1 owns only the partition statement:
She splits it up into 25-foot sections.
Prior total (separate statement, must be in graph state before partition inject):
Jan buys 1000 feet of cable.
Gold partition step: 1000 ÷ 25 = 40 sections.
Live refusal (pinned proxy):
candidate_graph: recognizer matched but produced no injection for statement: 'She splits it up into 25-foot sections.' (category=discrete_count_statement)
Microscope classification: dcs_misroute_unit_partition → candidate primitive unit_partition → movement downstream_reclassification.
Exemplar seed (mis-route documentation): teaching/admissibility_exemplars/discrete_count_statement_v1.jsonl (dcs-v1-0021, train_case_id: gsm8k-train-sample-v1-0002).
Confuser spine: evals/gsm8k_math/confusers/v1/cases.jsonl confuser-v1-0007 — canonical 0002 misfire (pseudo-accumulation → 996 if partition read as Initial(25) only).
3. What is v1 in scope?
Closed template (statement-only)
| Requirement | Rule |
|---|---|
| Partition verb | split, cut, divide, separate, or ratified synonym ∈ DIVIDE_VERBS (mirror existing divide lexicon; no broadening in v1) |
| Chunk size | Hyphenated or explicit measure: <N>-<unit> (EX-6) or equivalent ratified span, e.g. 25-foot sections, 20-inch pieces |
| Total quantity | Prior statement in the same problem must ground (actor, total_unit) with value ≥ chunk size; partition stmt does not carry total |
| Unit compatibility | Dividend unit == chunk-size unit (same canonical unit); no implicit conversion |
| Quotient | total % chunk_size == 0; exact positive integer chunk count |
| Output | Emit typed unit_partition operation (§5) writing chunk count under a dimensionless or explicit count-noun unit (section, piece, … from counted noun in chunk phrase) |
| Actor | Same actor chain as prior total statement (v1: same-sentence subject or ratified narrow antecedent — no cross-sentence pronoun binding beyond existing session rules) |
| Injection target | Statements only — not question surfaces (mirror Gate A1) |
| DCS yield | Partition-bearing stmt must not match discrete_count_statement first / must not emit Initial(chunk_size, material_unit) misread |
Positive admission surfaces (v1)
| ID | Pattern | Notes |
|---|---|---|
| A01 | 1000 feet … splits into 25-foot sections |
Canonical 0002 pair |
| A02 | 800 inches … cuts into 20-inch pieces |
Paraphrase (confuser-v1-0005 first clause) |
| A04 | 12 feet rope cut into 4-foot sections → 3 |
Clean single-pair toy |
Expected v1 deliverable (implementation PR)
unit_partition(or ratified equivalent) on serving recognizer/injector path- Live bucket:
unit_partitionrecognized_no_injection = 0 (mirror Inc3/Gate A1 closure) - Case 0002 partition stmt: reclassification off DCS misroute (not guaranteed end-to-end solve)
4. What is explicitly out of v1?
| Case / surface | Deferral reason |
|---|---|
| 0002 full problem (¼ give, half of rest, "keep on hand") | Fraction + rest-state + question binding — composition follow-up; confuser-v1-0007 must stay refuse until separately ratified |
| 0007 inverse residual ("how many more boxes") | Question-parser slot; box capacity derivation — not stmt-level chunk partition |
| 0008 yield ("50 beads per bracelet, how many bracelets") | Production/yield question frame; needs inventory aggregate first |
| 0004 nested fraction partition | Half of the kids… — fractional subset-of-set, not fixed chunk size |
| 0025 peer-pick ("three friends pick the same amount") | Multiplicative peer partition question — not measure chunking |
| 0047 equal pack into N bags | Equal distribution into N containers (divisor = bag count), not M-unit chunks; taxonomy discrete_count_statement |
0030 2-hour drive |
Duration descriptor; taxonomy false positive via hyphen — must refuse |
| Non-integer quotient | e.g. 1000 feet → 30-foot sections — refuse (no floor/ceil) |
| Missing total | Partition stmt alone: She splits it into 25-foot sections — refuse |
| Missing chunk size | equal sections, into 40 sections (count without unit size) — refuse |
| "Split into N groups/boxes" | PR-6d / enumeration — divisor is group count, not chunk measure |
| Rate / per-unit | per hour, per kg, $2 for one cup — Inc3/Gate A1 closed buckets |
| Broad DCS composition wall | 14× dcs_composition_wall — forbidden without derivation_composer ratification |
| Affine equation / graph_planner | Out of Workstream A injector ladder |
| Cross-sentence pronoun reference | ADR-0138 — not proven safe |
| Remainder / subtract-after-partition | Needs gain/loss chaining beyond v1 injector |
Widening discrete-count injector to emit partition ops is explicitly forbidden (metric-inert / wrong=0 hazard per question-layer survey).
5. What typed primitive is required?
Ratified choice: unit_partition (new Operation.kind)
Not safe to reuse bare divide without extension because:
_apply_dividestores quotient under divisor unit (feet), not chunk count (sections).- Existing
divideencodes equal N-way split (groupsdimensionless + material unit tag) — different from fixed chunk size. - Candidate-graph parser does not emit serving
dividetoday; partition requires a new injector, not parser reuse alone.
Not the same as PR-6d _Partition: aggregate-then-divide into N equal containers with perquery pairing (generate/quantitative_comprehension.py). Gate A2a is total ÷ fixed chunk measure → count.
Minimal operation shape (conceptual — implementation must match or re-ratify)
Operation(
actor=<subject>,
kind="unit_partition",
operand=Quantity(value=<chunk_size>, unit=<chunk_unit>), # e.g. 25, foot
# implementation must also carry result_unit=<count_noun> or dimensionless count
)
Apply rule (wrong=0):
- Read
(actor, dividend_unit)from prior grounded state. - Require
dividend_unit == chunk_unit(after canonicalization). - Require
total % chunk_size == 0. - Write
(actor, result_unit) = total // chunk_sizewhereresult_unitis explicit count noun or dimensionless count — not dividend unit.
Stop/re-ratify trigger: If implementation discovers the substrate can only safely use divide + mandatory result_unit fields without a new kind, implementation PR must cite this doc and either (a) prove equivalence in tests or (b) open amendment PR before merge.
Rejected names: unitize (versor algebra collision); partition_count alone (ambiguous with PR-6d and equal-N split).
6. Grounding rules
| Slot | Rule |
|---|---|
| total_quantity | From prior statement quantity anchor; literal span grounded; actor key matches partition subject |
| chunk_size | From partition statement hyphenated or explicit measure token; literal substring in matched_*_token fields |
| chunk_unit | Canonical unit from EX-6 / _canonicalize_unit; must match total unit |
| partition_verb | Literal verb ∈ ratified divide verb set |
| counted_noun | Optional: sections, pieces — drives result_unit label |
| source_span | Full partition statement byte-identical to input |
| No inferred totals | Partition stmt without prior total state → injector returns () or downstream refuse |
| No implicit conversion | feet vs inches → refuse unless conversion ratified elsewhere |
| No LLM / heuristic fill | Every slot maps to a literal substring (Inc2/Gate A1 doctrine) |
Matcher category and parsed_anchors (implementation PR)
| Anchor key | Semantics |
|---|---|
actor_token |
Partition statement subject |
chunk_size_token |
Numeric chunk magnitude (e.g. 25) |
chunk_unit_token |
Unit phrase (e.g. foot) |
counted_noun_token |
Plural noun after chunk phrase if present (sections) |
partition_verb_token |
Matched split/cut verb |
source_span |
Full statement |
7. wrong=0 guard
| Condition | Behavior |
|---|---|
| Ambiguous partition (two chunk sizes, competing interpretations) | Refuse — no injection |
| Incompatible units (feet vs inches) | Refuse |
Non-exact division (total % chunk_size != 0) |
Refuse at solve/verify — never floor |
| Missing total or missing chunk size | Refuse — injector () |
| Partition stmt matches DCS only | Refuse mis-injection — must not emit Initial(N, material_unit) |
| Multiple partition cues in one sentence | Refuse |
| Downstream composition (fractions, rest, question) | Refuse on serving until composition ratified — partial graph must not verify as full gold |
| Numeric answer without verifier | Forbidden — math_verifier.verify must pass before correct (evals/gsm8k_math/runner.py) |
Architectural boundaries (INV-30 / INV-31):
- No
determine(), noanswer=False, noFrameVerdict, no CLOSE interaction on this path. - No
core.reliability_gateas substitute for verifier replay on serving.
Serving path: parse_and_solve → solve → math_verifier.verify → gold compare; wrong == 0 hard.
8. Confusers future implementation must prove
Hard refuses (30+ structural — minimum bar in implementation PR)
| ID | Surface class | Example | Expected |
|---|---|---|---|
| C01–C02 | Non-integer chunk | 1000 feet → 7-foot / 2.5-meter |
refuse |
| C03–C06 | Missing total or chunk | stmt-only 25-foot sections; equal sections |
refuse |
| C07 | Unit mismatch | 1000 feet → 25-inch sections |
refuse |
| C08 | Discrete packing | 48 cookies → boxes of 6 |
refuse |
| C09–C11 | N groups vs M-unit chunks | equally into 4 bags; Half of the kids |
refuse |
| C12 | Inverse residual (0007) | how many more boxes |
refuse |
| C13 | Yield (0008) | 50 beads per bracelet, how many… |
refuse |
| C14–C16 | Fraction/rest follow-up | full 0002; gives 1/4; subtract sections |
refuse |
| C17 | Non-exact total | 999 feet → 25-foot |
refuse |
| C18–C20 | Rate/per-unit | $18/hour, $20 per kg, $2 per cup |
refuse |
| C21–C22 | Duration false positive | 2-hour drive; 10 oysters in 5 minutes |
refuse |
| C23–C26 | DCS / composition wall | routes, multi-entity lists, comparatives, fraction-of-prior | refuse |
| C27 | Pseudo-accumulation twin | ribbon 800 + cut + give ¼ | refuse |
| C28 | Additive comparative (0016) | 2 more than 5 miles |
refuse |
| C29–C30 | Peer-pick / product each | 0025 question; 24 erasers each |
refuse |
Critical discrimination pairs:
1000 feet split into 25-foot sections(admit partition inject) vs stmt-only25-foot sections(refuse)1000 feet split into 25 sections(count divisor, no unit size) — refuse1000 feet split into bags(missing chunk measure) — refuse
Admission tests (5 minimum)
| ID | Proves |
|---|---|
| A01–A02 | Canonical inject on 0002 / ribbon paraphrase |
| A03 | Pair consistency: paired total+chunk vs stmt-only |
| A05 | Category unit_partition, not discrete_count_statement |
Graduation rule (ADR-0163-F2): Confusers graduate to solve only when a general composition mechanism is ratified and proven — not by regex widening.
9. Partition-adjacent cases (frontier evidence)
| case_id | Relevance | v1 |
|---|---|---|
| 0002 | Lead — dcs_misroute_unit_partition |
IN (partition stmt inject) |
| 0007 | Inverse box residual — question layer | OUT |
| 0008 | Beads-per-bracelet yield — question + aggregate | OUT |
| 0004 | Nested fraction subset | OUT |
| 0025 | Peer-pick multiplication question | OUT |
| 0047 | Equal pack into 4 bags — container count divisor | OUT |
Only 0002 has confirmed unit_partition DCS misroute on live serving (recognized_no_injection subfamily count = 1).
10. Expected metric movement
Live ephemeral runner (main @ e134e53c): 6 correct / 44 refused / 0 wrong; rate_with_currency and comparative_with_unit no-injection = 0.
Expected after Gate A2a implementation:
| Metric | Expected |
|---|---|
wrong |
0 (hard) |
correct |
≥ 6 (monotonic); not guaranteed lift |
refused |
≤ 44 (monotonic) |
| 0002 | Reclassification — partition stmt moves off DCS no-injection; full 15 solve still refused until composition ratified |
discrete_count_statement no-injection |
Likely −1 (19→18) |
unit_partition no-injection |
Target 0 on live runner |
Pinned report.json |
Unchanged unless separate rebaseline PR |
Do not claim aggregate correct-count lift in implementation PR body without per-case ephemeral evidence.
11. Explicit non-goals
- No implementation in this ratification PR (docs only).
- No
report.jsonrebaseline. - No additive comparative (Gate A1b / Comparative-A2).
- No Inc4 rate denom-state.
- No broad DCS widening /
derivation_composerwithout separate ratification. - No affine equation frame /
graph_planner.pychanges. - No sealed-lane /
scripts/verify_lane_shas.pypin movement (unless accidental — revert). - No corpus / pack / policy / identity mutation.
- No
determine()/FrameVerdict/ CLOSE / idle consolidation (INV-30 / INV-31). - No
answer=Falseon open-world paths. - No LLM / stochastic generation.
- No promotion of taxonomy
_HYPHENATED_UNIT_REalone into matcher (0030 hazard). - No conflation with PR-6d aggregate-partition template.
- No full Gate A2 family in one PR.
12. Implementation touch list (future PR — not this PR)
| File | Change |
|---|---|
generate/math_problem_graph.py |
Add unit_partition to VALID_OPERATION_KINDS (if new kind) |
generate/math_solver.py |
_apply_unit_partition with result_unit contract |
generate/recognizer_match.py |
_match_unit_partition, _MATCHERS registration |
generate/recognizer_anchor_inject.py |
inject_unit_partition, _INJECTORS; DCS yield guard |
generate/math_verifier.py / math_roundtrip.py |
Roundtrip + verify hooks for new kind |
teaching/admissibility_exemplars/ |
unit_partition_v1.jsonl (Phase C corridor) |
tests/test_recognizer_unit_partition_inject.py |
new — ≥15 unit tests |
tests/test_math_candidate_graph_unit_partition_injection.py |
new — ≥8 graph tests |
tests/test_gsm8k_frontier_report.py |
live unit_partition bucket closure |
tests/test_candidate_graph_recognizer_wiring.py |
wrong=0 replay |
tests/test_candidate_graph_completeness_guard.py |
pseudo-accumulation regressions |
tests/test_adr_0163_f2_confusers.py |
confuser-v1-0005/0007/0008 |
Read-only unless proven necessary: generate/derivation/* (off-serving), core/reliability_gate/*, graph_planner.py.
13. Validation obligations (implementation PR — not this PR)
.venv/bin/python -m pytest tests/test_recognizer_unit_partition_inject.py -q
.venv/bin/python -m pytest tests/test_math_candidate_graph_unit_partition_injection.py -q
.venv/bin/python -m pytest tests/test_gsm8k_frontier_report.py -q
.venv/bin/python -m pytest tests/test_gsm8k_post_gate_a1_frontier_microscope.py -q
.venv/bin/python -m pytest tests/test_candidate_graph_recognizer_wiring.py -q
.venv/bin/python -m pytest tests/test_candidate_graph_completeness_guard.py -q
.venv/bin/python -m pytest tests/test_adr_0163_f2_confusers.py -q
.venv/bin/python -m pytest tests/test_adr_0175_phase2_practice_lane.py -q
.venv/bin/python -m pytest tests/test_architectural_invariants.py -q -k "INV_30 or INV_31"
.venv/bin/python scripts/verify_lane_shas.py
.venv/bin/core test --suite smoke -q
# Ephemeral only — do not commit unless ratified:
# build_report(cases) → wrong==0
14. Validation obligations (this ratification PR — docs only)
git diff --check origin/main...HEAD
git diff --name-status origin/main...HEAD
Expected: single added file docs/analysis/gsm8k-workstream-a-gate-a2a-unit-partition-ratification-2026-06-17.md.
15. Open risks (honest)
| Risk | Severity | Mitigation |
|---|---|---|
Over-recognition on any \d+-(foot|hour|…) |
Critical | Closed verb+chunk template; 0030/0014 confusers; refuse duration |
| DCS wins match race on 0002 | High | Explicit DCS yield guard + matcher priority tests |
Bare divide quotient unit bug |
Critical | Ratify unit_partition + result_unit; never store 40 under feet |
| PR-6d conflation | High | Refuse into N bags / perquery shapes in v1 |
| Pseudo-accumulation 996 misfire | Critical | confuser-v1-0007 green; completeness guard |
| Claiming 0002 full solve | High | Doc + tests: injector closure only |
| Composition pressure (fractions/rest) | Medium | Explicit out-of-v1; separate ratification |
| No guaranteed correct lift | Low (expected) | PR body documents reclassification only |
result_unit noun ambiguity (sections vs dimensionless) |
Medium | Resolve in implementation tests; amend if dual |
16. Arena reconciliation summary
| Agent | Key finding |
|---|---|
| Partition substrate mapper | Bare divide unsafe; PR-6d distinct; ratify new unit_partition kind with result_unit; EX-6 + taxonomy landed, injector missing |
| Frontier evidence auditor | 0002 lead exemplar confirmed; 0007/0008/0004/0025/0047 OUT of v1; reclassification-only metric |
| Over-recognition adversary | 30 hard refuses + 5 admission tests; duration/rate/DCS wall highest risk |
| Truth-boundary auditor | INV-30/31; verifier before correct; no determine/FrameVerdict/CLOSE |
| Test-plan auditor | Inc2/Gate A1 ladder mirror; 15+8 unit/graph tests; frontier bucket closure; this PR docs-only |
This ratification closes the docs-first gate. Implementation may proceed only after this document merges to main.