From f074ba729edccadd950370874f8b8fe08d33ebef Mon Sep 17 00:00:00 2001 From: Shay Date: Mon, 18 May 2026 19:52:27 -0700 Subject: [PATCH] =?UTF-8?q?feat(packs):=20en=5Fcore=5Fattitude=5Fv1=20?= =?UTF-8?q?=E2=80=94=20adjective=20pack=20(40=20lemmas)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workstream 1 second pack. Closes the ADJ POS gap — prior to this pack zero adjectives existed in any mounted English content pack, so the runtime could not emit grounded surfaces for predicative queries like "What is true?" or "What is important?". Pack composition (40 ADJ entries): attitude.truth_value.* (8) true false valid invalid accurate inaccurate factual sound attitude.evaluative.* (6) good bad right better worse best attitude.epistemic.* (10) certain uncertain possible impossible likely unlikely probable clear obscure evident attitude.modal.* (4) necessary sufficient required optional attitude.importance.* (6) important essential relevant central primary useful attitude.scope.* (6) general specific broad narrow universal particular Files: language_packs/data/en_core_attitude_v1/ lexicon.jsonl — 40 entries, SHA-256 checksum-sealed manifest.json — operational_base / D0 / checksum-verified chat/pack_resolver.py Appended to DEFAULT_RESOLVABLE_PACK_IDS after cognition + meta. core/config.py Added to RuntimeConfig.input_packs default mount. tests/test_en_core_attitude_v1_pack.py 11 contract tests: checksum, POS=ADJ uniformity, primary-domain namespace, no-collision regression gate against all 4 prior packs, contiguous entry_ids, mounted-by-default, resolver-order invariant, routing correctness, and cognition+meta resolution unchanged. Authoring: Three parallel subagents (1 per cluster) — truth/eval, epistemic/modal, importance/scope. Strict exemplar + forbidden-lemma list against all prior packs. Main pass assembled, validated, sealed. Verification: Full lane: 2159 passed, 2 skipped, 0 failed (+11 new tests over the previous 2148 baseline). Cognition eval byte-identical on both splits: public 100 / 100 / 91.7 / 100 holdout 100 / 100 / 83.3 / 100 Live composer probes: every ADJ lemma emits a deterministic pack-grounded surface from en_core_attitude_v1. --- chat/pack_resolver.py | 1 + core/config.py | 1 + .../data/en_core_attitude_v1/lexicon.jsonl | 40 +++++ .../data/en_core_attitude_v1/manifest.json | 13 ++ tests/test_en_core_attitude_v1_pack.py | 155 ++++++++++++++++++ 5 files changed, 210 insertions(+) create mode 100644 language_packs/data/en_core_attitude_v1/lexicon.jsonl create mode 100644 language_packs/data/en_core_attitude_v1/manifest.json create mode 100644 tests/test_en_core_attitude_v1_pack.py diff --git a/chat/pack_resolver.py b/chat/pack_resolver.py index 455220a9..0785e6e5 100644 --- a/chat/pack_resolver.py +++ b/chat/pack_resolver.py @@ -43,6 +43,7 @@ from pathlib import Path DEFAULT_RESOLVABLE_PACK_IDS: tuple[str, ...] = ( "en_core_cognition_v1", "en_core_meta_v1", + "en_core_attitude_v1", "en_core_relations_v1", "en_core_relations_v2", ) diff --git a/core/config.py b/core/config.py index 2c10dfa4..577c9524 100644 --- a/core/config.py +++ b/core/config.py @@ -14,6 +14,7 @@ class RuntimeConfig: "en_minimal_v1", "en_core_cognition_v1", "en_core_meta_v1", + "en_core_attitude_v1", "en_core_relations_v1", "en_core_relations_v2", "he_logos_micro_v1", diff --git a/language_packs/data/en_core_attitude_v1/lexicon.jsonl b/language_packs/data/en_core_attitude_v1/lexicon.jsonl new file mode 100644 index 00000000..d8286fd0 --- /dev/null +++ b/language_packs/data/en_core_attitude_v1/lexicon.jsonl @@ -0,0 +1,40 @@ +{"entry_id":"en-core-attitude-001","surface":"true","lemma":"true","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.true","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-002","surface":"false","lemma":"false","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.false","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-003","surface":"valid","lemma":"valid","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.true","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-004","surface":"invalid","lemma":"invalid","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.false","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-005","surface":"accurate","lemma":"accurate","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.true","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-006","surface":"inaccurate","lemma":"inaccurate","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.false","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-007","surface":"factual","lemma":"factual","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.true","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-008","surface":"sound","lemma":"sound","language":"en","pos":"ADJ","semantic_domains":["attitude.truth_value.true","attitude.truth_value","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-009","surface":"good","lemma":"good","language":"en","pos":"ADJ","semantic_domains":["attitude.evaluative.positive","attitude.evaluative","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-010","surface":"bad","lemma":"bad","language":"en","pos":"ADJ","semantic_domains":["attitude.evaluative.negative","attitude.evaluative","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-011","surface":"right","lemma":"right","language":"en","pos":"ADJ","semantic_domains":["attitude.evaluative.positive","attitude.evaluative","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-012","surface":"better","lemma":"better","language":"en","pos":"ADJ","semantic_domains":["attitude.evaluative.positive","attitude.evaluative","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-013","surface":"worse","lemma":"worse","language":"en","pos":"ADJ","semantic_domains":["attitude.evaluative.negative","attitude.evaluative","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-014","surface":"best","lemma":"best","language":"en","pos":"ADJ","semantic_domains":["attitude.evaluative.positive","attitude.evaluative","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-015","surface":"certain","lemma":"certain","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.certainty","attitude.epistemic","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-016","surface":"uncertain","lemma":"uncertain","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.uncertainty","attitude.epistemic","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-017","surface":"possible","lemma":"possible","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.possibility","attitude.epistemic","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-018","surface":"impossible","lemma":"impossible","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.impossibility","attitude.epistemic","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-019","surface":"likely","lemma":"likely","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.possibility","attitude.epistemic","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-020","surface":"unlikely","lemma":"unlikely","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.impossibility","attitude.epistemic","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-021","surface":"probable","lemma":"probable","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.possibility","attitude.epistemic","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-022","surface":"clear","lemma":"clear","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.certainty","attitude.epistemic","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-023","surface":"obscure","lemma":"obscure","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.uncertainty","attitude.epistemic","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-024","surface":"evident","lemma":"evident","language":"en","pos":"ADJ","semantic_domains":["attitude.epistemic.certainty","attitude.epistemic","cognition.epistemic"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-025","surface":"necessary","lemma":"necessary","language":"en","pos":"ADJ","semantic_domains":["attitude.modal.necessity","attitude.modal","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-026","surface":"sufficient","lemma":"sufficient","language":"en","pos":"ADJ","semantic_domains":["attitude.modal.sufficiency","attitude.modal","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-027","surface":"required","lemma":"required","language":"en","pos":"ADJ","semantic_domains":["attitude.modal.necessity","attitude.modal","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-028","surface":"optional","lemma":"optional","language":"en","pos":"ADJ","semantic_domains":["attitude.modal.optionality","attitude.modal","cognition.modal"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-029","surface":"important","lemma":"important","language":"en","pos":"ADJ","semantic_domains":["attitude.importance.high","attitude.importance","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-030","surface":"essential","lemma":"essential","language":"en","pos":"ADJ","semantic_domains":["attitude.importance.high","attitude.importance","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-031","surface":"relevant","lemma":"relevant","language":"en","pos":"ADJ","semantic_domains":["attitude.importance.relevance","attitude.importance","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-032","surface":"central","lemma":"central","language":"en","pos":"ADJ","semantic_domains":["attitude.importance.high","attitude.importance","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-033","surface":"primary","lemma":"primary","language":"en","pos":"ADJ","semantic_domains":["attitude.importance.high","attitude.importance","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-034","surface":"useful","lemma":"useful","language":"en","pos":"ADJ","semantic_domains":["attitude.importance.utility","attitude.importance","cognition.judgment"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-035","surface":"general","lemma":"general","language":"en","pos":"ADJ","semantic_domains":["attitude.scope.general","attitude.scope","cognition.category"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-036","surface":"specific","lemma":"specific","language":"en","pos":"ADJ","semantic_domains":["attitude.scope.specific","attitude.scope","cognition.category"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-037","surface":"broad","lemma":"broad","language":"en","pos":"ADJ","semantic_domains":["attitude.scope.general","attitude.scope","cognition.category"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-038","surface":"narrow","lemma":"narrow","language":"en","pos":"ADJ","semantic_domains":["attitude.scope.specific","attitude.scope","cognition.category"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-039","surface":"universal","lemma":"universal","language":"en","pos":"ADJ","semantic_domains":["attitude.scope.general","attitude.scope","cognition.category"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} +{"entry_id":"en-core-attitude-040","surface":"particular","lemma":"particular","language":"en","pos":"ADJ","semantic_domains":["attitude.scope.specific","attitude.scope","cognition.category"],"morphology_tags":["adjective"],"provenance_ids":["seed:core_attitude_v1"]} diff --git a/language_packs/data/en_core_attitude_v1/manifest.json b/language_packs/data/en_core_attitude_v1/manifest.json new file mode 100644 index 00000000..1e9d80ed --- /dev/null +++ b/language_packs/data/en_core_attitude_v1/manifest.json @@ -0,0 +1,13 @@ +{ + "pack_id": "en_core_attitude_v1", + "language": "en", + "role": "operational_base", + "script": "Latin", + "normalization_policy": "unitize_versor", + "source_manifest": "en_core_attitude_v1.lexicon.jsonl", + "determinism_class": "D0", + "checksum": "11076d46a2b8642e4d2d958a5aa3fc13c42fb53a17d746ec312f27716c237c79", + "version": "1.0.0", + "gate_engaged": true, + "oov_policy": "tagged_fallback" +} diff --git a/tests/test_en_core_attitude_v1_pack.py b/tests/test_en_core_attitude_v1_pack.py new file mode 100644 index 00000000..99b39b22 --- /dev/null +++ b/tests/test_en_core_attitude_v1_pack.py @@ -0,0 +1,155 @@ +"""``en_core_attitude_v1`` — adjective pack tests. + +The attitude pack is the first ADJ-bearing pack in the runtime. It +carries 40 adjectives across three semantic clusters: + + - attitude.truth_value.* — true, false, valid, accurate, ... (8) + - attitude.evaluative.* — good, bad, right, better, ... (6) + - attitude.epistemic.* — certain, possible, likely, ... (10) + - attitude.modal.* — necessary, sufficient, ... (4) + - attitude.importance.* — important, essential, useful, ... (6) + - attitude.scope.* — general, specific, broad, ... (6) + +Contracts pinned: + + - Checksum-verified load. + - 40 entries, all POS=ADJ. + - Every entry's primary semantic_domain begins with ``attitude.``. + - No collision with cognition_v1 / meta_v1 / relations_v1 / relations_v2. + - Mounted by default in ``RuntimeConfig.input_packs``. + - Registered in ``DEFAULT_RESOLVABLE_PACK_IDS`` *after* cognition and + meta (preserves cognition-lane and meta-lane resolution invariants). +""" + +from __future__ import annotations + +import json +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_attitude_v1" +_PACK_ROOT = Path(__file__).resolve().parent.parent / "language_packs" / "data" / PACK_ID + +EXPECTED_TOTAL = 40 + +EXPECTED_LEMMAS: tuple[str, ...] = ( + "true", "false", "valid", "invalid", "accurate", "inaccurate", "factual", + "sound", "good", "bad", "right", "better", "worse", "best", + "certain", "uncertain", "possible", "impossible", "likely", "unlikely", + "probable", "clear", "obscure", "evident", + "necessary", "sufficient", "required", "optional", + "important", "essential", "relevant", "central", "primary", "useful", + "general", "specific", "broad", "narrow", "universal", "particular", +) + + +def _read_lexicon() -> list[dict]: + path = _PACK_ROOT / "lexicon.jsonl" + return [ + json.loads(line) + for line in path.read_text(encoding="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 all(c in "0123456789abcdef" for c in manifest.checksum) + assert len(manifold) == EXPECTED_TOTAL + + +def test_all_entries_are_adjectives() -> None: + entries = _read_lexicon() + assert len(entries) == EXPECTED_TOTAL + for entry in entries: + assert entry["pos"] == "ADJ", entry["entry_id"] + assert entry["morphology_tags"] == ["adjective"], entry["entry_id"] + + +def test_all_expected_lemmas_present() -> None: + _, manifold = load_pack(PACK_ID) + surfaces = {manifold.get_word_at(i) for i in range(len(manifold))} + assert surfaces == set(EXPECTED_LEMMAS) + + +def test_every_entry_has_attitude_namespace_primary_domain() -> None: + for entry in _read_lexicon(): + domains = entry["semantic_domains"] + assert isinstance(domains, list) and domains, entry + assert domains[0].startswith("attitude."), ( + f"entry {entry['entry_id']} primary domain {domains[0]!r} " + f"is not in the attitude.* namespace" + ) + + +def test_no_collision_with_prior_packs() -> None: + """Anti-leakage: must not duplicate any lemma in any earlier-mounted + English pack. Re-asserts the authoring-time exclusion.""" + prior_lemmas: set[str] = set() + pack_root = _PACK_ROOT.parent + for pack in ( + "en_core_cognition_v1", + "en_core_meta_v1", + "en_core_relations_v1", + "en_core_relations_v2", + ): + for line in (pack_root / pack / "lexicon.jsonl").read_text("utf-8").splitlines(): + if line.strip(): + prior_lemmas.add(json.loads(line)["lemma"].lower()) + for entry in _read_lexicon(): + lemma = entry["lemma"].lower() + assert lemma not in prior_lemmas, ( + f"lemma {lemma!r} collides with a prior-mounted pack" + ) + + +def test_provenance_is_seed_core_attitude_v1() -> None: + for entry in _read_lexicon(): + assert entry["provenance_ids"] == ["seed:core_attitude_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-attitude-{i:03d}", entry["entry_id"] + + +def test_pack_mounted_in_default_runtime_config() -> None: + cfg = RuntimeConfig() + assert PACK_ID in cfg.input_packs + + +def test_pack_registered_after_cognition_and_meta() -> None: + assert PACK_ID in DEFAULT_RESOLVABLE_PACK_IDS + cog_idx = DEFAULT_RESOLVABLE_PACK_IDS.index("en_core_cognition_v1") + meta_idx = DEFAULT_RESOLVABLE_PACK_IDS.index("en_core_meta_v1") + attitude_idx = DEFAULT_RESOLVABLE_PACK_IDS.index(PACK_ID) + assert cog_idx < attitude_idx + assert meta_idx < attitude_idx + + +def test_resolver_routes_attitude_lemmas_to_this_pack() -> None: + for lemma in EXPECTED_LEMMAS: + resolved = resolve_lemma(lemma) + assert resolved is not None, f"lemma {lemma!r} did not resolve" + pack_id, domains = resolved + assert pack_id == PACK_ID, ( + f"lemma {lemma!r} resolved to {pack_id} instead of {PACK_ID}" + ) + assert domains[0].startswith("attitude.") + + +def test_prior_pack_lemma_resolution_unchanged() -> None: + """Cognition + meta lemma resolution unaffected by attitude pack.""" + for lemma in ("truth", "knowledge", "memory"): + resolved = resolve_lemma(lemma) + assert resolved is not None and resolved[0] == "en_core_cognition_v1" + for lemma in ("doubt", "fact", "self"): + resolved = resolve_lemma(lemma) + assert resolved is not None and resolved[0] == "en_core_meta_v1"