W-006 (operator decision: delete): - Remove dormant packs/en/el/grc/he/readback_rules.py (4 files, 0 live production callers). generate/realizer.py superseded the per-language readback path; per [[feedback-cleanup-as-you-find]], superseded code is removed rather than preserved. - Remove _gate_readback from packs/common/validator.py and drop it from the validate_pack_dir gate sequence. Add language to the report dict so the param remains non-vacuous. W-010 (operator decision: intentional token-level): - Amend ADR-0143 with "Vocabulary isolation is intentional" section. Token-level anti-unification derives its own structural vocabulary; importing VocabManifold adds no information at that level. Confirmed intentional by operator review 2026-05-25. W-014 (operator decision: evals-only): - Add deployment-scope note to core/cognition/provenance.py docstring: evals-only infrastructure, no live runtime caller. Confirmed evals-only by operator review 2026-05-25.
122 lines
4.8 KiB
Python
122 lines
4.8 KiB
Python
"""Executable validation gates for local language packs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
|
|
from packs.common.runtime_rules import read_jsonl
|
|
|
|
|
|
def _load_module(path: Path, name: str) -> ModuleType:
|
|
spec = importlib.util.spec_from_file_location(name, path)
|
|
if spec is None or spec.loader is None:
|
|
raise ImportError(f"cannot load {path}")
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def _gate_schema(pack_dir: Path) -> tuple[bool, str]:
|
|
required = ("lemmas.jsonl", "senses.jsonl", "morphology.jsonl", "probes/basic.jsonl")
|
|
for rel in required:
|
|
path = pack_dir / rel
|
|
if not path.exists():
|
|
return False, f"missing {rel}"
|
|
try:
|
|
read_jsonl(path)
|
|
except json.JSONDecodeError as exc:
|
|
return False, f"{rel}: invalid JSONL: {exc}"
|
|
return True, "ok"
|
|
|
|
|
|
def _gate_lexical(pack_dir: Path) -> tuple[bool, str]:
|
|
seen = set()
|
|
for record in read_jsonl(pack_dir / "lemmas.jsonl"):
|
|
lid = record["lemma_id"]
|
|
if lid in seen:
|
|
return False, f"duplicate lemma_id: {lid}"
|
|
seen.add(lid)
|
|
if not record.get("field_hooks"):
|
|
return False, f"lemma has no field_hooks: {lid}"
|
|
return True, "ok"
|
|
|
|
|
|
def _gate_morphology(pack_dir: Path) -> tuple[bool, str]:
|
|
known = {record["lemma_id"] for record in read_jsonl(pack_dir / "lemmas.jsonl")}
|
|
for record in read_jsonl(pack_dir / "morphology.jsonl"):
|
|
if record["lemma_id"] not in known:
|
|
return False, f"unknown lemma_id in morphology: {record['lemma_id']}"
|
|
return True, "ok"
|
|
|
|
|
|
def _gate_lift(pack_dir: Path) -> tuple[bool, str]:
|
|
lift_rules = _load_module(pack_dir / "lift_rules.py", f"{pack_dir.name}_lift_rules")
|
|
for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl"):
|
|
if probe.get("kind") != "lift":
|
|
continue
|
|
packet = lift_rules.lift(probe["input"])[0]
|
|
payload = json.loads(packet.payload_json)
|
|
expected = probe["expected"]
|
|
for key in ("field_target", "pressure_kind"):
|
|
if key in expected and payload.get(key) != expected[key]:
|
|
return False, f"{probe['probe_id']}: {key} {payload.get(key)!r} != {expected[key]!r}"
|
|
return True, "ok"
|
|
|
|
|
|
def _gate_determinism(pack_dir: Path) -> tuple[bool, str]:
|
|
lift_rules = _load_module(pack_dir / "lift_rules.py", f"{pack_dir.name}_lift_rules_det")
|
|
for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl"):
|
|
if probe.get("kind") == "lift":
|
|
left = lift_rules.lift(probe["input"])[0].payload_json
|
|
right = lift_rules.lift(probe["input"])[0].payload_json
|
|
if left != right:
|
|
return False, f"{probe['probe_id']}: lift is nondeterministic"
|
|
return True, "ok"
|
|
|
|
|
|
def _gate_alignment(pack_dir: Path) -> tuple[bool, str]:
|
|
lemmas = {record["lemma_id"] for record in read_jsonl(pack_dir / "lemmas.jsonl")}
|
|
senses = {record["sense_id"] for record in read_jsonl(pack_dir / "senses.jsonl")}
|
|
for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl"):
|
|
if probe.get("kind") != "alignment":
|
|
continue
|
|
expected = probe["expected"]
|
|
if expected.get("lemma_id") not in lemmas:
|
|
return False, f"{probe['probe_id']}: unknown lemma anchor"
|
|
if expected.get("sense_id") not in senses:
|
|
return False, f"{probe['probe_id']}: unknown sense anchor"
|
|
return True, "ok"
|
|
|
|
|
|
def _gate_coverage(pack_dir: Path) -> tuple[bool, str]:
|
|
covered = {probe["kind"] for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl")}
|
|
required = {"normalize", "lift", "alignment"}
|
|
missing = required - covered
|
|
if missing:
|
|
return False, f"missing probe kind(s): {', '.join(sorted(missing))}"
|
|
return True, "ok"
|
|
|
|
|
|
def validate_pack_dir(pack_dir: Path, *, pack_id: str, language: str) -> dict:
|
|
gates = (
|
|
("schema", lambda: _gate_schema(pack_dir)),
|
|
("lexical", lambda: _gate_lexical(pack_dir)),
|
|
("morphology", lambda: _gate_morphology(pack_dir)),
|
|
("lift", lambda: _gate_lift(pack_dir)),
|
|
("determinism", lambda: _gate_determinism(pack_dir)),
|
|
("alignment", lambda: _gate_alignment(pack_dir)),
|
|
("coverage", lambda: _gate_coverage(pack_dir)),
|
|
)
|
|
report = {"pack_id": pack_id, "language": language, "active": False, "gates": {}}
|
|
for index, (name, gate_fn) in enumerate(gates):
|
|
passed, reason = gate_fn()
|
|
report["gates"][name] = {"passed": passed, "reason": reason}
|
|
if not passed:
|
|
for remaining_name, _ in gates[index + 1:]:
|
|
report["gates"][remaining_name] = {"passed": False, "reason": "blocked by prior gate failure"}
|
|
return report
|
|
report["active"] = True
|
|
return report
|