fix(intent): widen CORRECTION to catch fully-spoken `that is/was ...` forms

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
This commit is contained in:
Shay 2026-05-21 08:36:33 -07:00
parent 0dd30b86a7
commit c945b9a045
2 changed files with 39 additions and 3 deletions

View file

@ -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),
)

View file

@ -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: