core/evals/math_capability_axes
Shay d22608ddcb
feat(ADR-0163.D.4): question grammar extension — mass nouns, comparatives, pronoun-entity resolution (#310)
Three new question shapes extracted from the GSM8K train_sample
post-Phase-D refusal taxonomy:

- Pattern A — "How much MASS_NOUN does ENTITY VERB ..." with narrow
  whitelist (money, profit, interest, income, savings, cost, amount,
  total).  Extending the whitelist requires a separate ADR.

- Pattern B — "How many more UNIT does ENTITY VERB ..." (comparative).
  Structurally detected (regex + comparative_marker field) but
  emission is gated until the solver gains comparative semantics
  (D.5 follow-up).  Without solver-side handling, emission would
  return the entity's current total (off by the missing delta) and
  break wrong=0.

- Pattern C — "How many UNIT does PRONOUN VERB [to VERB2] ..." with
  a closed-set action-verb whitelist.

Pronoun-entity resolution (Pattern C):
- Pure, deterministic function _resolve_pronoun_entity
- Refuses on ambiguity: >1 distinct female/male name in problem text
  → no candidate emitted (better refuse than admit-with-wrong-entity)
- "they" / "it" outside scope — refuses
- Closed-set ~50/~50 female/male name whitelists sourced from
  GSM8K train_sample observation

Wrong=0 safety nets:
1. Regex narrowness (mass-noun whitelist, "more" anchor, closed verb set)
2. Pronoun resolver refuse-on-ambiguity
3. Pattern B emission gated until solver semantics catch up

CandidateUnknown.comparative_marker added with default False so
existing 200+ construction sites stay byte-identical.

Plumbing: extract_question_candidates / _filtered_question_choices /
parse_and_solve thread an optional problem_text through to the
pronoun resolver.  No solver, recognizer-registry, matcher,
candidate-graph wiring, proposal log, or eval-harness changes.

Validation (all green on this branch):
  pytest tests/test_adr_0163_d4_question_grammar.py            -> 45 passed
  pytest tests/test_adr_0163_d3_conditional_prefix.py          -> green
  pytest tests/test_math_candidate_parser.py                   -> green
  pytest tests/test_math_candidate_graph.py                    -> green
  pytest tests/test_candidate_graph_recognizer_wiring.py       -> green
  pytest tests/test_adr_0131_*.py                              -> green
                                  331 passed, 3 skipped
  python -m evals.math_capability_axes.G3_numerics.v1.runner   -> overall_pass=True
                                  solved=20 / wrong=0
  python -m evals.gsm8k_math.train_sample.v1.runner            -> correct=3
                                                                  refused=47
                                                                  wrong=0

GSM8K train_sample baseline:
  Pre-D.4 (D.3 base):     correct=3, refused=47, wrong=0
  Post-D.4 (this PR):     correct=3, refused=47, wrong=0

No lift on this base branch.  Cases that Pattern A admits at the
question level (e.g. 0001 "how much money does she make") still
refuse at the statement layer because the round-2 exemplar-corpus
recognizers (PR #309) are not on this base.  Refusal reasons
update from "no admissible candidate for question" to "no admissible
candidate for statement" / "no branch produced a solvable graph" —
expected.  The grammar machinery is structurally ready: when
stacked on PR #309, the projected lift to correct=8-13 should
manifest.

Per-pattern coverage on the 38 question refusals (post-Phase-D
question shape categorization):
  Pattern A — mass-noun ENTITY VERB:   ≥4 evidenced cases
                                       (0001, 0003, 0022, 0029)
  Pattern B — comparative quantifier:  ≥3 evidenced (0007, 0035, ...)
                                       — detection only, no emission
  Pattern C — pronoun + action verb:   ≥1 in-scope (0011)
                                       (0008 modal "be able to" + 0025
                                        joint-subject deferred to D.5)

Cross-references: ADR-0163 (#294), Phase D.3 (#308 — base), round-1
ratification (#304), round-2 ratification (#309 — required for the
projected lift), session recap (#305).
2026-05-26 16:19:37 -07:00
..
G1_verb_classes/v1
G2_comparatives fix(ADR-0131.G.2): rebase + mastery hardening — quarter/third fraction anchors, gate regex, boundary refusals (#196) 2026-05-23 19:28:09 -07:00
G3_numerics feat(ADR-0163.D.4): question grammar extension — mass nouns, comparatives, pronoun-entity resolution (#310) 2026-05-26 16:19:37 -07:00
G4_multi_clause
G5_aggregate feat(ADR-0131.G.5): aggregate answer composition — combined/together cues wired, axis lane 20/20, wrong==0 (#197) 2026-05-23 19:42:55 -07:00
S1_rate_events feat(ADR-0136.S.1): rate/event statement parsing — capacity + earnings shapes, axis lane 20/20, wrong==0, gsm8k-0014 admits (#201) 2026-05-23 20:36:01 -07:00
S3_compound_initial_mutation feat(ADR-0136.S.3): compound initial-mutation extractor — one shape, gsm8k-0010 barrier shift, wrong==0 (#207) 2026-05-23 21:58:55 -07:00
S4_novel_initial_form feat(ADR-0136.S.4): novel-initial-form parser extension + rescan v4 (#210) 2026-05-23 22:34:51 -07:00
__init__.py