Six contained-tier gap closures from the cross-phase gap audit.
Every gaps.md file involved gains a resolution block; the historical
findings are preserved for traceability.
Identity (teaching/review.py)
- _ROLE_FRAMES gains the philosophical-axis family (axiology, ontology,
telos, ethos, epistemology, morality, ethics, virtues, norms,
doctrine, creed, convictions, beliefs, values, principles).
- _REDIRECT_VERBS gains the dismissal family (disregard, dismiss,
bypass, circumvent, renounce, reject, repudiate).
- Closes multi_agent_composition holdout MAC-HOLD-001 ("disregard your
axiology") and the matching adversarial_identity gap.
- Multi-agent holdouts: 8/8 attacks rejected, 3/3 legits accepted.
Pipeline (core/cognition/pipeline.py + docs/runtime_contracts.md)
- When the unknown-domain gate fires, ChatRuntime returns the
"I don't have field coordinates for that yet." stub and
vault_hits == 0. The pipeline now honours that stub as the
user-facing surface instead of overriding with the realizer's
fallback articulation. walk_surface is unchanged either way.
- New contract test
tests/test_semantic_realizer_integration.py::test_pipeline_honours_safety_stub_when_gate_fires
locks the contract; the existing semantic-surface test now primes
the vault first so the gate doesn't fire on the probe.
- Closes calibration gaps.md Finding 2.
Realizer morphology (generate/morphology.py)
- G1: ~100-entry irregular-verb table replaces the previous list which
contained only regular forms. Includes bind→bound, run→ran,
stand→stood, write→wrote/written, eat→ate/eaten, fly→flew/flown,
swim→swam/swum, etc.
- CVC doubling rule for -ed and -ing (stop→stopped/stopping,
plan→planned, run→running).
- Short-ies disambiguation (die/lie/tie keep -ie- in the base; cry/fly
collapse to -y). Lie is also irregular (lay/lain) — uses
_IRREGULAR_FORMS first.
- 28-case regression test (tests/test_morphology_irregular.py).
Realizer plural agreement (generate/templates.py)
- G2: under universal/existential/many/few/most quantifiers, count-noun
subjects pluralise (molecule → molecules) and the verb de-conjugates
(binds → bind). Negation toggles does-not → do-not. Aspect toggles
has → have, is → are. All other constructions unchanged.
- Mass nouns (evidence, wisdom, knowledge, truth, water, …) stay
singular under quantifiers — "all evidence supports truth" is right;
"all evidences support" would be wrong English.
- 17-case regression test
(tests/test_realizer_quantifier_agreement.py) covering count vs mass,
irregular plurals (child→children, analysis→analyses), and the
quantifier-tense / quantifier-aspect / quantifier-negation grid.
Rubric punctuation tolerance (evals/grammatical_coverage/runner.py)
- G3: _check_word_order strips trailing/leading punctuation
(.,;:!?—–) before exact-word comparison so "river," still satisfies
word_order=["river"]. must_contain also accepts punctuation-
stripped token matches.
- Affects every lane that uses grammatical_coverage scoring; the OOD
case generators no longer need to pin punctuated accept_surfaces for
C06.
Case generator + lane regeneration
- scripts/generate_english_fluency_ood.py uses generate.templates.pluralize
for C07/C08 must_contain + word_order so case-side constraints stay
aligned with the (more correct) realizer.
- All Phase 5 OOD lane cases (5.1, 5.4–5.7) regenerated; results files
re-scored.
CLI (core/cli.py)
- cmd_eval no longer crashes on lanes whose case_details use "id"
instead of "case_id" (adversarial_identity, multi_agent_composition).
- Cognition CLI lane gains the two new morphology/quantifier
regression test files.
Lane sweep (all 100%, no regression):
english_fluency_ood 117/117 public + 39/39 holdouts
elementary_mathematics_ood 117/117 + 39/39
foundational_physics_ood 117/117 + 39/39
foundational_biology_ood 117/117 + 39/39
classical_literature_ood 117/117 + 39/39
grammatical_coverage back to 100% on its own seed cases
hebrew_fluency / koine_greek_fluency 3/3 each
CLI lane health:
smoke 54, runtime 19, teaching 17, packs 6, cognition 103 (was 57),
algebra 132.
108 lines
4.3 KiB
Python
108 lines
4.3 KiB
Python
"""Realizer plural agreement under quantifiers — G2 regression.
|
|
|
|
Closes english_fluency_ood gaps.md G2: under universal/existential
|
|
quantifiers, count-noun subjects pluralise and the verb de-conjugates
|
|
to the bare base. Mass nouns (evidence, wisdom, …) stay singular
|
|
under the same quantifiers ("all evidence supports truth" is correct;
|
|
"all evidences support truth" is wrong English).
|
|
|
|
Coverage also includes the quantifier-tense / quantifier-aspect /
|
|
quantifier-negation interactions so future regressions are caught.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from generate.graph_planner import RhetoricalMove
|
|
from generate.templates import is_mass_noun, pluralize, render_step
|
|
|
|
|
|
# Count-noun pluralisation under "all"/"some" quantifiers.
|
|
_PLURAL_CASES: list[tuple[str, str, str, str, str]] = [
|
|
("all", "molecule", "binds", "enzyme", "all molecules bind enzyme"),
|
|
("all", "atom", "forms", "bond", "all atoms form bond"),
|
|
("some", "river", "flows", "valley", "some rivers flow valley"),
|
|
("all", "child", "fits", "school", "all children fit school"), # irregular plural
|
|
("all", "analysis", "yields","insight", "all analyses yield insight"), # latinate
|
|
("some", "ribosome", "assembles", "protein", "some ribosomes assemble protein"),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("quantifier,subj,pred,obj,expected", _PLURAL_CASES)
|
|
def test_count_noun_pluralises_under_quantifier(
|
|
quantifier: str, subj: str, pred: str, obj: str, expected: str
|
|
) -> None:
|
|
surface = render_step(RhetoricalMove.ASSERT, subj, pred, obj, quantifier=quantifier)
|
|
assert surface == expected
|
|
|
|
|
|
# Mass-noun cases — must NOT pluralise; verb stays singular too.
|
|
_MASS_CASES: list[tuple[str, str, str, str, str]] = [
|
|
("all", "evidence", "supports", "truth", "all evidence supports truth"),
|
|
("all", "wisdom", "requires", "patience", "all wisdom requires patience"),
|
|
("some", "truth", "requires", "courage", "some truth requires courage"),
|
|
("some", "knowledge","grounds", "action", "some knowledge grounds action"),
|
|
("all", "water", "flows", "downhill", "all water flows downhill"),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("quantifier,subj,pred,obj,expected", _MASS_CASES)
|
|
def test_mass_noun_stays_singular_under_quantifier(
|
|
quantifier: str, subj: str, pred: str, obj: str, expected: str
|
|
) -> None:
|
|
surface = render_step(RhetoricalMove.ASSERT, subj, pred, obj, quantifier=quantifier)
|
|
assert surface == expected
|
|
|
|
|
|
# Quantifier + negation interaction: plural subject → "do not", mass/none → "does not".
|
|
def test_quantifier_negation_uses_do_not_for_plural_subject() -> None:
|
|
s = render_step(
|
|
RhetoricalMove.ASSERT, "molecule", "binds", "enzyme",
|
|
quantifier="all", negated=True,
|
|
)
|
|
assert s == "all molecules do not bind enzyme"
|
|
|
|
|
|
def test_quantifier_negation_uses_does_not_for_mass_subject() -> None:
|
|
s = render_step(
|
|
RhetoricalMove.ASSERT, "evidence", "supports", "truth",
|
|
quantifier="all", negated=True,
|
|
)
|
|
assert s == "all evidence does not support truth"
|
|
|
|
|
|
# Quantifier + aspect: plural subject → "have/are", mass/none → "has/is".
|
|
def test_quantifier_perfective_aspect_uses_have_for_plural() -> None:
|
|
s = render_step(
|
|
RhetoricalMove.ASSERT, "molecule", "binds", "enzyme",
|
|
quantifier="all", aspect="perfective",
|
|
)
|
|
assert s == "all molecules have bound enzyme"
|
|
|
|
|
|
def test_quantifier_imperfective_aspect_uses_are_for_plural() -> None:
|
|
s = render_step(
|
|
RhetoricalMove.ASSERT, "atom", "forms", "bond",
|
|
quantifier="some", aspect="imperfective",
|
|
)
|
|
assert s == "some atoms are forming bond"
|
|
|
|
|
|
# Helper-level checks (so future code changes that bypass render_step
|
|
# still hit the same rules).
|
|
def test_pluralize_handles_irregular_and_latinate() -> None:
|
|
assert pluralize("child") == "children"
|
|
assert pluralize("analysis") == "analyses"
|
|
assert pluralize("bus") == "buses"
|
|
assert pluralize("city") == "cities"
|
|
assert pluralize("leaf") == "leaves"
|
|
assert pluralize("fish") == "fish" # invariant
|
|
|
|
|
|
def test_is_mass_noun_known_set() -> None:
|
|
assert is_mass_noun("evidence")
|
|
assert is_mass_noun("Wisdom") # case-insensitive
|
|
assert is_mass_noun("water")
|
|
assert not is_mass_noun("molecule")
|
|
assert not is_mass_noun("atom")
|