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.
206 lines
7.3 KiB
Python
206 lines
7.3 KiB
Python
"""Deterministic surface templates for rhetorical moves.
|
|
|
|
Each template is a format string keyed by RhetoricalMove. Slots:
|
|
{subject} — primary subject from the articulation step
|
|
{predicate} — semantic predicate (e.g. "is_defined_as", "contrasts_with")
|
|
{obj} — object slot from the graph node (may be "<pending>")
|
|
|
|
Templates are intentionally simple. The goal is structural correctness,
|
|
not fluency — fluency comes in a later phase when the generation stream
|
|
consumes these as constraints rather than final output.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from generate.graph_planner import RhetoricalMove
|
|
from generate.morphology import base_form, past_participle, past_tense, present_participle
|
|
|
|
|
|
# Noun pluralisation — used under quantifiers (all/some/many/few/most).
|
|
# Closes english_fluency_ood gaps.md G2 (plural agreement).
|
|
_IRREGULAR_PLURALS: dict[str, str] = {
|
|
"child": "children", "ox": "oxen", "foot": "feet", "tooth": "teeth",
|
|
"man": "men", "woman": "women", "person": "people",
|
|
"mouse": "mice", "louse": "lice", "goose": "geese",
|
|
# invariant
|
|
"sheep": "sheep", "fish": "fish", "deer": "deer", "moose": "moose",
|
|
"series": "series", "species": "species",
|
|
# latin/greek-origin domain vocabulary
|
|
"datum": "data", "criterion": "criteria", "phenomenon": "phenomena",
|
|
"analysis": "analyses", "axis": "axes", "basis": "bases",
|
|
"thesis": "theses", "hypothesis": "hypotheses",
|
|
"mitochondrion": "mitochondria",
|
|
}
|
|
|
|
|
|
def pluralize(noun: str) -> str:
|
|
if not noun:
|
|
return noun
|
|
if noun in _IRREGULAR_PLURALS:
|
|
return _IRREGULAR_PLURALS[noun]
|
|
n = noun
|
|
if n.endswith(("s", "sh", "ch", "x", "z")):
|
|
return n + "es"
|
|
if n.endswith("y") and len(n) > 1 and n[-2] not in "aeiou":
|
|
return n[:-1] + "ies"
|
|
if n.endswith("fe"):
|
|
return n[:-2] + "ves"
|
|
if n.endswith("f"):
|
|
return n[:-1] + "ves"
|
|
return n + "s"
|
|
|
|
|
|
# Quantifiers that demand plural agreement on the subject + verb.
|
|
# "the" / "a" stay singular; "every" / "each" are singular by English
|
|
# rule even though semantically universal.
|
|
_PLURAL_QUANTIFIERS: frozenset[str] = frozenset({
|
|
"all", "some", "many", "few", "most", "several", "various", "no",
|
|
})
|
|
|
|
# Mass nouns — uncountable in English, so "all evidence", "some wisdom"
|
|
# stay singular under quantifiers ("all evidences" is wrong). The
|
|
# verb still agrees (singular: "all evidence supports truth").
|
|
# This list covers the abstract/epistemic vocabulary in
|
|
# en_core_cognition_v1 + common English mass nouns.
|
|
_MASS_NOUNS: frozenset[str] = frozenset({
|
|
# epistemic / abstract (the seed-pack vocabulary)
|
|
"evidence", "wisdom", "knowledge", "truth", "light", "darkness",
|
|
"information", "data", "music", "art", "literature", "philosophy",
|
|
"courage", "patience", "love", "hope", "fear", "grace",
|
|
"meaning", "purpose", "beauty", "justice", "freedom",
|
|
# physical mass
|
|
"water", "air", "fire", "earth", "sand", "rain", "snow", "ice",
|
|
"wood", "metal", "gold", "silver", "iron", "stone",
|
|
"blood", "flesh", "bone",
|
|
# collective / continuous
|
|
"weather", "traffic", "furniture", "luggage", "advice",
|
|
"equipment", "machinery", "scenery", "money", "news",
|
|
"research", "progress", "feedback",
|
|
})
|
|
|
|
|
|
def is_mass_noun(noun: str) -> bool:
|
|
return noun.lower() in _MASS_NOUNS
|
|
|
|
|
|
_PREDICATE_DISPLAY: dict[str, str] = {
|
|
"is_defined_as": "is defined as",
|
|
"is_caused_by": "is caused by",
|
|
"has_steps": "has the following steps",
|
|
"contrasts_with": "contrasts with",
|
|
"corrects": "corrects",
|
|
"recalls": "recalls",
|
|
"is_verified_as": "is verified as",
|
|
"addresses": "addresses",
|
|
"defines": "defines",
|
|
"means": "means",
|
|
"grounds": "grounds",
|
|
"supports": "supports",
|
|
"causes": "causes",
|
|
"reveals": "reveals",
|
|
"precedes": "precedes",
|
|
"follows": "follows",
|
|
"belongs_to": "belongs to",
|
|
"answers": "answers",
|
|
"is_grounded_in": "is grounded in",
|
|
"is_distinguished_from": "is distinguished from",
|
|
"implies": "implies",
|
|
"entails": "entails",
|
|
"requires": "requires",
|
|
"verifies": "verifies",
|
|
"evidences": "evidences",
|
|
"orders": "orders",
|
|
}
|
|
|
|
|
|
def _humanize_predicate(predicate: str) -> str:
|
|
return _PREDICATE_DISPLAY.get(predicate, predicate.replace("_", " "))
|
|
|
|
|
|
_MOVE_TEMPLATES: dict[RhetoricalMove, str] = {
|
|
RhetoricalMove.ASSERT: "{subject} {predicate_h} {obj}",
|
|
RhetoricalMove.ELABORATE: "furthermore, {subject} {predicate_h} {obj}",
|
|
RhetoricalMove.CONTRAST: "in contrast, {subject} {predicate_h} {obj}",
|
|
RhetoricalMove.SEQUENCE: "next, {subject} {predicate_h} {obj}",
|
|
RhetoricalMove.CORRECT: "correction: {subject} {predicate_h} {obj}",
|
|
}
|
|
|
|
|
|
def _inflect_predicate(
|
|
predicate_h: str,
|
|
*,
|
|
negated: bool = False,
|
|
tense: str | None = None,
|
|
aspect: str | None = None,
|
|
plural_subject: bool = False,
|
|
) -> str:
|
|
"""Apply tense/aspect/negation to a humanized predicate.
|
|
|
|
When ``plural_subject`` is true, the conjugation uses plural
|
|
agreement (do not / have / are / bare-base verb in present) so
|
|
surfaces like "all molecules bind enzyme" come out correctly
|
|
instead of "all molecule binds enzyme" (english_fluency_ood G2).
|
|
"""
|
|
verb = predicate_h
|
|
base = base_form(verb)
|
|
|
|
match (aspect, tense, negated, plural_subject):
|
|
case ("perfective", _, _, True):
|
|
return f"have {past_participle(verb)}"
|
|
case ("perfective", _, _, False):
|
|
return f"has {past_participle(verb)}"
|
|
case ("imperfective", _, _, True):
|
|
return f"are {present_participle(verb)}"
|
|
case ("imperfective", _, _, False):
|
|
return f"is {present_participle(verb)}"
|
|
case (_, "past", True, _):
|
|
return f"did not {base}"
|
|
case (_, "past", False, _):
|
|
return past_tense(verb)
|
|
case (_, "future", True, _):
|
|
return f"will not {base}"
|
|
case (_, "future", False, _):
|
|
return f"will {base}"
|
|
case (_, _, True, True):
|
|
return f"do not {base}"
|
|
case (_, _, True, False):
|
|
return f"does not {base}"
|
|
case (_, _, False, True):
|
|
return base
|
|
case _:
|
|
return verb
|
|
|
|
|
|
def render_step(
|
|
move: RhetoricalMove,
|
|
subject: str,
|
|
predicate: str,
|
|
obj: str,
|
|
*,
|
|
negated: bool = False,
|
|
quantifier: str | None = None,
|
|
tense: str | None = None,
|
|
aspect: str | None = None,
|
|
) -> str:
|
|
"""Render a single articulation step into a surface fragment."""
|
|
template = _MOVE_TEMPLATES[move]
|
|
# Mass nouns under a quantifier stay singular ("all evidence
|
|
# supports", not "all evidences support"). Count nouns
|
|
# pluralise and the verb de-conjugates ("all molecules bind").
|
|
plural_q = quantifier is not None and quantifier.lower() in _PLURAL_QUANTIFIERS
|
|
is_mass = is_mass_noun(subject)
|
|
plural = plural_q and not is_mass
|
|
predicate_h = _humanize_predicate(predicate)
|
|
predicate_h = _inflect_predicate(
|
|
predicate_h,
|
|
negated=negated, tense=tense, aspect=aspect,
|
|
plural_subject=plural,
|
|
)
|
|
obj_display = obj if obj != "<pending>" else "..."
|
|
subject_form = pluralize(subject) if plural else subject
|
|
subject_display = f"{quantifier} {subject_form}" if quantifier else subject_form
|
|
return template.format(
|
|
subject=subject_display,
|
|
predicate_h=predicate_h,
|
|
obj=obj_display,
|
|
)
|