docs(adr-0179-ex3): record second deferral trap — postmodifier adjectives (#460)
Track C of docs/handoff/PARALLEL-WORK-PLAN-2026-05-29.md asked for a tight
EX-3 multi-word-unit redo satisfying (a) "12 jumping jacks." -> "jumping
jacks", (b) "6 apples and 4 apples." -> two apples, (c) all GB-1/2/3 tests
green. The cleanest tight rule that satisfies all three —
(?<![\w.])(\d+(?:\.\d+)?)\s+([a-z]+\s+[a-z]+)(?=\s*[.?!,]|\s*$)
— was implemented and passed the four pinned test files. Full-suite
verification then surfaced a second trap the audit at
docs/handoff/AUDIT-ADR-0179-EX-RECONCILE.md did not anticipate:
postmodifier-adjective tails. "25 years old?" fires the tight rule and
produces unit "years old" rather than "years", regressing
test_adr_0176_ms1_question_target.py::TestQuestionQuantities::
test_extracts_quantity_stated_in_question and the "X years old" pattern
in tests/test_adr_0176_ms2_chain.py. The pattern is endemic in GSM8K
(cases 0006 and 0033 both use "X years old"); closing it would need a
second closed lexeme set ({old, tall, long, wide, deep, away, ago, ...})
which the brief judged too open-ended to enumerate responsibly.
Per the brief's escape hatch ("If no rule satisfies all of (a)-(c) without
a grammar template, write a note and ship no code — a refusal is fine")
this commit:
* updates extract.py's module docstring to name BOTH known traps
(connective-crossing AND postmodifier-adjective tails);
* adds tests/test_adr_0179_extract.py::TestEX3StillDeferred with two pins
asserting the postmodifier-adjective shape stays at unit "years" alone,
so no future redo silently re-introduces the regression;
* ships NO extractor code change — the regex remains exactly as on main.
Scope/safety:
* Files touched are within Track C's allowed set (extract.py + its test).
* Zero functional change: extract_quantities byte-identical to main.
* Serving lane untouched (chat/ does not import this module).
* Safe alongside GB-3b on compose.py / clauses.py.
This commit is contained in:
parent
b1416814ea
commit
d5c79e87f4
2 changed files with 71 additions and 5 deletions
|
|
@ -24,11 +24,28 @@ this module, so none of this can move the serving ``3/47/0``):
|
|||
sentence/text or before terminal punctuation) extracts with an empty unit so it
|
||||
stays available to the completeness check without inventing a unit lexeme.
|
||||
|
||||
EX-3 (multi-word units) is deliberately **not** integrated: the greedy lowercase
|
||||
unit span regresses GB-2's same-unit detection (``"6 apples and 4 apples"`` →
|
||||
unit ``"apples and"``) and does not cleanly recover real multi-word units from
|
||||
0024-class text (``"20 jumping jacks on Monday"`` → ``"jumping jacks on"``). See
|
||||
``docs/handoff/AUDIT-ADR-0179-EX-RECONCILE.md``.
|
||||
EX-3 (multi-word units) is deliberately **not** integrated. Two distinct traps
|
||||
defeat the tightest lookahead-anchored rule the brief admits:
|
||||
|
||||
1. **Connective-crossing** (in
|
||||
``docs/handoff/AUDIT-ADR-0179-EX-RECONCILE.md``). The greedy lowercase unit
|
||||
span regresses GB-2's same-unit detection (``"6 apples and 4 apples"`` → unit
|
||||
``"apples and"``) and does not cleanly recover real multi-word units from
|
||||
0024-class text (``"20 jumping jacks on Monday"`` → ``"jumping jacks on"``).
|
||||
2. **Postmodifier-adjective tails** (discovered during the Track C redo of
|
||||
``docs/handoff/PARALLEL-WORK-PLAN-2026-05-29.md``). Even a *tight*
|
||||
``digit + lc word + lc word + (?=clause-terminator)`` rule fires on
|
||||
``"25 years old?"`` and produces unit ``"years old"`` instead of
|
||||
``"years"`` — regressing
|
||||
``tests/test_adr_0176_ms1_question_target.py::TestQuestionQuantities::test_extracts_quantity_stated_in_question``.
|
||||
The pattern is endemic: GSM8K cases 0006/0033 and several MS2 chain tests use
|
||||
``"X years old"``. Closing it would need a second closed lexeme set (a stop
|
||||
list of measurement postmodifiers — ``old``, ``tall``, ``long``, ``wide``,
|
||||
``deep``, ``high``, ``away``, ``apart``, ``ago``, …) which the audit did not
|
||||
anticipate and which the Track C brief judged too open-ended to enumerate
|
||||
responsibly. ``TestEX3StillDeferred`` in
|
||||
``tests/test_adr_0179_extract.py`` pins this second trap so no future redo
|
||||
silently re-introduces it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
|
|||
|
|
@ -94,6 +94,55 @@ class TestNoRegression:
|
|||
assert _triples("It costs 0.75 dollars.") == [(0.75, "dollars", "0.75")]
|
||||
|
||||
|
||||
class TestEX3StillDeferred:
|
||||
"""Honest pin: EX-3 (multi-word units) remains deferred after the Track C redo.
|
||||
|
||||
The brief in ``docs/handoff/PARALLEL-WORK-PLAN-2026-05-29.md`` Track C asked
|
||||
for a *tight* rule satisfying:
|
||||
|
||||
* (a) ``"12 jumping jacks."`` → unit ``"jumping jacks"`` only where tight;
|
||||
* (b) ``"6 apples and 4 apples."`` → two ``apples`` quantities (not
|
||||
``"apples and"``);
|
||||
* (c) all GB-1/GB-2/GB-3 tests stay green;
|
||||
|
||||
plus the plan's global rule that *no currently-green test* be regressed.
|
||||
|
||||
A candidate rule —
|
||||
``(?<![\\w.])(\\d+(?:\\.\\d+)?)\\s+([a-z]+\\s+[a-z]+)(?=\\s*[.?!,]|\\s*$)``
|
||||
— satisfies (a)+(b)+(c) against the four listed test files but **regresses
|
||||
a different green test**: ``test_adr_0176_ms1_question_target.py
|
||||
::TestQuestionQuantities::test_extracts_quantity_stated_in_question``,
|
||||
which pins ``"25 years old?"`` → unit ``"years"``. The candidate rule fires
|
||||
on ``"25 years old"`` and produces unit ``"years old"`` — and the
|
||||
postmodifier-adjective tail (``"old"`` / ``"tall"`` / ``"long"`` / ``"wide"``
|
||||
/ ``"away"`` / ``"ago"`` / …) is endemic in GSM8K (cases 0006, 0033 and
|
||||
several MS2 chain tests all use ``"X years old"``).
|
||||
|
||||
The audit at ``docs/handoff/AUDIT-ADR-0179-EX-RECONCILE.md`` named the first
|
||||
trap (connective-crossing). This pin names the second one
|
||||
(postmodifier-adjective tails), so the deferred status of EX-3 is owned by
|
||||
*both* known traps and a future redo cannot silently land without addressing
|
||||
them.
|
||||
|
||||
These assertions hold against the *current* (no-EX-3) extractor. Any patch
|
||||
that introduces a multi-word-unit pass without also blocking postmodifier
|
||||
tails will flip them — that is the point.
|
||||
"""
|
||||
|
||||
def test_postmodifier_adjective_does_not_inflate_unit_to_two_words(self) -> None:
|
||||
# The trap. Naive tight EX-3 produces "years old"; the correct unit is
|
||||
# "years" alone.
|
||||
qs = extract_quantities("How old will the father be when she is 25 years old?")
|
||||
assert [(q.value, q.unit) for q in qs] == [(25.0, "years")]
|
||||
|
||||
def test_postmodifier_trap_present_for_full_sentence_form_too(self) -> None:
|
||||
# The same trap with a period instead of a question mark, paralleling
|
||||
# ``"Rachel is 12 years old."`` from GSM8K case 0033 / the MS2 chain tests.
|
||||
qs = extract_quantities("Rachel is 12 years old.")
|
||||
assert qs[0].value == 12.0
|
||||
assert qs[0].unit == "years"
|
||||
|
||||
|
||||
class TestRealCase0024StillBlocked:
|
||||
"""Honest pin: the EX-4 unit-list pattern does NOT recover real case 0024.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue