Phase D made statement-level admission consult the ratified
recognizer registry (PR #302) but the same wiring at the
question-admissibility point was left for follow-up. Post-Phase-B
round-2 ratification, 38 of 47 still-refused GSM8K train_sample
cases now refuse on QUESTIONS (vs 7 pre-ratification) — the
architectural bottleneck has migrated downstream.
The biggest single still-refused question shape is
``nested_question_target`` (11 of 38 cases): ``If X, how many Y
does Z have?`` style. The existing ``_Q_ENTITY_RE`` regex only
matches ``How many UNIT does ENTITY have`` without a conditional
prefix.
D.3 adds a deterministic, pure prefix-strip step that runs ONLY
when the bare parser returns no candidates:
_filtered_question_choices:
candidates = existing parser
if empty AND sentence starts with "If X, ":
strip the prefix, upper-case the first letter
re-run the existing parser on the suffix
Tests pin: prefix-strip correctness on the 5 brief-mandated case
shapes, no false admissions when the suffix is still unparseable,
non-question pass-through unchanged, idempotency, no input
mutation, real-GSM8K-question parameterised coverage.
Empirical reality (verified by re-running the train_sample lane):
the strip operation succeeds deterministically on every
nested_question_target case, but the resulting suffix still hits
OTHER parser limitations (``how much`` mass nouns instead of
``how many`` units, modal verbs like ``will be able to``, pronoun
entities, additional clause prefixes). D.3 alone produces ZERO
additional case-level lift on the current parser regex. D.3 is
necessary-but-not-sufficient; the next layer (extending the
question grammar to mass nouns + non-"have" verbs + pronoun
entity resolution) is required for the conditional-question
cases to compose into correct answers.
That layer is a separate ADR — it touches grammar surface, not
admission wiring. This PR ships ONLY the wiring extension.
Validation:
- 43 new + existing tests passed: tests/test_adr_0163_d3_*,
tests/test_math_candidate_graph,
tests/test_candidate_graph_recognizer_wiring
- 222 capability-axis tests passed / 2 pre-existing main
failures / 3 skipped — G1..G5 + S1 wrong=0 byte-identical
- 67 smoke passed
wrong=0 invariant preserved by construction: recovered candidates
flow through the same _question_admissible gate as direct
candidates; no new admission paths bypass the structural check.
Scope: extends one function in generate/math_candidate_graph.py.
Does not modify the parser regexes, the solver, or the recognizer
registry.