- 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
97 lines
3.3 KiB
Python
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()) |