From e72e946c0b4f411ad4ceb7af04d4d15c84623c10 Mon Sep 17 00:00:00 2001 From: Shay Date: Mon, 18 May 2026 22:00:08 -0700 Subject: [PATCH] =?UTF-8?q?feat(packs):=20en=5Fcore=5Fcausation=5Fv1=20?= =?UTF-8?q?=E2=80=94=20causation=20vocabulary=20(15=20lemmas)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workstream 1 seventh pack. Extends the causal apparatus beyond cognition_v1's ``cause`` (NOUN+VERB) and ``because`` (SCONJ). Pack composition (15 entries — 6 NOUN / 6 VERB / 3 ADJ): causation.effect.* effect result consequence outcome impact influence causation.verb.* trigger induce yield enable prevent drive causation.adjective.* causal resultant consequent ``cause`` was deliberately retained in en_core_cognition_v1. Test pins the invariant. Verification: Cognition eval byte-identical (100/100/91.7/100 public, 100/100/83.3/100 holdout). --- chat/pack_resolver.py | 1 + core/config.py | 1 + .../data/en_core_causation_v1/lexicon.jsonl | 15 ++ .../data/en_core_causation_v1/manifest.json | 13 ++ tests/test_en_core_causation_v1_pack.py | 129 ++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 language_packs/data/en_core_causation_v1/lexicon.jsonl create mode 100644 language_packs/data/en_core_causation_v1/manifest.json create mode 100644 tests/test_en_core_causation_v1_pack.py diff --git a/chat/pack_resolver.py b/chat/pack_resolver.py index 388ecfbf..73efa3da 100644 --- a/chat/pack_resolver.py +++ b/chat/pack_resolver.py @@ -48,6 +48,7 @@ DEFAULT_RESOLVABLE_PACK_IDS: tuple[str, ...] = ( "en_core_action_v1", "en_core_quantitative_v1", "en_core_spatial_v1", + "en_core_causation_v1", "en_core_relations_v1", "en_core_relations_v2", ) diff --git a/core/config.py b/core/config.py index 33acb5cb..775a1420 100644 --- a/core/config.py +++ b/core/config.py @@ -19,6 +19,7 @@ class RuntimeConfig: "en_core_action_v1", "en_core_quantitative_v1", "en_core_spatial_v1", + "en_core_causation_v1", "en_core_relations_v1", "en_core_relations_v2", "he_logos_micro_v1", diff --git a/language_packs/data/en_core_causation_v1/lexicon.jsonl b/language_packs/data/en_core_causation_v1/lexicon.jsonl new file mode 100644 index 00000000..dfdfff38 --- /dev/null +++ b/language_packs/data/en_core_causation_v1/lexicon.jsonl @@ -0,0 +1,15 @@ +{"entry_id":"en-core-causation-001","surface":"effect","lemma":"effect","language":"en","pos":"NOUN","semantic_domains":["causation.effect.outcome","causation.effect","cognition.causal"],"morphology_tags":["noun"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-002","surface":"result","lemma":"result","language":"en","pos":"NOUN","semantic_domains":["causation.effect.outcome","causation.effect","cognition.causal"],"morphology_tags":["noun"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-003","surface":"consequence","lemma":"consequence","language":"en","pos":"NOUN","semantic_domains":["causation.effect.consequence","causation.effect","cognition.causal"],"morphology_tags":["noun"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-004","surface":"outcome","lemma":"outcome","language":"en","pos":"NOUN","semantic_domains":["causation.effect.outcome","causation.effect","cognition.causal"],"morphology_tags":["noun"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-005","surface":"impact","lemma":"impact","language":"en","pos":"NOUN","semantic_domains":["causation.effect.impact","causation.effect","cognition.causal"],"morphology_tags":["noun"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-006","surface":"influence","lemma":"influence","language":"en","pos":"NOUN","semantic_domains":["causation.effect.influence","causation.effect","cognition.causal"],"morphology_tags":["noun"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-007","surface":"trigger","lemma":"trigger","language":"en","pos":"VERB","semantic_domains":["causation.verb.initiate","causation.verb","cognition.causal"],"morphology_tags":["verb"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-008","surface":"induce","lemma":"induce","language":"en","pos":"VERB","semantic_domains":["causation.verb.initiate","causation.verb","cognition.causal"],"morphology_tags":["verb"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-009","surface":"yield","lemma":"yield","language":"en","pos":"VERB","semantic_domains":["causation.verb.produce_result","causation.verb","cognition.causal"],"morphology_tags":["verb"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-010","surface":"enable","lemma":"enable","language":"en","pos":"VERB","semantic_domains":["causation.verb.permit","causation.verb","cognition.causal"],"morphology_tags":["verb"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-011","surface":"prevent","lemma":"prevent","language":"en","pos":"VERB","semantic_domains":["causation.verb.block","causation.verb","cognition.causal"],"morphology_tags":["verb"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-012","surface":"drive","lemma":"drive","language":"en","pos":"VERB","semantic_domains":["causation.verb.produce_result","causation.verb","cognition.causal"],"morphology_tags":["verb"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-013","surface":"causal","lemma":"causal","language":"en","pos":"ADJ","semantic_domains":["causation.adjective.causal","causation.adjective","cognition.causal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-014","surface":"resultant","lemma":"resultant","language":"en","pos":"ADJ","semantic_domains":["causation.adjective.resultant","causation.adjective","cognition.causal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_causation_v1"]} +{"entry_id":"en-core-causation-015","surface":"consequent","lemma":"consequent","language":"en","pos":"ADJ","semantic_domains":["causation.adjective.resultant","causation.adjective","cognition.causal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_causation_v1"]} diff --git a/language_packs/data/en_core_causation_v1/manifest.json b/language_packs/data/en_core_causation_v1/manifest.json new file mode 100644 index 00000000..ebd2fb64 --- /dev/null +++ b/language_packs/data/en_core_causation_v1/manifest.json @@ -0,0 +1,13 @@ +{ + "pack_id": "en_core_causation_v1", + "language": "en", + "role": "operational_base", + "script": "Latin", + "normalization_policy": "unitize_versor", + "source_manifest": "en_core_causation_v1.lexicon.jsonl", + "determinism_class": "D0", + "checksum": "0e173445c84c26a92546bba9243bcd42f33a516b9f9f660a2d13d25ed07df741", + "version": "1.0.0", + "gate_engaged": true, + "oov_policy": "tagged_fallback" +} diff --git a/tests/test_en_core_causation_v1_pack.py b/tests/test_en_core_causation_v1_pack.py new file mode 100644 index 00000000..9cd3020d --- /dev/null +++ b/tests/test_en_core_causation_v1_pack.py @@ -0,0 +1,129 @@ +"""``en_core_causation_v1`` — causation vocabulary pack tests. + +Cognition pack already owns ``cause``/``because``; this pack extends +the causal apparatus with effect nouns, causative verbs not in +en_core_action_v1, and causal adjectives. 15 entries: + + - causation.effect.* effect result consequence outcome impact influence (6 NOUN) + - causation.verb.* trigger induce yield enable prevent drive (6 VERB) + - causation.adjective.* causal resultant consequent (3 ADJ) +""" + +from __future__ import annotations + +import json +from collections import Counter +from pathlib import Path + +from chat.pack_resolver import DEFAULT_RESOLVABLE_PACK_IDS, resolve_lemma +from core.config import RuntimeConfig +from language_packs.compiler import load_pack + + +PACK_ID = "en_core_causation_v1" +_PACK_ROOT = Path(__file__).resolve().parent.parent / "language_packs" / "data" / PACK_ID + +EXPECTED_TOTAL = 15 +EXPECTED_POS_COUNTS = {"NOUN": 6, "VERB": 6, "ADJ": 3} + +EXPECTED_LEMMAS: tuple[str, ...] = ( + "effect", "result", "consequence", "outcome", "impact", "influence", + "trigger", "induce", "yield", "enable", "prevent", "drive", + "causal", "resultant", "consequent", +) + + +def _read_lexicon() -> list[dict]: + return [ + json.loads(line) + for line in (_PACK_ROOT / "lexicon.jsonl").read_text("utf-8").splitlines() + if line.strip() + ] + + +def test_pack_loads_with_matching_checksum() -> None: + manifest, manifold = load_pack(PACK_ID) + assert manifest.pack_id == PACK_ID + assert len(manifest.checksum) == 64 + assert len(manifold) == EXPECTED_TOTAL + + +def test_pos_distribution_matches_design() -> None: + assert dict(Counter(e["pos"] for e in _read_lexicon())) == EXPECTED_POS_COUNTS + + +def test_all_expected_lemmas_present() -> None: + _, manifold = load_pack(PACK_ID) + assert {manifold.get_word_at(i) for i in range(len(manifold))} == set(EXPECTED_LEMMAS) + + +def test_every_entry_has_causation_namespace_primary_domain() -> None: + for entry in _read_lexicon(): + assert entry["semantic_domains"][0].startswith("causation."), entry + + +def test_no_collision_with_prior_packs() -> None: + prior: set[str] = set() + for pack in ( + "en_core_cognition_v1", "en_core_meta_v1", "en_core_attitude_v1", + "en_core_temporal_v1", "en_core_action_v1", "en_core_quantitative_v1", + "en_core_spatial_v1", "en_core_relations_v1", "en_core_relations_v2", + ): + for line in (_PACK_ROOT.parent / pack / "lexicon.jsonl").read_text("utf-8").splitlines(): + if line.strip(): + prior.add(json.loads(line)["lemma"].lower()) + for entry in _read_lexicon(): + assert entry["lemma"].lower() not in prior, entry + + +def test_provenance_is_seed_core_causation_v1() -> None: + for entry in _read_lexicon(): + assert entry["provenance_ids"] == ["seed:core_causation_v1"], entry + + +def test_entry_ids_contiguous_and_zero_padded() -> None: + entries = sorted(_read_lexicon(), key=lambda d: d["entry_id"]) + for i, entry in enumerate(entries, start=1): + assert entry["entry_id"] == f"en-core-causation-{i:03d}", entry["entry_id"] + + +def test_pack_mounted_in_default_runtime_config() -> None: + assert PACK_ID in RuntimeConfig().input_packs + + +def test_pack_registered_after_prior_content_packs() -> None: + assert PACK_ID in DEFAULT_RESOLVABLE_PACK_IDS + for earlier in ( + "en_core_cognition_v1", "en_core_meta_v1", "en_core_attitude_v1", + "en_core_temporal_v1", "en_core_action_v1", "en_core_quantitative_v1", + "en_core_spatial_v1", + ): + assert DEFAULT_RESOLVABLE_PACK_IDS.index(earlier) < DEFAULT_RESOLVABLE_PACK_IDS.index(PACK_ID) + + +def test_resolver_routes_causation_lemmas_to_this_pack() -> None: + for lemma in EXPECTED_LEMMAS: + resolved = resolve_lemma(lemma) + assert resolved is not None and resolved[0] == PACK_ID + assert resolved[1][0].startswith("causation.") + + +def test_cause_remains_in_cognition_v1() -> None: + """``cause`` was deliberately excluded — en_core_cognition_v1 + already owns it (both NOUN and VERB).""" + resolved = resolve_lemma("cause") + assert resolved is not None and resolved[0] == "en_core_cognition_v1" + + +def test_prior_pack_lemma_resolution_unchanged() -> None: + for lemma, expected in ( + ("truth", "en_core_cognition_v1"), + ("doubt", "en_core_meta_v1"), + ("true", "en_core_attitude_v1"), + ("now", "en_core_temporal_v1"), + ("do", "en_core_action_v1"), + ("all", "en_core_quantitative_v1"), + ("here", "en_core_spatial_v1"), + ): + resolved = resolve_lemma(lemma) + assert resolved is not None and resolved[0] == expected