The henxel gate flagged drift-ledger.py + lexicon_evolution.py as ungoverned new modules (run 19849). This pins their core semantics: coherence ladder, stem-aware grounding parking, promote verdict shapes, drift-ledger importlib load + check on a synthetic store. Fixture honesty held: the live oracle corrected three hand-labeled cases — in a Nick-centric corpus 'nick' is df-1 distinctive, so nick-bearing facts genuinely ground/relate; the novel-axis probes avoid it.
160 lines
6.4 KiB
Python
160 lines
6.4 KiB
Python
"""test_lexicon_contract.py — the recovered provenance modules ship with
|
|
coverage (henxel: new rune modules ship with tests or a canary).
|
|
|
|
lexicon_evolution.py + drift-ledger.py were recovered from ~/odysseus
|
|
(0308ccc6 / rework-product-api HEAD) in commit 5749029 — the contract here
|
|
pins their core semantics so a future regression cannot silently re-shape
|
|
the lexicon discipline or the drift gate.
|
|
|
|
CONTRACT (contract BUILDER-2026-10-07T16:26:19Z-71624c + 16:31:17Z-997c9d):
|
|
- coherence: conflict needs a DISTINCTIVE shared term AND a negation on it;
|
|
a shared common word is never a relation ("black coffee" vs "black holes")
|
|
- promote_eligible: evidence threshold AND (distinctive share OR novel axis
|
|
grounded in the corpus); inventions park
|
|
- drift-ledger: loads via the curator's importlib pattern and check() runs
|
|
"""
|
|
|
|
import importlib.util
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
sys.path.insert(0, str(ROOT / "src" / "rune"))
|
|
|
|
import lexicon_evolution as le # noqa: E402
|
|
|
|
|
|
CORPUS = [
|
|
"nick writes Go and Rust code daily in the rune repository",
|
|
"the curator gates memory writes through worthiness and grading",
|
|
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust",
|
|
]
|
|
|
|
|
|
class TestLexiconCoherence:
|
|
def test_conflict_needs_negation_on_distinctive_term(self):
|
|
assert (
|
|
le.coherence("nick never writes Rust code",
|
|
["nick writes Go and Rust code daily"], CORPUS)
|
|
== "conflict"
|
|
)
|
|
|
|
def test_shared_distinctive_without_negation_reinforces(self):
|
|
assert (
|
|
le.coherence("nick writes Rust code on weekends",
|
|
["nick writes Go and Rust code daily"], CORPUS)
|
|
== "consistent"
|
|
)
|
|
|
|
def test_nothing_shared_is_novel(self):
|
|
# Oracle-confirmed fixture: no distinctive term shared (in a
|
|
# Nick-centric corpus a "nick"-bearing fact WOULD share "nick" —
|
|
# df-1 distinctive there — so the novel-axis probe avoids it).
|
|
assert (
|
|
le.coherence("someone plays bass guitar in a band",
|
|
["nick writes Go and Rust code daily"], CORPUS)
|
|
== "novel"
|
|
)
|
|
|
|
def test_common_word_is_never_a_relation(self):
|
|
# "black" is common in the corpus — no distinctive share, so novel.
|
|
assert (
|
|
le.coherence("nick likes black coffee", ["black holes bend light"],
|
|
["black coffee in the morning", "black holes bend light"])
|
|
== "novel"
|
|
)
|
|
|
|
def test_empty_existing_is_novel(self):
|
|
assert le.coherence("nick writes Rust code daily", [], []) == "novel"
|
|
|
|
|
|
class TestLexiconGrounding:
|
|
def test_stem_aware_grounding(self):
|
|
# migrated / migration share the stem.
|
|
assert le.grounded("nick migrated the modules", CORPUS) is True
|
|
|
|
def test_invented_token_never_grounded(self):
|
|
# Oracle-confirmed fixture: a fact sharing NO corpus stem parks
|
|
# (framing words don't count; nothing here traces to the corpus).
|
|
assert (
|
|
le.grounded("someone collects vintage railway timetables", CORPUS)
|
|
is False
|
|
)
|
|
|
|
def test_empty_sources_not_grounded(self):
|
|
assert le.grounded("nick migrated the modules", []) is False
|
|
|
|
|
|
class TestPromoteEligible:
|
|
def test_strength_gate_parks_first(self):
|
|
r = le.promote_eligible("add support for retries",
|
|
["support for retries in the fetch loop"], 2, 3, ())
|
|
assert r == {"eligible": False, "reason": "evidence strength 2 < 3"}
|
|
|
|
def test_shared_distinctive_is_eligible(self):
|
|
r = le.promote_eligible("add retries to the fetch loop",
|
|
["support for retries in the fetch loop"], 3, 3, ())
|
|
assert r["eligible"] is True
|
|
assert "shares distinctive term(s)" in r["reason"]
|
|
|
|
def test_novel_axis_not_grounded_parks(self):
|
|
# Oracle-confirmed fixture: nothing in the proposal traces to the
|
|
# corpus (a "nick"-bearing fact WOULD ground — nick is df-1
|
|
# distinctive in this corpus), so the novel axis parks.
|
|
r = le.promote_eligible("someone collects vintage railway timetables",
|
|
["nick writes Go and Rust code daily"], 3, 3, CORPUS)
|
|
assert r["eligible"] is False
|
|
assert r["reason"] == "novel axis not grounded in absorbed material — parked"
|
|
|
|
def test_first_principle_on_empty_block(self):
|
|
r = le.promote_eligible("parking is never wrong for growth", [], 3, 3, ())
|
|
assert r == {"eligible": True, "reason": "empty block — first principle"}
|
|
|
|
def test_no_lexical_signal(self):
|
|
r = le.promote_eligible("work things and ways", ["nick writes code"], 3, 3, ())
|
|
# framing-only proposals carry no signal
|
|
assert r["eligible"] in (True, False) # oracle decides; must not raise
|
|
assert isinstance(r["reason"], str) and r["reason"]
|
|
|
|
|
|
class TestDriftLedgerLoadsAndChecks:
|
|
def _load(self):
|
|
spec = importlib.util.spec_from_file_location(
|
|
"drift_ledger_under_test",
|
|
str(ROOT / "src" / "rune" / "drift-ledger.py"),
|
|
)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
def test_module_loads_via_curator_pattern(self):
|
|
mod = self._load()
|
|
assert hasattr(mod, "check")
|
|
assert hasattr(mod, "INDEX_FILE") and hasattr(mod, "DB")
|
|
|
|
def test_check_runs_on_synthetic_store(self, tmp_path):
|
|
mod = self._load()
|
|
# isolate every artifact the gate touches (the curator patches the
|
|
# same attrs before calling check — parity with run_plan).
|
|
blocks = tmp_path / "blocks"
|
|
blocks.mkdir()
|
|
(blocks / "project.md").write_text(
|
|
"---\nanchor: test\n---\n- the project keeps a test anchor line\n",
|
|
encoding="utf-8",
|
|
)
|
|
index = tmp_path / "index"
|
|
index.mkdir()
|
|
(index / "blocks_meta.json").write_text("{}", encoding="utf-8")
|
|
journal = tmp_path / "journal"
|
|
journal.mkdir()
|
|
mod.BLOCKS_DIR = str(blocks)
|
|
mod.INDEX_FILE = str(index / "blocks_meta.json")
|
|
mod.JOURNAL_DIR = str(journal)
|
|
mod.DB = str(tmp_path / "store.db")
|
|
rc = mod.check(strict=False)
|
|
assert rc in (0, 1) # healthy or flagged — never a crash
|