Files
lore-engine-poc-v3/scripts/04_consistency.py
Lore Engine Dev 96563bc912 slice 2.5: 04_consistency.py script + demo wiring (4/4 tests; AC 2.4)
- scripts/04_consistency.py: standalone on-demand run of the consistency
  engine over the seed codex; prints summary + per-category detail
- scripts/02_demo.py: append consistency section using the singleton
  consistency_tools path so 'latest_run()' agrees with the run summary
- tests/test_consistency/test_consistency_script.py: 2 tests (end-to-end
  run + --codex flag)
- tests/test_consistency/test_demo.py: 2 tests (end-to-end run + --query
  flag exercises the consistency section)
- 249/249 tests pass
2026-06-18 02:56:47 -04:00

97 lines
3.3 KiB
Python

"""04_consistency — run the consistency engine over the in-memory codex + seed.
Run:
python3 scripts/04_consistency.py [--codex lore_engine_poc/seed]
Walks the codex like ``01_ingest.py``, builds the in-memory
graph, then runs the consistency engine and prints a summary.
This is the "on-demand" path from AC 2.4; the nightly cron is
a separate sub-slice (slice 2.6+).
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
# Allow ``python3 scripts/04_consistency.py`` from the project root.
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
from lore_engine_poc.consistency import (
Anachronism,
Contradiction,
OntologyViolation,
Orphan,
)
from lore_engine_poc.consistency_runner import ConsistencyRunner
from lore_engine_poc.parsers import extract_triples, iter_codex, iter_structured_yaml
from lore_engine_poc.tools import build_graph
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser()
p.add_argument("--codex", default=str(ROOT / "lore_engine_poc" / "seed"))
return p.parse_args()
def main() -> int:
args = parse_args()
codex_root = args.codex
# Walk both markdown and YAML.
md_entities = list(iter_codex(codex_root))
md_triples = list(extract_triples(md_entities))
yaml_dir = Path(codex_root) / "yaml"
yaml_triples = []
if yaml_dir.exists():
yaml_triples = list(iter_structured_yaml(str(yaml_dir)))
g = build_graph(md_entities, md_triples + yaml_triples)
runner = ConsistencyRunner()
run = runner.run(g)
# Summary.
print(f"Consistency run {run.id}")
print(f" started_at: {run.started_at}")
print(f" finished_at: {run.finished_at}")
print(f" duration_ms: {run.duration_ms}")
print(f" rules_run: {run.rules_run}")
print(f" contradictions: {run.violations_found}")
print(f" anachronisms: {run.anachronisms_found}")
print(f" orphans: {run.orphans_found}")
print(f" ontology rules: {sum(1 for v in runner.last_violations if isinstance(v, OntologyViolation))}")
print()
# Per-category detail.
for category_name, category_class in [
("Contradiction", Contradiction),
("Anachronism", Anachronism),
("Orphan", Orphan),
("OntologyViolation", OntologyViolation),
]:
items = [v for v in runner.last_violations if isinstance(v, category_class)]
if not items:
continue
print(f"--- {category_name} ({len(items)}) ---")
for v in items:
print(f" {v.id} severity={v.severity} flagged={v.flagged}")
if isinstance(v, Contradiction):
print(f" {v.subject} -[{v.predicate}]-> {v.claim_a} | {v.claim_b}")
print(f" sources: {v.sources}")
elif isinstance(v, Anachronism):
print(f" {v.entity_name} {v.claim} {v.event_name} (expected={v.expected}, actual={v.actual})")
elif isinstance(v, Orphan):
print(f" {v.entity_name} ({v.entity_type}): {v.reason}")
elif isinstance(v, OntologyViolation):
print(f" rule={v.rule_id} subject={v.subject} predicate={v.predicate}")
if v.claim:
print(f" claim: {v.claim}")
print()
return 0
if __name__ == "__main__":
raise SystemExit(main())