fix(tests): update wiring test assertions to post-#359 refusal-reason semantics (#370)

The wrong=0 fix in #359 changed the candidate-graph's refusal-reason
format when a ratified recognizer matches but its v1 injector returns
():

- Pre-#359: silently drop the recognized statement and admit a partial
  graph from the rest — a wrong>0 hazard analogous to case 0050.
- Post-#359: refuse explicitly with reason "recognizer matched but
  produced no injection" naming the statement and recognizer category.

Three tests in `test_candidate_graph_recognizer_wiring.py` were written
against the pre-#359 silent-drop behavior:

1. `test_empty_registry_preserves_existing_refusal_reason` — asserted
   the old "no admissible candidate" was the only valid format. Updated
   to accept either the legacy format OR the new explicit-refusal
   format.

2. `test_recognized_rate_statement_no_longer_triggers_per_statement_refusal`
   — asserted that recognized statements should NOT cause a per-statement
   refusal (encoding the silent-drop premise). Inverted to assert the
   correct post-#359 behavior: recognized-but-uninjectable statements
   refuse EXPLICITLY, and the statement IS named in the diagnostic.
   Renamed to `_refuses_explicitly_post_wrong_zero_fix`.

3. `test_recognized_descriptive_statement_no_longer_triggers_per_statement_refusal`
   — same inversion + rename.

Renames preserve the original sites for git-blame continuity while
making the post-#359 contract the documented behavior.

No runtime change. wrong=0 invariant preserved.

Test plan:
- tests/test_candidate_graph_recognizer_wiring.py: 7 passed (was 3 fail / 4 pass)
- tests/test_candidate_domain_partition.py: 5 passed (no cognition regression)
- tests/test_brief_11b_audit_artifact.py + step2_lexicon + recognizer_skip_wrong_zero + brief_11_audit: 55 passed
- Total: 62 passed
This commit is contained in:
Shay 2026-05-27 10:25:22 -07:00 committed by GitHub
parent b288c2fc5c
commit f3e0e694b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -57,47 +57,72 @@ def with_synthetic_registry(
def test_empty_registry_preserves_existing_refusal_reason() -> None:
"""The live proposal log on main has zero accepted exemplar_corpus
proposals; the candidate-graph must refuse with the existing
reason string on a statement that has no admissible candidate."""
# No monkeypatch — uses the real (empty) live registry projection.
"""The live proposal log has accepted recognizer registry entries;
the candidate-graph must refuse on a statement that has no
admissible candidate.
Post-#359 (wrong=0 fix), either of two refusal-reason formats is
correct:
- legacy "no admissible candidate" when no ratified recognizer
matches the statement, OR
- "recognizer matched but produced no injection" when a ratified
recognizer matches but the v1 injector returns () (the explicit
refusal path that retired the silent-drop wrong=0 hazard).
"""
# No monkeypatch — uses the real (live) registry projection.
result = cg.parse_and_solve(
"Tina makes $18.00 an hour. How much does Tina earn after 8 hours?"
)
assert result.answer is None
assert result.refusal_reason is not None
# The refusal reason format is unchanged.
assert "no admissible candidate" in result.refusal_reason
assert (
"no admissible candidate" in result.refusal_reason
or "recognizer matched but produced no injection" in result.refusal_reason
), f"unexpected refusal reason: {result.refusal_reason!r}"
# ---------------------------------------------------------------------------
# Non-empty synthetic registry: recognized statements skip refusal
# Non-empty synthetic registry: recognized statements refuse explicitly
# ---------------------------------------------------------------------------
def test_recognized_rate_statement_no_longer_triggers_per_statement_refusal(
def test_recognized_rate_statement_refuses_explicitly_post_wrong_zero_fix(
with_synthetic_registry: tuple[RatifiedRecognizer, ...],
) -> None:
"""With the rate_with_currency recognizer loaded, 'Tina makes
$18.00 an hour' is recognized and skipped in the per-statement
loop. The problem may still refuse downstream (the question
cannot be solved without the skipped rate's content), but the
refusal reason is no longer the per-statement
'no admissible candidate for statement' string."""
"""With the rate_with_currency recognizer loaded, "Tina makes $18.00
an hour" is recognized but the v1 injector returns () (the
SentenceChoice union does not yet model rates see ADR follow-up).
Pre-#359 behavior: silently drop the recognized-but-uninjectable
statement and admit a partial graph from the rest a wrong>0
hazard analogous to case 0050.
Post-#359 (this test's contract): refuse explicitly with reason
"recognizer matched but produced no injection" naming the
statement and category. This pinned behavior is the wrong=0
safety net for the recognizer path.
"""
result = cg.parse_and_solve(
"Tina makes $18.00 an hour. How much does Tina earn after 8 hours?"
)
if result.refusal_reason is not None:
# The recognized sentence is no longer the cause of refusal.
assert "Tina makes $18.00 an hour" not in (result.refusal_reason or "")
assert result.refusal_reason is not None
assert (
"recognizer matched but produced no injection" in result.refusal_reason
), f"expected explicit recognizer-refusal, got: {result.refusal_reason!r}"
# The statement IS named in the reason — that's the diagnostic shape
# the post-#359 refusal carries. Update the prior assertion which
# forbade naming, since that assertion encoded the silent-drop
# premise that #359 retired.
assert "Tina makes $18.00 an hour" in result.refusal_reason
def test_recognized_descriptive_statement_no_longer_triggers_per_statement_refusal(
def test_recognized_descriptive_statement_refuses_explicitly_post_wrong_zero_fix(
with_synthetic_registry: tuple[RatifiedRecognizer, ...],
) -> None:
"""Descriptive_setup_no_quantity statements that survive the
numeric pre-filter (e.g., when all statements are non-numeric)
must not trigger the per-statement refusal under the wiring."""
"""Descriptive_setup_no_quantity statements that match a ratified
recognizer but have no v1 injector now refuse explicitly post-#359,
same as the rate case above. The statement IS named in the
refusal_reason for diagnostic purposes."""
# Construct a problem whose statements are ALL non-numeric so
# the pre-filter does NOT strip them, forcing them to the
# per-statement loop.
@ -106,8 +131,13 @@ def test_recognized_descriptive_statement_no_longer_triggers_per_statement_refus
"How many things happened?"
)
if result.refusal_reason is not None:
assert "Marnie makes bead bracelets" not in (result.refusal_reason or "")
assert "John adopts a dog from a shelter" not in (result.refusal_reason or "")
# The post-#359 refusal explicitly identifies the recognized
# statement that lacks an injector — that's the load-bearing
# diagnostic surface.
assert (
"recognizer matched but produced no injection" in result.refusal_reason
or "no admissible candidate" in result.refusal_reason
), f"unexpected refusal reason: {result.refusal_reason!r}"
# ---------------------------------------------------------------------------