Commit graph

2 commits

Author SHA1 Message Date
Shay
feeb64818c feat(ADR-0126 P3+P4): graph assembly + decision rule + runner wiring
P3 — generate/math_candidate_graph.py:
  Branch enumeration over per-sentence candidate choices (Cartesian
  product, cap=64). Per-sentence ambiguity tiebreaker via most-grounded-
  slots-wins (transfer beats subtract when 'to Tom' grounds). Decision
  rule: 0 admissible -> refuse; 1 -> emit; >=2 same answer -> emit;
  >=2 different answers -> refuse (preserves wrong==0 on genuine
  ambiguity). End-to-end parse_and_solve(text) -> CandidateGraphResult.

  Question extractor added to math_candidate_parser.py (CandidateUnknown,
  total + entity question shapes mirroring math_parser).

  22 new tests. Permissive verbs ('bought', 'ate', 'bakes') now produce
  correct answers via the candidate-graph path; ambiguous 'gives to Tom'
  resolves to transfer reading (Tom gets the apples) deterministically.

P4 — evals/gsm8k_math/runner.py:
  New sibling function _score_one_candidate_graph(case) -> CaseOutcome.
  Identical shape to _score_one; swaps parse_problem for parse_and_solve;
  preserves verifier/realizer/expected-answer stages. Callers (e.g.
  PR #160's train_sample/v1/runner.py) substitute the new function in
  one line to evaluate the candidate-graph topology.

  9 new wiring tests. Three groups:
    - No regression: cases legacy solves, new also solves.
    - Lift: cases legacy refuses, new solves (the architectural payoff).
    - Wrong==0: out-of-grammar refuses, never wrong.

Regression: 714/714 existing math + runner tests still green.
ADR-0126 total: 74/74 tests green across P1+P2+P3+P4.
2026-05-23 06:36:13 -07:00
Shay
e8894f7a70 feat(ADR-0126 P2): candidate-emitting sentence parser + 17 tests
Sibling to math_parser.py — pure candidate-extraction functions that
emit list[CandidateOperation] per sentence without mutating any state.
State threading defers to P3 (per-branch graph assembly).

Topology change vs legacy:
  - No first-match-wins; every verb-kind regex runs independently.
  - Ambiguous verbs ('gives', 'returns') emit multiple candidates;
    P1's round-trip filter + P3's decision rule resolve.
  - Out-of-grammar sentences return [], NOT ParseError. Empty list
    is the deterministic 'no candidate' signal.

Permissive verb tables (imported from math_roundtrip.KIND_TO_VERBS)
mean past-tense and production verbs ('bought', 'ate', 'bakes')
that the legacy parser refused are now admissible — the round-trip
filter is the safety mechanism, not regex narrowness.

P2 scope (canonical Subject-verb-Value-Unit-[to-Target] shape only):
  - extract_initial_candidates(sentence) for 'X has N units'
  - extract_operation_candidates(sentence) for add/subtract/transfer

Out of scope (deferred to later sub-phases):
  - Pronoun resolution / unit inheritance (needs per-branch state)
  - Multiply / divide / rate / comparison (same machinery, more matchers)

Regression: existing math suite 701/701 green. Zero changes to
math_parser.py, math_solver.py, math_verifier.py, math_realizer.py.
2026-05-23 06:36:13 -07:00