# ADR-0131.G.5 — Aggregate Answer Composition **Status:** Accepted **Parent:** [ADR-0131.G — GSM8K Coverage Probe](ADR-0131.G-gsm8k-coverage-probe.md) **Date:** 2026-05-23 ## Context The aggregate-answer path — questions like "How many apples do they have altogether?" — was functionally complete before this ADR. The parser (`_Q_TOTAL_RE` in `generate/math_candidate_parser.py`) already emitted `Unknown(entity=None, unit=)` for aggregate cues, and the solver (`generate/math_solver.py`) already summed all terminal state entries matching the questioned unit when `entity is None`. What was missing: 1. **Vocabulary gap (now closed):** `"combined"` and `"together"` were absent from `_Q_TOTAL_RE`'s tail alternation, causing questions using those cues to be refused even when the solver would have produced the correct sum. 2. **No pinned lane:** no curated axis cases proved the 2-entity, 3-entity, and degenerate aggregate paths end-to-end through `parse_and_solve`. ## Decision ### Closed aggregate-cue vocabulary Exactly four cues are admitted: | Cue | Example tail | |-----|-------------| | `in total` | "How many apples do they have in total?" | | `altogether` | "How many apples do they have altogether?" | | `combined` | "How many apples do they have combined?" | | `together` | "How many apples do they have together?" | All four map to `entity=None` semantics — the solver sums all state entries whose unit matches the questioned unit, across all entities. ### Solver path (pre-existing) The `entity is None` branch in `_resolve_unknown` was not changed. It sums `v for (_, unit), v in state.items() if unit == unknown.unit`. This ADR extends the cue vocabulary and pins the lane, not the solver. ### Axis lane 20 curated cases at `evals/math_capability_axes/G5_aggregate/v1/cases.jsonl`: | Shape | Count | Purpose | |-------|-------|---------| | 2-entity sum, no operations | 4 | one case per cue | | 3-entity sum, no operations | 4 | one case per cue | | 2-entity sum with add/subtract op | 4 | mixed cues | | Single-entity degenerate | 4 | regression guard | | Refusal: outside closed cue | 4 | wrong==0 probe | Refusal cases use question forms outside the closed `_Q_TOTAL_RE` pattern (e.g., "How many apples does everyone have?", "What is the total number of coins?") to verify the parser correctly refuses paraphrases not in the closed cue set. ### Gate `wrong == 0` on every axis case. GSM8K `admitted_wrong == 0` preserved (no admission movement expected — all 50 sample cases still refuse at statement parsing; question-layer work cannot lift that). ## Deferred - **Implicit aggregation without a cue word:** "How many apples do Sam and Tom have?" requires coreference resolution (named-entity → pronoun-equivalent grouping). Out of scope for the closed-cue model. - **Rate-based aggregation:** "How many dollars did they earn in total?" where the unit derives from a rate operation. Requires rate-verb support in the statement parser. - **GSM8K admission lift:** all 50 sample cases fail at statement parsing (rate verbs, compound sentences, implicit entities). Question-layer cue extensions cannot move that number. ## Evidence - Axis runner: `evals/math_capability_axes/G5_aggregate/v1/runner.py` - Report: `evals/math_capability_axes/G5_aggregate/v1/report.json` - Tests: `tests/test_adr_0131_G5_aggregate.py` - B3 lane unchanged. - GSM8K `admitted_wrong == 0` preserved.