Rank 5 on the docket, and the first item this arc whose expected outcome is LOSING capability. conservative_floor is a one-sided Wilson lower bound, and Wilson assumes independent trials. CORE's pipeline is deterministic, so replaying an identical case is not a second trial — it is the same trial with a guaranteed outcome. The deduction sealer folded the raw practice corpus and counted every replay, so bands recorded 720/720 committed whether that was 720 decisions or 28 decisions seen 26 times each. conservative_floor(720,720)=0.990868 cleared theta_SERVE=0.99; conservative_floor(28,28)=0.808413 is not close. RESULT: 25 licensed bands -> 4. Twenty-one demoted. Survivors: en_conditional_chain, en_disjunctive, en_verb_fact, en_verb_universal — the only bands whose corpus holds enough independent cases. What the demotion did and did not do, stated exactly. NO ANSWER CHANGED. No answer became wrong. `wrong` stayed 0 across all 25 bands. The engine is exactly as correct as it was. What changed is the CLAIM attached to the answer: 21 bands now serve the same sound conclusion prefixed with "(reasoned, but I haven't yet earned a verified track record on arguments of this shape)". The reasoning did not get worse; the boast did. The producer is hardened, which is the half that stops this recurring. seal_ledger now refuses outright — assert_sealed_evidence_distinct compares the ledger about to be written against the corpus's distinct-case count and raises BEFORE anything is written. The curriculum sealer has carried this guarantee since ADR-0264 R9; the deduction sealer had no equivalent, which is exactly how the exposure arose. Catching it at seal time rather than in an audit matters: an audit finds a padded ledger after it is committed, trusted, and gating a live flag, and unwinding that then needs a ruling — it needed one. A HOLLOW GUARD OF MY OWN, caught by sabotage rather than trusted. The first version compared all_gold_problems() with distinct_gold_problems() — two functions that agree with each other by construction, and neither of which is what build_ledger folds. Reverting build_ledger to the raw corpus walked straight past it AND re-sealed the inflated artifact. Same defect class the ledger already suffered: a guard checking something adjacent to the thing it protects. It now takes the BUILT LEDGER as input, because the invariant is about the artifact. Re-running the identical attack: refused, wrote nothing. Pinned as test_sealer_refuses_a_ledger_that_counts_replays. A SECOND MISS, caught by the gate rather than by me. My blast-radius scan grepped for deduction_serve_license / deduction_serve_ledger / 720 and found nine files. It missed tests/test_construction_inventory.py, which asserts deduction SURFACE STRINGS and never mentions the ledger — so a pattern search over the wrong noun could not see it. The full gate found it. Recorded because "I scanned for the blast radius" is worth exactly as much as the scan's key, and mine was the wrong key. That test is the #138 fabrication defect pin, and it is updated WITHOUT being weakened. Both surfaces now carry the disclosure prefix, and the fabrication survives untouched — which is a sharper finding than before: a disclosure prefix looks like it might cover the hazard, and it does not. The user is still told they said "furthermore". Exact-match assertions kept on the full strings so neither the fabrication nor the licence state can drift unnoticed, plus an isolating assertion that the only difference between clean and leaked recitals is the fabricated premise. Four records corrected in the same PR, each of which would otherwise have gone on asserting capability that no longer exists: - CAPABILITY_LEDGERS' manifest note ("25 bands at 720/720 wrong=0") - the published CLAIMS.md claim — caught immediately by G-22's own pin, which is that fix earning its keep three days after landing - test_volume_honesty.py's module docstring, which said the exposure was "NOT silently repaired ... changing it is Shay's ratification, not a test's". R-13 IS that ratification, so the line went stale by being acted on - the exposure inventory's framing: it now pins the repair, not the gap test_volume_honesty's sealed-vs-producer assertion is strictly stronger than it was. It compared sealed committed counts to the producer's committed counts and both were the inflated 720 — the assertion held perfectly while the number it agreed on was the wrong number. Two records agreeing is not evidence that either is right. It now pins committed == distinct, plus a non-vacuity check. Nine tests encoded the unearned expectation and were updated to the honest one. Closes G-19 and H-1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
521 lines
22 KiB
Python
521 lines
22 KiB
Python
"""Deduction-serve arc, Phase 1 — DEDUCTION composer tests.
|
|
|
|
The contract these tests pin:
|
|
|
|
- ``deduction_serving_enabled`` is ON by default since its ratification
|
|
(2026-07-24); flag-OFF ``chat()`` output remains byte-identical to
|
|
pre-arc behavior for argument-shaped text (the pack-token-gloss
|
|
fallback, unchanged), which is what keeps rollback a one-line, no-residue
|
|
operation.
|
|
- Flag-on: a sentence-initial "therefore" conclusion clause commits the
|
|
turn to ``chat/deduction_surface.py``; every committed turn returns a
|
|
surface (never a silent fall-through to a different composer).
|
|
- The full propositional comprehension gold corpus (Band v1) decides
|
|
correctly end-to-end through the real ``ChatRuntime.chat()`` path,
|
|
across multiple turns in one session (wrong=0).
|
|
- Out-of-band shapes (categorical/syllogism, multi-word English
|
|
propositions) are honestly declined, not silently misrouted.
|
|
- Non-argument-shaped text is untouched regardless of the flag.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from chat.deduction_surface import (
|
|
_UNVERIFIED_SHAPE_DISCLOSURE,
|
|
deduction_grounded_surface,
|
|
looks_like_deductive_argument,
|
|
)
|
|
from chat.runtime import ChatRuntime
|
|
from core.config import RuntimeConfig
|
|
from evals.propositional_logic.runner import _load_cases
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pure-function contract — looks_like_deductive_argument
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_looks_like_deductive_argument_true_for_therefore_conclusion() -> None:
|
|
assert looks_like_deductive_argument("If p then q. p. Therefore q.")
|
|
assert looks_like_deductive_argument("p or q. Not p. Therefore q.")
|
|
assert looks_like_deductive_argument(
|
|
"All mammals are animals. All whales are mammals. "
|
|
"Therefore all whales are animals."
|
|
)
|
|
|
|
|
|
def test_looks_like_deductive_argument_requires_sentence_initial_therefore() -> None:
|
|
""""therefore" mid-clause (not its own conclusion clause) does not commit
|
|
the turn — matches the reader's own per-clause discipline."""
|
|
assert not looks_like_deductive_argument("What is light therefore darkness")
|
|
assert not looks_like_deductive_argument("What is light?")
|
|
assert not looks_like_deductive_argument("")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pure-function contract — deduction_grounded_surface
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_non_argument_text_returns_none() -> None:
|
|
assert deduction_grounded_surface("What is light?") is None
|
|
assert deduction_grounded_surface("Why does parent exist?") is None
|
|
|
|
|
|
def test_entailed_case_renders_entailment() -> None:
|
|
surface = deduction_grounded_surface("If p then q. p. Therefore q.")
|
|
assert surface is not None
|
|
assert "entail: q" in surface
|
|
|
|
|
|
def test_refuted_case_renders_refutation() -> None:
|
|
surface = deduction_grounded_surface(
|
|
"If p then q. Not q. Therefore not p."
|
|
)
|
|
assert surface is not None
|
|
assert "entail" in surface
|
|
|
|
|
|
def test_unknown_case_renders_honest_indeterminacy() -> None:
|
|
surface = deduction_grounded_surface("p or q. Therefore p.")
|
|
assert surface is not None
|
|
assert "don't settle" in surface
|
|
|
|
|
|
def test_inconsistent_premises_renders_typed_decline() -> None:
|
|
surface = deduction_grounded_surface("p. Not p. Therefore q.")
|
|
assert surface is not None
|
|
assert "inconsistent" in surface
|
|
|
|
|
|
def test_categorical_argument_is_decided_band_v1b() -> None:
|
|
"""A valid categorical syllogism is now DECIDED (Band v1b, Phase 4) — the
|
|
production categorical decider serves a validity verdict, no longer a
|
|
decline."""
|
|
surface = deduction_grounded_surface(
|
|
"All mammals are animals. All whales are mammals. "
|
|
"Therefore all whales are animals."
|
|
)
|
|
assert surface is not None
|
|
assert "valid" in surface
|
|
assert "follows" in surface
|
|
|
|
|
|
def test_invalid_categorical_argument_is_rejected() -> None:
|
|
"""An invalid syllogism (undistributed middle) is decided INVALID, not
|
|
declined and not falsely served as valid (wrong=0 on the categorical band)."""
|
|
surface = deduction_grounded_surface(
|
|
"All cats are animals. All dogs are animals. Therefore all dogs are cats."
|
|
)
|
|
assert surface is not None
|
|
assert "doesn't follow" in surface
|
|
|
|
|
|
def test_multiword_conditional_is_decided_band_v2_en() -> None:
|
|
"""Natural-English multi-word propositions — Band v1's documented boundary
|
|
case (this exact text used to surface ``reserved_word_in_np``) — are now
|
|
DECIDED by the English-clause band (ADR-0257), authoritatively (the
|
|
``en_conditional_single`` band holds an earned SERVE license), with the
|
|
verdict rendered over the user's own clauses."""
|
|
surface = deduction_grounded_surface(
|
|
"If it rains then the ground is wet. It rains. "
|
|
"Therefore the ground is wet."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: the ground is wet" in surface
|
|
# R-13 (2026-07-28): this band was DEMOTED by the distinct-evidence re-count
|
|
# — en_conditional_single (474 distinct of 720 committed). The shape is still DECIDED, which is what this
|
|
# test is actually about; what changed is the claim attached to the answer.
|
|
# The engine now discloses that it has not earned a track record on this
|
|
# shape instead of answering authoritatively. Asserting the old behaviour
|
|
# here would re-assert a licence the evidence never supported.
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
|
|
|
|
def test_contraposition_is_decided_band_v2_en() -> None:
|
|
"""The other documented Band v1 boundary case (nested negation in a
|
|
conditional conclusion) — now decided: contraposition is ENTAILED."""
|
|
surface = deduction_grounded_surface("If p then q. Therefore if not q then not p.")
|
|
assert surface is not None
|
|
assert "Your premises entail: if not q then not p" in surface
|
|
|
|
|
|
def test_english_modus_tollens_via_copular_negation() -> None:
|
|
surface = deduction_grounded_surface(
|
|
"If the alarm is armed then the light is on. The light is not on. "
|
|
"Therefore the alarm is not armed."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: the alarm is not armed" in surface
|
|
|
|
|
|
def test_english_unknown_is_scoped_to_the_opaque_reading() -> None:
|
|
"""UNKNOWN is the one verdict that is NOT substitution-closed (internal
|
|
clause structure this band did not read could settle it), so its surface
|
|
must scope the claim to the opaque reading explicitly (ADR-0257 §3)."""
|
|
surface = deduction_grounded_surface(
|
|
"If it rains then the ground is wet. The ground is wet. Therefore it rains."
|
|
)
|
|
assert surface is not None
|
|
assert "Reading each clause as one indivisible claim" in surface
|
|
assert "don't settle" in surface
|
|
|
|
|
|
def test_english_unearned_band_would_be_hedged() -> None:
|
|
"""The en_* bands ride the SAME earned-license gate: strip the license and
|
|
the same sound answer is served DISCLOSED, never authoritatively."""
|
|
surface = deduction_grounded_surface(
|
|
"If it rains then the ground is wet. It rains. Therefore the ground is wet.",
|
|
license_lookup=lambda band: None,
|
|
)
|
|
assert surface is not None
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
assert "Your premises entail: the ground is wet" in surface
|
|
|
|
|
|
def test_english_reader_refusal_keeps_prior_honest_surface() -> None:
|
|
"""When the English band ALSO cannot read the argument, the pre-existing
|
|
honest surfaces are preserved verbatim — the band only widens."""
|
|
surface = deduction_grounded_surface("It never snows. Therefore it never snows.")
|
|
assert surface is not None
|
|
assert "can't parse" in surface
|
|
|
|
|
|
def test_membership_syllogism_is_decided_band_v3_mem() -> None:
|
|
"""The classic instantiation syllogism — reserved by ADR-0257 §6.1, now
|
|
DECIDED by the member band (ADR-0258), authoritatively (the
|
|
``en_member_single`` band holds an earned SERVE license), with the verdict
|
|
rendered over the user's own sentences."""
|
|
surface = deduction_grounded_surface(
|
|
"Socrates is a man. All men are mortal. Therefore Socrates is mortal."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: socrates is mortal" in surface
|
|
# R-13 (2026-07-28): this band was DEMOTED by the distinct-evidence re-count
|
|
# — en_member_single (60 distinct of 720 committed). The shape is still DECIDED, which is what this
|
|
# test is actually about; what changed is the claim attached to the answer.
|
|
# The engine now discloses that it has not earned a track record on this
|
|
# shape instead of answering authoritatively. Asserting the old behaviour
|
|
# here would re-assert a licence the evidence never supported.
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
|
|
|
|
def test_member_negative_universal_is_decided() -> None:
|
|
"""The E-form ("no …") lowers to a negated instantiation and decides."""
|
|
surface = deduction_grounded_surface(
|
|
"Fido is a dog. No dogs are cats. Therefore Fido is not a cat."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: fido is not a cat" in surface
|
|
|
|
|
|
def test_member_unknown_is_scoped_to_the_instantiated_reading() -> None:
|
|
"""Member-band UNKNOWN must scope itself (ADR-0258 §3): co-reference of
|
|
distinct names or an unlinked class identity could settle what the
|
|
lowering leaves open, so the claim is stated of the reading."""
|
|
surface = deduction_grounded_surface(
|
|
"Socrates is mortal. All men are mortal. Therefore Socrates is a man."
|
|
)
|
|
assert surface is not None
|
|
assert "Reading each name as one individual" in surface
|
|
assert "don't settle" in surface
|
|
|
|
|
|
def test_member_unearned_band_would_be_hedged() -> None:
|
|
"""The en_member_* bands ride the SAME earned-license gate: strip the
|
|
license and the same sound answer is served DISCLOSED, never
|
|
authoritatively."""
|
|
surface = deduction_grounded_surface(
|
|
"Socrates is a man. All men are mortal. Therefore Socrates is mortal.",
|
|
license_lookup=lambda band: None,
|
|
)
|
|
assert surface is not None
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
assert "Your premises entail: socrates is mortal" in surface
|
|
|
|
|
|
def test_member_reader_refusal_keeps_prior_honest_surface() -> None:
|
|
"""When the member band ALSO cannot read the argument (here: tense, a
|
|
deliberate ADR-0258 scope-out), the pre-existing honest surfaces are
|
|
preserved verbatim — the band only widens."""
|
|
surface = deduction_grounded_surface(
|
|
"Socrates was a man. All men are mortal. Therefore Socrates is mortal."
|
|
)
|
|
assert surface is not None
|
|
assert "can't parse" in surface
|
|
|
|
|
|
def test_conditional_membership_fusion_is_decided_band_v4_cm() -> None:
|
|
"""The ADR-0258 §6.1 scope-out, now decided (ADR-0259): a conditional
|
|
whose clauses are membership facts, authoritatively (the
|
|
``en_condmem_conditional`` band holds an earned SERVE license)."""
|
|
surface = deduction_grounded_surface(
|
|
"If Socrates is a man then Socrates is mortal. Socrates is a man. "
|
|
"Therefore Socrates is mortal."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: socrates is mortal" in surface
|
|
# R-13 (2026-07-28): this band was DEMOTED by the distinct-evidence re-count
|
|
# — en_condmem_conditional (60 distinct of 720 committed). The shape is still DECIDED, which is what this
|
|
# test is actually about; what changed is the claim attached to the answer.
|
|
# The engine now discloses that it has not earned a track record on this
|
|
# shape instead of answering authoritatively. Asserting the old behaviour
|
|
# here would re-assert a licence the evidence never supported.
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
|
|
|
|
def test_conditional_membership_fusion_genuinely_fuses() -> None:
|
|
"""The mechanism this band adds: a bare universal's instantiated atom
|
|
unifies with a connective leaf's atom — neither mechanism alone decides
|
|
this argument."""
|
|
surface = deduction_grounded_surface(
|
|
"All men are mortal. If Socrates is a philosopher then Socrates is a man. "
|
|
"Socrates is a philosopher. Therefore Socrates is mortal."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: socrates is mortal" in surface
|
|
|
|
|
|
def test_cond_member_unknown_is_scoped_to_the_instantiated_reading() -> None:
|
|
surface = deduction_grounded_surface(
|
|
"If Socrates is a man then Socrates is mortal. Therefore Socrates is mortal."
|
|
)
|
|
assert surface is not None
|
|
assert "Reading each name as one individual" in surface
|
|
assert "don't settle" in surface
|
|
|
|
|
|
def test_cond_member_unearned_band_would_be_hedged() -> None:
|
|
"""The en_condmem_* bands ride the SAME earned-license gate: strip the
|
|
license and the same sound answer is served DISCLOSED, never
|
|
authoritatively."""
|
|
surface = deduction_grounded_surface(
|
|
"If Socrates is a man then Socrates is mortal. Socrates is a man. "
|
|
"Therefore Socrates is mortal.",
|
|
license_lookup=lambda band: None,
|
|
)
|
|
assert surface is not None
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
assert "Your premises entail: socrates is mortal" in surface
|
|
|
|
|
|
def test_cond_member_reader_refusal_keeps_prior_honest_surface() -> None:
|
|
"""When the conditional-membership band ALSO cannot read the argument
|
|
(here: a conditional nested inside another), the pre-existing honest
|
|
surface is preserved verbatim — the band only widens."""
|
|
surface = deduction_grounded_surface(
|
|
"If the door is open then if the window is open then the room is cold. "
|
|
"Therefore the room is cold."
|
|
)
|
|
assert surface is not None
|
|
assert "can't parse" in surface
|
|
|
|
|
|
def test_surface_is_deterministic() -> None:
|
|
text = "If p then q. p. Therefore q."
|
|
assert deduction_grounded_surface(text) == deduction_grounded_surface(text)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Live runtime — flag off is byte-identical to pre-arc dispatch
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_flag_off_preserves_pack_token_gloss_byte_identity() -> None:
|
|
rt = ChatRuntime(
|
|
config=RuntimeConfig(deduction_serving_enabled=False), no_load_state=True,
|
|
)
|
|
resp = rt.chat("If p then q. p. Therefore q.")
|
|
assert resp.grounding_source == "pack"
|
|
assert "Pack-resident tokens" in resp.surface
|
|
|
|
|
|
def test_flag_is_on_by_default_after_ratification() -> None:
|
|
"""RATIFIED 2026-07-24 (ADR-0256): the default flipped False -> True on 25
|
|
bands at 720/720 wrong=0 and a 166/166 serve lane. The off-path above stays
|
|
pinned because rollback must remain byte-identical, not because it is the
|
|
default."""
|
|
assert RuntimeConfig().deduction_serving_enabled is True
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Live runtime — flag on, single turn
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_runtime_deduction_serves_entailed_answer() -> None:
|
|
rt = ChatRuntime(
|
|
config=RuntimeConfig(deduction_serving_enabled=True), no_load_state=True,
|
|
)
|
|
resp = rt.chat("If p then q. p. Therefore q.")
|
|
assert resp.grounding_source == "deduction"
|
|
assert "entail: q" in resp.surface
|
|
|
|
|
|
def test_runtime_non_argument_prompt_unaffected_by_flag() -> None:
|
|
"""The flag only intercepts argument-shaped text; ordinary prompts
|
|
still route through the pre-existing dispatch."""
|
|
rt = ChatRuntime(
|
|
config=RuntimeConfig(deduction_serving_enabled=True), no_load_state=True,
|
|
)
|
|
resp = rt.chat("What is light?")
|
|
assert resp.grounding_source != "deduction"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Live runtime — full propositional gold corpus, multi-turn session, wrong=0
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_runtime_decides_full_propositional_gold_corpus_wrong_zero() -> None:
|
|
"""End-to-end regression: every Band v1 gold case, decided through the
|
|
real ``core chat`` serving path across one multi-turn session (not
|
|
just the isolated comprehend->project->oracle lane). wrong=0 is the
|
|
load-bearing assertion; declines are acceptable, a disagreement with
|
|
gold is not."""
|
|
rt = ChatRuntime(
|
|
config=RuntimeConfig(deduction_serving_enabled=True), no_load_state=True,
|
|
)
|
|
cases = _load_cases()
|
|
assert cases, "gold corpus must not be empty"
|
|
wrong: list[str] = []
|
|
for case in cases:
|
|
resp = rt.chat(case["text"])
|
|
assert resp.grounding_source == "deduction", (
|
|
f"expected deduction routing for {case['text']!r}, "
|
|
f"got grounding_source={resp.grounding_source!r}"
|
|
)
|
|
surface = resp.surface
|
|
gold = case["gold"]
|
|
if gold == "entailed" and "Your premises entail:" in surface:
|
|
continue
|
|
if gold == "refuted" and "entail the opposite" in surface:
|
|
continue
|
|
if gold == "unknown" and "don't settle" in surface:
|
|
continue
|
|
wrong.append(f"{case['text']!r} -> gold={gold} surface={surface!r}")
|
|
assert not wrong, "\n".join(wrong)
|
|
|
|
|
|
def test_verb_predicate_is_decided_band_v5_vp() -> None:
|
|
"""The ADR-0258 §6.3 verb-predicate scope-out, now decided (ADR-0260):
|
|
a verb universal discharged by a membership fact, authoritatively (the
|
|
``en_verb_universal`` band holds an earned SERVE license)."""
|
|
surface = deduction_grounded_surface(
|
|
"All philosophers teach. Socrates is a philosopher. "
|
|
"Therefore Socrates teaches."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: socrates teaches" in surface
|
|
assert not surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
|
|
|
|
def test_verb_universal_discharged_by_membership_chain() -> None:
|
|
"""The mechanism this band adds: the membership atom minted by copula
|
|
sentences unifies with a verb universal's instantiated antecedent across
|
|
an is-a chain — neither v3-MEM (refuses verb sentences) nor v2-EN
|
|
(refuses quantifier leads and is-a clauses) alone decides this."""
|
|
surface = deduction_grounded_surface(
|
|
"Felix is a tortoise. All tortoises are reptiles. All reptiles crawl. "
|
|
"Therefore Felix crawls."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: felix crawls" in surface
|
|
|
|
|
|
def test_verb_unknown_is_scoped_to_the_verb_reading() -> None:
|
|
surface = deduction_grounded_surface(
|
|
"Viktor cooks. Viktor is a chef. Therefore Viktor cooks stew."
|
|
)
|
|
assert surface is not None
|
|
assert "verb phrase at face value" in surface
|
|
assert "don't settle" in surface
|
|
|
|
|
|
def test_verb_unearned_band_would_be_hedged() -> None:
|
|
"""The en_verb_* bands ride the SAME earned-license gate: strip the
|
|
license and the same sound answer is served DISCLOSED, never
|
|
authoritatively."""
|
|
surface = deduction_grounded_surface(
|
|
"All philosophers teach. Socrates is a philosopher. "
|
|
"Therefore Socrates teaches.",
|
|
license_lookup=lambda band: None,
|
|
)
|
|
assert surface is not None
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
assert "Your premises entail: socrates teaches" in surface
|
|
|
|
|
|
def test_verb_reader_refusal_keeps_prior_honest_surface() -> None:
|
|
"""When the verb band ALSO cannot read the argument (here: a
|
|
ditransitive), the pre-existing honest surface is preserved verbatim —
|
|
the band only widens."""
|
|
surface = deduction_grounded_surface(
|
|
"Hannah gives Samuel bread. Hannah is a mother. "
|
|
"Therefore Hannah gives Samuel bread."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail" not in surface
|
|
|
|
|
|
def test_existential_is_decided_band_v6_ex() -> None:
|
|
"""ADR-0261: an existential premise carried through a universal to an
|
|
existential conclusion (Darii), authoritatively — the
|
|
``en_exist_universal`` band holds an earned SERVE license."""
|
|
surface = deduction_grounded_surface(
|
|
"Every mammal is a vertebrate. Some whales are mammals. "
|
|
"Therefore some whales are vertebrates."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail: some whales are vertebrates" in surface
|
|
# R-13 (2026-07-28): this band was DEMOTED by the distinct-evidence re-count
|
|
# — en_exist_universal (46 distinct of 720 committed). The shape is still DECIDED, which is what this
|
|
# test is actually about; what changed is the claim attached to the answer.
|
|
# The engine now discloses that it has not earned a track record on this
|
|
# shape instead of answering authoritatively. Asserting the old behaviour
|
|
# here would re-assert a licence the evidence never supported.
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
|
|
|
|
def test_existential_conclusion_does_not_close_the_domain() -> None:
|
|
"""The mechanism this band adds: every NAMED individual fails the query,
|
|
yet the arbitrary element keeps the answer honestly unsettled instead of
|
|
claiming refutation."""
|
|
surface = deduction_grounded_surface(
|
|
"Vega is a star. Vega is not visible. Therefore some stars are visible."
|
|
)
|
|
assert surface is not None
|
|
assert "don't settle" in surface
|
|
assert "cannot hold" not in surface
|
|
|
|
|
|
def test_existential_unknown_discloses_the_no_import_reading() -> None:
|
|
surface = deduction_grounded_surface(
|
|
"All unicorns are horned. Therefore some unicorns are horned."
|
|
)
|
|
assert surface is not None
|
|
assert "don't settle" in surface
|
|
assert 'as claiming a member exists' in surface
|
|
|
|
|
|
def test_existential_unearned_band_would_be_hedged() -> None:
|
|
"""The en_exist_* bands ride the SAME earned-license gate."""
|
|
surface = deduction_grounded_surface(
|
|
"Every mammal is a vertebrate. Some whales are mammals. "
|
|
"Therefore some whales are vertebrates.",
|
|
license_lookup=lambda band: None,
|
|
)
|
|
assert surface is not None
|
|
assert surface.startswith(_UNVERIFIED_SHAPE_DISCLOSURE)
|
|
assert "Your premises entail: some whales are vertebrates" in surface
|
|
|
|
|
|
def test_existential_reader_refusal_keeps_prior_honest_surface() -> None:
|
|
"""When the existential band ALSO cannot read the argument (here: a
|
|
partitive), the pre-existing honest surface is preserved verbatim — the
|
|
last band only widens, like every band before it."""
|
|
surface = deduction_grounded_surface(
|
|
"Some of the sailors are brave. Therefore some sailors are brave."
|
|
)
|
|
assert surface is not None
|
|
assert "Your premises entail" not in surface
|