content(en_core_relations_v1): +14 kinship/social lemmas + +14 chains, cognition eval byte-identical, ratify-idempotent (#211)
* content(packs): update relations checksum
* revert transient relations manifest checksum
* content(packs): extend relations lexicon additively
* content(teaching): extend relations chains additively
* content(packs): ratify relations manifest checksum
* test(packs): accept additive relations lemma extension
* test(packs): add relations v1 extension regressions
* fix(tests): align relations extension lemma set
* content(packs): add relations mastery report
* content(packs): drop unused .mastery_report.json sidecar
Language packs do not consume mastery reports — the pattern is from
identity packs (packs/identity/) and has no consumer in language_packs/
loader.py or compiler.py. The added sidecar's self-seal hash also did
not validate against sha256(json.dumps(body, sort_keys=True,
separators=(',', ':'))).
Drop the file. The actual ratification surface for this pack is the
manifest.json lexicon_checksum, which still matches lexicon.jsonl
bytes (verified).
This commit is contained in:
parent
0685c87e54
commit
6e072f95be
5 changed files with 181 additions and 10 deletions
|
|
@ -6,3 +6,17 @@
|
|||
{"entry_id":"en-core-rel-006","surface":"descendant","lemma":"descendant","language":"en","pos":"NOUN","semantic_domains":["kinship.descendant.transitive","lineage.downward","kinship.successor"],"morphology_tags":["noun"],"provenance_ids":["seed:core_relations_v1"]}
|
||||
{"entry_id":"en-core-rel-007","surface":"spouse","lemma":"spouse","language":"en","pos":"NOUN","semantic_domains":["kinship.partner","kinship.lateral.affinal","social.marriage"],"morphology_tags":["noun"],"provenance_ids":["seed:core_relations_v1"]}
|
||||
{"entry_id":"en-core-rel-008","surface":"offspring","lemma":"offspring","language":"en","pos":"NOUN","semantic_domains":["kinship.descendant.direct","biology.progeny","kinship.child"],"morphology_tags":["noun"],"provenance_ids":["seed:core_relations_v1"]}
|
||||
{"entry_id":"en-core-rel-009","surface":"advisor","lemma":"advisor","language":"en","pos":"NOUN","semantic_domains":["professional.guidance","social.counsel","relation.developmental"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-010","surface":"apprentice","lemma":"apprentice","language":"en","pos":"NOUN","semantic_domains":["professional.learner","social.student","relation.developmental"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-011","surface":"caregiver","lemma":"caregiver","language":"en","pos":"NOUN","semantic_domains":["social.care.role","kinship.support","relation.protective"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-012","surface":"colleague","lemma":"colleague","language":"en","pos":"NOUN","semantic_domains":["professional.peer","social.workplace","relation.cooperative"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-013","surface":"cousin","lemma":"cousin","language":"en","pos":"NOUN","semantic_domains":["kinship.lateral.extended","family.role","social.peer"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-014","surface":"elder","lemma":"elder","language":"en","pos":"NOUN","semantic_domains":["kinship.ascendant.social","social.seniority","relation.guidance"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-015","surface":"friend","lemma":"friend","language":"en","pos":"NOUN","semantic_domains":["social.bond.peer","social.trust","relation.voluntary"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-016","surface":"guardian","lemma":"guardian","language":"en","pos":"NOUN","semantic_domains":["social.protection.role","kinship.care","relation.responsible"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-017","surface":"manager","lemma":"manager","language":"en","pos":"NOUN","semantic_domains":["professional.authority","social.organization","relation.supervisory"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-018","surface":"mentor","lemma":"mentor","language":"en","pos":"NOUN","semantic_domains":["professional.guidance","social.teacher","relation.developmental"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-019","surface":"neighbor","lemma":"neighbor","language":"en","pos":"NOUN","semantic_domains":["social.proximity.local","social.community","relation.adjacent"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-020","surface":"relative","lemma":"relative","language":"en","pos":"NOUN","semantic_domains":["kinship.member.extended","family.role","relation.kin"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-021","surface":"supervisor","lemma":"supervisor","language":"en","pos":"NOUN","semantic_domains":["professional.authority","social.organization","relation.supervisory"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
{"entry_id":"en-core-rel-022","surface":"teammate","lemma":"teammate","language":"en","pos":"NOUN","semantic_domains":["professional.team.peer","social.cooperation","relation.shared_goal"],"morphology_tags":["noun"],"provenance_ids":["relations_extension_v1:reviewed:2026-05-23"]}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"normalization_policy": "unitize_versor",
|
||||
"source_manifest": "en_core_relations_v1.lexicon.jsonl",
|
||||
"determinism_class": "D0",
|
||||
"checksum": "1a013b464f6a0fad4ef96f9efaf1406c44729643e422c1f76a516099476e61d3",
|
||||
"checksum": "57859a47bab8d62fc4b8b2089eca2da707c4943ca8ac70af578b5b76d5751109",
|
||||
"version": "1.0.0",
|
||||
"gate_engaged": true,
|
||||
"oov_policy": "tagged_fallback",
|
||||
|
|
|
|||
|
|
@ -5,3 +5,17 @@
|
|||
{"chain_id":"cause_family_grounds_parent","subject":"family","intent":"cause","connective":"grounds","object":"parent","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0064:reviewed:2026-05-18:relations_seed_v1"}
|
||||
{"chain_id":"verification_child_requires_parent","subject":"child","intent":"verification","connective":"requires","object":"parent","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0064:reviewed:2026-05-18:relations_seed_v1"}
|
||||
{"chain_id":"verification_descendant_requires_ancestor","subject":"descendant","intent":"verification","connective":"requires","object":"ancestor","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0064:reviewed:2026-05-18:relations_seed_v1"}
|
||||
{"chain_id":"cause_advisor_precedes_apprentice","subject":"advisor","intent":"cause","connective":"precedes","object":"apprentice","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_apprentice_requires_advisor","subject":"apprentice","intent":"verification","connective":"requires","object":"advisor","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"cause_caregiver_supports_child","subject":"caregiver","intent":"cause","connective":"supports","object":"child","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_guardian_requires_child","subject":"guardian","intent":"verification","connective":"requires","object":"child","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"cause_colleague_supports_teammate","subject":"colleague","intent":"cause","connective":"supports","object":"teammate","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_teammate_requires_colleague","subject":"teammate","intent":"verification","connective":"requires","object":"colleague","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"cause_cousin_belongs_to_family","subject":"cousin","intent":"cause","connective":"belongs_to","object":"family","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_relative_requires_family","subject":"relative","intent":"verification","connective":"requires","object":"family","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"cause_elder_precedes_descendant","subject":"elder","intent":"cause","connective":"precedes","object":"descendant","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_friend_requires_trust","subject":"friend","intent":"verification","connective":"requires","object":"colleague","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"cause_manager_precedes_supervisor","subject":"manager","intent":"cause","connective":"precedes","object":"supervisor","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_supervisor_requires_manager","subject":"supervisor","intent":"verification","connective":"requires","object":"manager","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"cause_mentor_precedes_apprentice","subject":"mentor","intent":"cause","connective":"precedes","object":"apprentice","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
{"chain_id":"verification_neighbor_requires_family","subject":"neighbor","intent":"verification","connective":"requires","object":"family","domains_subject_k":2,"domains_object_k":1,"provenance":"relations_extension_v1:reviewed:2026-05-23"}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Contract tests for the ``en_core_relations_v1`` kinship starter pack.
|
||||
"""Contract tests for the ``en_core_relations_v1`` relations starter pack.
|
||||
|
||||
Following the teaching-order doctrine
|
||||
([`docs/teaching_order.md`](../docs/teaching_order.md) §5),
|
||||
|
|
@ -8,7 +8,7 @@ domain whose triples exercise every formation gate end-to-end.
|
|||
As of ADR-0063 (cross-pack surface resolver), this pack IS in the
|
||||
default ``RuntimeConfig.input_packs``. Pack-grounded surface
|
||||
composers in :mod:`chat.pack_grounding` consult
|
||||
:mod:`chat.pack_resolver` so kinship lemmas ground on the live path
|
||||
:mod:`chat.pack_resolver` so relations lemmas ground on the live path
|
||||
without a separate composer module.
|
||||
|
||||
These tests pin:
|
||||
|
|
@ -16,10 +16,9 @@ These tests pin:
|
|||
- The pack loads via ``load_pack("en_core_relations_v1")`` without
|
||||
a checksum mismatch (manifest.checksum matches the bytes on
|
||||
disk per CLAUDE.md's pack-discipline).
|
||||
- The 8 kinship lemmas are all present.
|
||||
- Each lemma carries the expected canonical semantic_domains
|
||||
(deterministic taxonomy under ``kinship.*``, ``lineage.*``,
|
||||
``biology.*``, ``social.*``).
|
||||
- The original 8 kinship lemmas are all still present.
|
||||
- Each original lemma carries the expected canonical primary
|
||||
semantic domain.
|
||||
- No accidental cross-pack lemma collision with
|
||||
``en_core_cognition_v1`` (orthogonal domains; deliberate
|
||||
separation per teaching_order.md).
|
||||
|
|
@ -32,7 +31,7 @@ from language_packs.compiler import load_pack, load_pack_entries
|
|||
|
||||
PACK_ID = "en_core_relations_v1"
|
||||
|
||||
EXPECTED_LEMMAS: tuple[str, ...] = (
|
||||
BASELINE_LEMMAS: tuple[str, ...] = (
|
||||
"parent",
|
||||
"child",
|
||||
"sibling",
|
||||
|
|
@ -43,6 +42,25 @@ EXPECTED_LEMMAS: tuple[str, ...] = (
|
|||
"offspring",
|
||||
)
|
||||
|
||||
EXPECTED_ADDED_LEMMAS: tuple[str, ...] = (
|
||||
"advisor",
|
||||
"apprentice",
|
||||
"caregiver",
|
||||
"colleague",
|
||||
"cousin",
|
||||
"elder",
|
||||
"friend",
|
||||
"guardian",
|
||||
"manager",
|
||||
"mentor",
|
||||
"neighbor",
|
||||
"relative",
|
||||
"supervisor",
|
||||
"teammate",
|
||||
)
|
||||
|
||||
EXPECTED_LEMMAS: tuple[str, ...] = BASELINE_LEMMAS + EXPECTED_ADDED_LEMMAS
|
||||
|
||||
# Note: `person` is intentionally NOT in this pack — it lives in
|
||||
# `en_core_cognition_v1` and the orthogonality test below pins
|
||||
# that boundary.
|
||||
|
|
@ -76,9 +94,22 @@ def test_all_expected_lemmas_present() -> None:
|
|||
assert surfaces == set(EXPECTED_LEMMAS)
|
||||
|
||||
|
||||
def test_each_lemma_carries_expected_primary_domain() -> None:
|
||||
def test_baseline_lemmas_still_present() -> None:
|
||||
_, manifold = load_pack(PACK_ID)
|
||||
surfaces = {manifold.get_word_at(i) for i in range(len(manifold))}
|
||||
assert set(BASELINE_LEMMAS) <= surfaces
|
||||
|
||||
|
||||
def test_added_lemmas_present() -> None:
|
||||
_, manifold = load_pack(PACK_ID)
|
||||
surfaces = {manifold.get_word_at(i) for i in range(len(manifold))}
|
||||
assert set(EXPECTED_ADDED_LEMMAS) <= surfaces
|
||||
|
||||
|
||||
def test_each_baseline_lemma_carries_expected_primary_domain() -> None:
|
||||
"""The first ``semantic_domains`` entry is the load-bearing
|
||||
primary domain — drives mounted-pack resonance grouping."""
|
||||
primary domain — drives mounted-pack resonance grouping. Baseline
|
||||
primary domains are frozen; changing them requires an ADR."""
|
||||
entries = load_pack_entries(PACK_ID)
|
||||
by_lemma = {e.lemma: e for e in entries}
|
||||
for lemma, expected_primary in EXPECTED_PRIMARY_DOMAINS.items():
|
||||
|
|
|
|||
112
tests/test_relations_pack_v1_extensions.py
Normal file
112
tests/test_relations_pack_v1_extensions.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
"""Regression tests for additive ``en_core_relations_v1`` content extension.
|
||||
|
||||
This file deliberately tests only existing schemas and live resolver
|
||||
behavior. It does not introduce new pack types, new validators, or new
|
||||
manifest fields.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from chat.pack_resolver import resolve_lemma
|
||||
from chat.teaching_grounding import TeachingCorpusSpec, _load_corpus
|
||||
|
||||
|
||||
PACK_ID = "en_core_relations_v1"
|
||||
PACK_DIR = Path("language_packs/data") / PACK_ID
|
||||
LEXICON_PATH = PACK_DIR / "lexicon.jsonl"
|
||||
MANIFEST_PATH = PACK_DIR / "manifest.json"
|
||||
CHAINS_PATH = Path("teaching/relations_chains/relations_chains_v1.jsonl")
|
||||
|
||||
BASELINE_LEMMA_COUNT = 8
|
||||
EXPECTED_ADDED_LEMMAS = frozenset({
|
||||
"advisor",
|
||||
"apprentice",
|
||||
"caregiver",
|
||||
"colleague",
|
||||
"cousin",
|
||||
"elder",
|
||||
"friend",
|
||||
"guardian",
|
||||
"manager",
|
||||
"mentor",
|
||||
"neighbor",
|
||||
"relative",
|
||||
"supervisor",
|
||||
"teammate",
|
||||
})
|
||||
EXPECTED_TOTAL_LEMMA_COUNT = BASELINE_LEMMA_COUNT + len(EXPECTED_ADDED_LEMMAS)
|
||||
|
||||
BASELINE_CHAIN_COUNT = 7
|
||||
EXPECTED_ADDED_CHAIN_COUNT = 14
|
||||
EXPECTED_TOTAL_CHAIN_COUNT = BASELINE_CHAIN_COUNT + EXPECTED_ADDED_CHAIN_COUNT
|
||||
|
||||
|
||||
def _lexicon_entries() -> list[dict[str, object]]:
|
||||
return [
|
||||
json.loads(line)
|
||||
for line in LEXICON_PATH.read_text(encoding="utf-8").splitlines()
|
||||
if line.strip()
|
||||
]
|
||||
|
||||
|
||||
def _chain_entries() -> list[dict[str, object]]:
|
||||
return [
|
||||
json.loads(line)
|
||||
for line in CHAINS_PATH.read_text(encoding="utf-8").splitlines()
|
||||
if line.strip()
|
||||
]
|
||||
|
||||
|
||||
def test_relations_v1_entry_count_delta() -> None:
|
||||
entries = _lexicon_entries()
|
||||
assert len(entries) == EXPECTED_TOTAL_LEMMA_COUNT
|
||||
added = {
|
||||
str(e["lemma"])
|
||||
for e in entries
|
||||
if "relations_extension_v1:reviewed:2026-05-23" in e.get("provenance_ids", [])
|
||||
}
|
||||
assert added == EXPECTED_ADDED_LEMMAS
|
||||
|
||||
|
||||
def test_relations_v1_has_no_duplicate_lemma_or_surface_keys() -> None:
|
||||
entries = _lexicon_entries()
|
||||
lemmas = [str(e["lemma"]) for e in entries]
|
||||
surfaces = [str(e["surface"]) for e in entries]
|
||||
assert len(lemmas) == len(set(lemmas))
|
||||
assert len(surfaces) == len(set(surfaces))
|
||||
|
||||
|
||||
def test_manifest_checksum_matches_lexicon_bytes() -> None:
|
||||
manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||
actual = hashlib.sha256(LEXICON_PATH.read_bytes()).hexdigest()
|
||||
assert manifest["checksum"] == actual
|
||||
|
||||
|
||||
def test_all_added_lemmas_resolve_via_cross_pack_resolver() -> None:
|
||||
for lemma in sorted(EXPECTED_ADDED_LEMMAS):
|
||||
resolved = resolve_lemma(lemma)
|
||||
assert resolved is not None, lemma
|
||||
pack_id, domains = resolved
|
||||
assert pack_id == PACK_ID
|
||||
assert domains
|
||||
|
||||
|
||||
def test_relations_v1_chain_count_delta_and_loadability() -> None:
|
||||
chains = _chain_entries()
|
||||
assert len(chains) == EXPECTED_TOTAL_CHAIN_COUNT
|
||||
added = [
|
||||
c for c in chains
|
||||
if c.get("provenance") == "relations_extension_v1:reviewed:2026-05-23"
|
||||
]
|
||||
assert len(added) == EXPECTED_ADDED_CHAIN_COUNT
|
||||
|
||||
loaded = _load_corpus(TeachingCorpusSpec(
|
||||
corpus_id="relations_chains_v1",
|
||||
path=CHAINS_PATH,
|
||||
pack_id=PACK_ID,
|
||||
))
|
||||
assert len(loaded) == EXPECTED_TOTAL_CHAIN_COUNT
|
||||
Loading…
Reference in a new issue