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.
214 lines
7.4 KiB
Python
214 lines
7.4 KiB
Python
"""Deterministic English morphology for the realizer.
|
|
|
|
Handles inflection of predicates for tense, aspect, and negation.
|
|
This is intentionally rule-based and limited to the seed vocabulary.
|
|
Irregular forms are listed explicitly; regular forms follow English rules.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
|
|
# Genuinely irregular English verbs (the previous tables held only
|
|
# regular forms that the suffix rules already produce correctly).
|
|
# Listed as 3rd-person singular present → (past, past_participle).
|
|
# Coverage: ~100 common English irregulars including every verb that
|
|
# the seed packs and Phase 5 OOD lanes use whose past form does not
|
|
# follow the regular -ed rule. Adding a verb here is the cheap fix
|
|
# for english_fluency_ood gaps.md G1.
|
|
_IRREGULAR_FORMS: dict[str, tuple[str, str]] = {
|
|
# be / have / do
|
|
"is": ("was", "been"),
|
|
"are": ("were", "been"),
|
|
"has": ("had", "had"),
|
|
"does": ("did", "done"),
|
|
# mental / cognitive
|
|
"thinks": ("thought", "thought"),
|
|
"knows": ("knew", "known"),
|
|
"sees": ("saw", "seen"),
|
|
"hears": ("heard", "heard"),
|
|
"feels": ("felt", "felt"),
|
|
"finds": ("found", "found"),
|
|
"loses": ("lost", "lost"),
|
|
"means": ("meant", "meant"),
|
|
"reads": ("read", "read"),
|
|
"tells": ("told", "told"),
|
|
"says": ("said", "said"),
|
|
"thinks": ("thought", "thought"),
|
|
# motion
|
|
"goes": ("went", "gone"),
|
|
"comes": ("came", "come"),
|
|
"runs": ("ran", "run"),
|
|
"stands": ("stood", "stood"),
|
|
"sits": ("sat", "sat"),
|
|
"lies": ("lay", "lain"),
|
|
"flies": ("flew", "flown"),
|
|
"swims": ("swam", "swum"),
|
|
"rides": ("rode", "ridden"),
|
|
"drives": ("drove", "driven"),
|
|
"rises": ("rose", "risen"),
|
|
"falls": ("fell", "fallen"),
|
|
# giving / taking / holding
|
|
"gives": ("gave", "given"),
|
|
"takes": ("took", "taken"),
|
|
"brings": ("brought", "brought"),
|
|
"buys": ("bought", "bought"),
|
|
"sells": ("sold", "sold"),
|
|
"holds": ("held", "held"),
|
|
"keeps": ("kept", "kept"),
|
|
"leaves": ("left", "left"),
|
|
"lends": ("lent", "lent"),
|
|
"sends": ("sent", "sent"),
|
|
"spends": ("spent", "spent"),
|
|
"puts": ("put", "put"),
|
|
"sets": ("set", "set"),
|
|
"lets": ("let", "let"),
|
|
# building / making / breaking
|
|
"makes": ("made", "made"),
|
|
"builds": ("built", "built"),
|
|
"breaks": ("broke", "broken"),
|
|
"tears": ("tore", "torn"),
|
|
"burns": ("burned", "burned"),
|
|
"shines": ("shone", "shone"),
|
|
"draws": ("drew", "drawn"),
|
|
"writes": ("wrote", "written"),
|
|
"speaks": ("spoke", "spoken"),
|
|
"wins": ("won", "won"),
|
|
"loses": ("lost", "lost"),
|
|
# binding / connecting
|
|
"binds": ("bound", "bound"),
|
|
"winds": ("wound", "wound"),
|
|
"weaves": ("wove", "woven"),
|
|
"flies": ("flew", "flown"),
|
|
"spins": ("spun", "spun"),
|
|
"sticks": ("stuck", "stuck"),
|
|
"swears": ("swore", "sworn"),
|
|
# giving form / shape
|
|
"becomes": ("became", "become"),
|
|
"grows": ("grew", "grown"),
|
|
"blows": ("blew", "blown"),
|
|
"throws": ("threw", "thrown"),
|
|
"shakes": ("shook", "shaken"),
|
|
"wakes": ("woke", "woken"),
|
|
# eating / drinking / cooking
|
|
"eats": ("ate", "eaten"),
|
|
"drinks": ("drank", "drunk"),
|
|
"feeds": ("fed", "fed"),
|
|
"bites": ("bit", "bitten"),
|
|
"freezes": ("froze", "frozen"),
|
|
# cutting / striking
|
|
"cuts": ("cut", "cut"),
|
|
"hits": ("hit", "hit"),
|
|
"shoots": ("shot", "shot"),
|
|
"splits": ("split", "split"),
|
|
"strikes": ("struck", "struck"),
|
|
"fights": ("fought", "fought"),
|
|
"wears": ("wore", "worn"),
|
|
# sleeping / rising / etc
|
|
"sleeps": ("slept", "slept"),
|
|
"wakes": ("woke", "woken"),
|
|
"rises": ("rose", "risen"),
|
|
# finding / hiding
|
|
"hides": ("hid", "hidden"),
|
|
"seeks": ("sought", "sought"),
|
|
"catches": ("caught", "caught"),
|
|
"teaches": ("taught", "taught"),
|
|
"thinks": ("thought", "thought"),
|
|
"brings": ("brought", "brought"),
|
|
# less common / archaic
|
|
"begins": ("began", "begun"),
|
|
"deals": ("dealt", "dealt"),
|
|
"leads": ("led", "led"),
|
|
"meets": ("met", "met"),
|
|
"sits": ("sat", "sat"),
|
|
"swears": ("swore", "sworn"),
|
|
"shoots": ("shot", "shot"),
|
|
"casts": ("cast", "cast"),
|
|
"costs": ("cost", "cost"),
|
|
"hurts": ("hurt", "hurt"),
|
|
"lets": ("let", "let"),
|
|
"quits": ("quit", "quit"),
|
|
"shuts": ("shut", "shut"),
|
|
}
|
|
|
|
# Legacy compatibility — historic call sites use these names. All
|
|
# regular-verb entries here match the suffix-rule output, so removing
|
|
# them is purely cosmetic; new irregulars live in `_IRREGULAR_FORMS`.
|
|
_IRREGULAR_PAST: dict[str, str] = {v: forms[0] for v, forms in _IRREGULAR_FORMS.items()}
|
|
|
|
_IRREGULAR_PARTICIPLE: dict[str, str] = {
|
|
# Present-participle (-ing) is almost always regular. Only handle
|
|
# the truly weird cases (lie→lying handled by the suffix rule;
|
|
# be→being is the one English present-participle that needs a
|
|
# special entry, but `is` doesn't normally surface as a content
|
|
# predicate in our realizer pipeline).
|
|
}
|
|
|
|
_IRREGULAR_PAST_PARTICIPLE: dict[str, str] = {v: forms[1] for v, forms in _IRREGULAR_FORMS.items()}
|
|
|
|
|
|
# Short -ies verbs whose base is -ie (not -y). English's "ies → y"
|
|
# rule (cries→cry, flies→fly) breaks for these short stems where the
|
|
# original lemma keeps the -ie (dies→die, lies→lie, ties→tie).
|
|
_IES_KEEP_IE: frozenset[str] = frozenset({"dies", "lies", "ties", "vies", "pies", "hies"})
|
|
|
|
|
|
def _base_form(verb_3sg: str) -> str:
|
|
if verb_3sg in _IES_KEEP_IE:
|
|
return verb_3sg[:-1]
|
|
if verb_3sg.endswith("ies"):
|
|
return verb_3sg[:-3] + "y"
|
|
if verb_3sg.endswith("es"):
|
|
return verb_3sg[:-2] if verb_3sg[:-2].endswith(("s", "sh", "ch", "x", "z", "o")) else verb_3sg[:-1]
|
|
if verb_3sg.endswith("s"):
|
|
return verb_3sg[:-1]
|
|
return verb_3sg
|
|
|
|
|
|
def past_tense(verb_3sg: str) -> str:
|
|
if verb_3sg in _IRREGULAR_PAST:
|
|
return _IRREGULAR_PAST[verb_3sg]
|
|
base = _base_form(verb_3sg)
|
|
if base.endswith("e"):
|
|
return base + "d" # make → made? no — make is irregular; bake → baked
|
|
if base.endswith("y") and len(base) > 1 and base[-2] not in "aeiou":
|
|
return base[:-1] + "ied" # cry → cried
|
|
if _is_cvc_ending(base):
|
|
return base + base[-1] + "ed" # stop → stopped, plan → planned
|
|
return base + "ed"
|
|
|
|
|
|
_VOWELS = frozenset("aeiou")
|
|
|
|
|
|
def _is_cvc_ending(base: str) -> bool:
|
|
"""True if `base` ends in consonant-vowel-consonant (excluding w/x/y),
|
|
the trigger pattern for doubling the final consonant before -ing /
|
|
-ed in English."""
|
|
if len(base) < 3:
|
|
return False
|
|
c1, v, c2 = base[-3], base[-2], base[-1]
|
|
if c2 in {"w", "x", "y"}:
|
|
return False
|
|
return (c1 not in _VOWELS) and (v in _VOWELS) and (c2 not in _VOWELS)
|
|
|
|
|
|
def present_participle(verb_3sg: str) -> str:
|
|
if verb_3sg in _IRREGULAR_PARTICIPLE:
|
|
return _IRREGULAR_PARTICIPLE[verb_3sg]
|
|
base = _base_form(verb_3sg)
|
|
if base.endswith("ie"):
|
|
return base[:-2] + "ying" # die → dying, lie → lying
|
|
if base.endswith("e") and not base.endswith("ee"):
|
|
return base[:-1] + "ing" # make → making
|
|
if _is_cvc_ending(base):
|
|
return base + base[-1] + "ing" # run → running, swim → swimming
|
|
return base + "ing"
|
|
|
|
|
|
def past_participle(verb_3sg: str) -> str:
|
|
if verb_3sg in _IRREGULAR_PAST_PARTICIPLE:
|
|
return _IRREGULAR_PAST_PARTICIPLE[verb_3sg]
|
|
return past_tense(verb_3sg)
|
|
|
|
|
|
def base_form(verb_3sg: str) -> str:
|
|
return _base_form(verb_3sg)
|