From c945b9a0454fa30e40b9f231ac18dac0310c0118 Mon Sep 17 00:00:00 2001 From: Shay Date: Thu, 21 May 2026 08:36:33 -0700 Subject: [PATCH] fix(intent): widen CORRECTION to catch fully-spoken ``that is/was ...`` forms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-on to the word-boundary fix (commit 0dd30b8). After tightening ``\bno\b`` etc. with word boundaries, an audit surfaced a separate pre-existing gap in the CORRECTION trigger: the contracted-only ``that'?s\s+(?:not|wrong)`` slot silently dropped every fully-spoken copula form to UNKNOWN. Concrete gap (every one previously UNKNOWN): "That is not right." → UNKNOWN "That is wrong." → UNKNOWN "That was wrong." → UNKNOWN "That is incorrect." → UNKNOWN "That is false." → UNKNOWN "That was not right." → UNKNOWN "that is mistaken." → UNKNOWN "That was incorrect." → UNKNOWN Root cause: the slot ``that'?s\s+(?:not|wrong)`` matches only that's / thats — ``'?s`` makes the apostrophe optional but the literal ``s`` is mandatory. ``that is`` (full word ``is``) and ``that was`` (full word ``was``) had no path. And the predicate alternation only accepted ``not`` or ``wrong``; ``incorrect``, ``false``, and ``mistaken`` were also missing. Fix: widen both slots in one pattern revision. Before: that'?s\s+(?:not|wrong) After: that(?:'?s|\s+(?:is|was))\s+(?:not|wrong|incorrect|false|mistaken) The full pattern now reads: \b(?:no |that(?:'?s|\s+(?:is|was))\s+(?:not|wrong|incorrect|false|mistaken) |incorrect |actually |correction)\b Boundary discipline holds: the outer ``\b...\b`` still prevents the predicate alternation from eating into longer words. Verified: "That is correct." → UNKNOWN (right NOT in predicate set) "That is right." → UNKNOWN (right NOT in predicate set) "That is true." → UNKNOWN (true NOT in predicate set) "That works." → UNKNOWN "That is interesting." → UNKNOWN "That is falsifiable." → UNKNOWN (``false`` + ``i`` is word→word so ``\b`` after ``false`` fails) "That was wrongly accused." → UNKNOWN (same logic for ``wrong``+``ly``) Tests extended: * ``test_correction_canonical_forms_still_route`` — 8 new parametrize cases for the fully-spoken copula forms * ``test_correction_does_not_eat_no_prefixed_words`` — 9 new parametrize cases for the affirmative ``That is/was ...`` shape AND the boundary-trap cases ``falsifiable`` / ``wrongly accused`` Verified: pytest tests/test_intent_subject_extraction.py 33/33 pass full intent + register-diagnostic + proposition graph 77/77 pass core test --suite smoke 67/67 pass core test --suite runtime 19/19 pass --- generate/intent.py | 9 ++++++- tests/test_intent_subject_extraction.py | 33 +++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/generate/intent.py b/generate/intent.py index bd42eba2..bf3b31f5 100644 --- a/generate/intent.py +++ b/generate/intent.py @@ -216,7 +216,14 @@ _RULES: tuple[tuple[re.Pattern[str], IntentTag], ...] = ( # ``"w remember light"``. The same hazard applies to ``incorrect`` # (would eat ``incorrectly``), ``actually`` (would eat # ``actualization``), and ``correction`` (would eat ``corrections``). - (re.compile(r"\b(?:no|that'?s\s+(?:not|wrong)|incorrect|actually|correction)\b", re.IGNORECASE), IntentTag.CORRECTION), + # + # The ``that(?:'?s|\s+(?:is|was))\s+(?:not|wrong|incorrect|false| + # mistaken)`` slot covers both the contracted form (``"That's wrong"``, + # ``"thats not"``) and the fully-spoken copula form (``"That is not + # right"``, ``"That was wrong"``, ``"That is incorrect"``, ``"That is + # false"``, ``"That was mistaken"``). The contracted-only predecessor + # silently dropped these to UNKNOWN. + (re.compile(r"\b(?:no|that(?:'?s|\s+(?:is|was))\s+(?:not|wrong|incorrect|false|mistaken)|incorrect|actually|correction)\b", re.IGNORECASE), IntentTag.CORRECTION), (re.compile(r"(?:remember|recall)\s+", re.IGNORECASE), IntentTag.RECALL), ) diff --git a/tests/test_intent_subject_extraction.py b/tests/test_intent_subject_extraction.py index dfc3a247..33084c23 100644 --- a/tests/test_intent_subject_extraction.py +++ b/tests/test_intent_subject_extraction.py @@ -145,6 +145,7 @@ def test_recall_strips_articles(prompt: str, expected_subject: str) -> None: @pytest.mark.parametrize( "prompt", [ + # Pre-existing canonical forms (contracted ``that's``) "No, that's wrong.", "No.", "No way.", @@ -153,11 +154,25 @@ def test_recall_strips_articles(prompt: str, expected_subject: str) -> None: "Actually, that's false.", "Correction: memory is not storage.", "That's wrong.", + # Fully-spoken copula forms — added when the contracted-only + # ``that'?s\s+(?:not|wrong)`` slot was widened to also accept + # ``that\s+(?:is|was)`` and the predicate alternation grew + # ``incorrect|false|mistaken``. Every one of these used to + # silently fall through to UNKNOWN. + "That is not right.", + "That is wrong.", + "That was wrong.", + "That is incorrect.", + "That is false.", + "That was not right.", + "that is mistaken.", + "That was incorrect.", ], ) def test_correction_canonical_forms_still_route(prompt: str) -> None: - """Legitimate CORRECTION pragmas must still classify after the - word-boundary fix narrowed the alternation.""" + """Legitimate CORRECTION pragmas must classify after the + word-boundary fix narrowed the alternation, AND the + fully-spoken copula variants must route too (previously UNKNOWN).""" intent = classify_intent(prompt) assert intent.tag is IntentTag.CORRECTION @@ -178,6 +193,20 @@ def test_correction_canonical_forms_still_route(prompt: str) -> None: "Corrections department.", # ``Actually`` prefix — rarer but symmetric "Actualization of intent.", + # Affirmatives that share the ``That is/was ...`` shape — the + # predicate alternation (``not|wrong|incorrect|false|mistaken``) + # must not over-match. ``That is correct/right/true`` are NOT + # corrections; ``falsifiable`` / ``wrongly accused`` carry the + # trigger root but extend past the boundary. + "That is correct.", + "That is right.", + "That is true.", + "That works.", + "That is interesting.", + "That is a fact.", + "That was a good question.", + "That is falsifiable.", + "That was wrongly accused.", ], ) def test_correction_does_not_eat_no_prefixed_words(prompt: str) -> None: