From 7ef4ef45462c1800f11ee52a425aaa43265b13d6 Mon Sep 17 00:00:00 2001 From: Shay Date: Thu, 21 May 2026 08:26:01 -0700 Subject: [PATCH] fix(intent): widen RECALL trigger to accept ``recall`` alongside ``remember`` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The articulation breadth benchmark surfaced a RECALL intent gap: Before (bench output): RECALL UNKNOWN pack Pack-resident tokens — pack-grounded (en_core_cognition_v1): recall ... The probe prompt ``"Recall truth."`` classified as UNKNOWN and fell through to the ADR-0086 pack-resident-token surface — a graceful degradation, not a hard failure, but a real classifier gap. Root cause: ``generate/intent.py`` ``_RULES`` line 213 only matched the imperative ``remember``: (re.compile(r"remember\s+", re.IGNORECASE), IntentTag.RECALL) The verb ``recall`` — every bit as natural an imperative — was missing from the trigger pattern. ``"Remember truth."`` correctly routed to RECALL; ``"Recall truth."`` did not. Fix: widen the alternation to ``(?:remember|recall)\s+``. One-word change; ``re.match`` anchoring at the start of the prompt means the fix only catches the canonical imperative form, leaving downstream contexts untouched: * ``Does memory require recall?`` → VERIFICATION (unchanged; earlier rule on the aux-verb pattern fires first) * ``What is recall?`` → DEFINITION (unchanged; ``what\s+is\s+`` fires first) * ``Why does recall exist?`` → CAUSE (unchanged; ``why\s+`` fires first) * ``I recall.`` → UNKNOWN (unchanged; no trailing word after ``recall``, ``\s+`` doesn't match) * ``Please recall the truth.`` → UNKNOWN (unchanged — symmetric with ``Please remember the truth.`` since rules use ``pattern.match`` not ``pattern.search``) After (bench output): RECALL RECALL pack Truth is what is true. pack-grounded (en_core_cognition_v1). The articulation bench probe now routes correctly and produces a pack-grounded definition surface — the canonical RECALL output on a pack-resident lemma. Tests extended: ``tests/test_intent_subject_extraction.py:: test_recall_strips_articles`` is parametrized with four new ``Recall ...`` cases parallel to the existing ``Remember ...`` cases. A regression that re-narrows the trigger pattern fails the gate immediately. Verified: * pytest tests/test_intent_subject_extraction.py 7/7 pass * pytest tests/test_register_firing_diagnostic.py 3/3 pass * core test --suite smoke 67/67 pass * core test --suite runtime 19/19 pass * core bench --suite articulation → RECALL ✓ pack-grounded --- generate/intent.py | 2 +- tests/test_intent_subject_extraction.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/generate/intent.py b/generate/intent.py index 3a682642..f83d08f8 100644 --- a/generate/intent.py +++ b/generate/intent.py @@ -210,7 +210,7 @@ _RULES: tuple[tuple[re.Pattern[str], IntentTag], ...] = ( (re.compile(r"how\s+(?:do|can|should|would)\s+(?:I|we|you)\s+", re.IGNORECASE), IntentTag.PROCEDURE), (re.compile(r"(?:is|are|does|do|can|could|would|should|was|were|has|have|will)\s+.+\??\s*$", re.IGNORECASE), IntentTag.VERIFICATION), (re.compile(r"(?:no|that'?s\s+(?:not|wrong)|incorrect|actually|correction)", re.IGNORECASE), IntentTag.CORRECTION), - (re.compile(r"remember\s+", re.IGNORECASE), IntentTag.RECALL), + (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 beec39f3..1f347479 100644 --- a/tests/test_intent_subject_extraction.py +++ b/tests/test_intent_subject_extraction.py @@ -108,6 +108,16 @@ def test_verification_extracts_head_noun(prompt: str, expected_subject: str) -> ("Remember light", "light"), ("Remember the truth", "truth"), ("Remember a procedure", "procedure"), + # ``recall`` is a synonym imperative of ``remember`` and must + # route identically. The articulation breadth benchmark probe + # ``"Recall truth."`` was misclassified as UNKNOWN until the + # trigger pattern in ``_RULES`` was widened to ``(?:remember| + # recall)\s+`` — without this case the regression silently + # returns. + ("Recall light", "light"), + ("Recall the truth", "truth"), + ("Recall a procedure", "procedure"), + ("Recall truth.", "truth"), ], ) def test_recall_strips_articles(prompt: str, expected_subject: str) -> None: