Files
ThothII/harness/tht/pi_skill_projection.py
T

72 lines
2.8 KiB
Python

"""Deterministic builder for the single Pi-facing Thoth session skill."""
import argparse
import sys
from pathlib import Path
HARNESS_ROOT = Path(__file__).resolve().parents[1]
SKILL_ROOT = HARNESS_ROOT / ".pi" / "skills" / "tht-sessione"
MODULE_ROOT = SKILL_ROOT / "modules"
TEMPLATE_PATH = SKILL_ROOT / "projection.md.tmpl"
PROJECTION_PATH = SKILL_ROOT / "SKILL.md"
# This tuple is the composition contract. Never derive it from directory order.
FRAGMENT_ORDER = (
("{{EVIDENCE_RUNTIME_SEARCH}}", "evidence/runtime-search.md"),
("{{DISAMBIGUATION_OPEN_AMBIGUITY}}", "disambiguation/open-ambiguity.md"),
("{{DISAMBIGUATION_INSTRUCTIONS}}", "disambiguation/phase-1.md"),
("{{MEMORY_INSTRUCTIONS}}", "memory/phase-2.md"),
("{{DISAMBIGUATION_REWRITING_INSTRUCTIONS}}", "disambiguation/phase-3.md"),
("{{MEMORY_SOLVED_SEARCH_F4}}", "memory/solved-search-f4.md"),
("{{DISAMBIGUATION_SCHEMA_GROUNDING}}", "disambiguation/schema-grounding.md"),
("{{EVIDENCE_FORMULA_PROPOSALS}}", "evidence/formula-proposals.md"),
("{{MEMORY_SOLVED_SEARCH_F6}}", "memory/solved-search-f6.md"),
("{{MEMORY_SOLVED_SEARCH_F7}}", "memory/solved-search-f7.md"),
("{{MEMORY_PROMOTION_F8}}", "memory/phase-8-promotion.md"),
("{{MEMORY_SOLVED_FINALIZATION}}", "memory/solved-finalization.md"),
)
def render_projection(*, template: bytes | None = None) -> bytes:
projection = TEMPLATE_PATH.read_bytes() if template is None else template
for marker_text, filename in FRAGMENT_ORDER:
marker = marker_text.encode()
if projection.count(marker) != 1:
raise ValueError(f"projection placeholder {marker_text} must occur exactly once")
fragment = (MODULE_ROOT / filename).read_bytes().rstrip(b"\n")
projection = projection.replace(marker, fragment)
return projection
def projection_is_current(path: Path = PROJECTION_PATH) -> bool:
return path.exists() and path.read_bytes() == render_projection()
def write_projection(path: Path = PROJECTION_PATH) -> None:
path.write_bytes(render_projection())
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument("--check", action="store_true", help="fail if SKILL.md is stale")
action.add_argument("--write", action="store_true", help="regenerate SKILL.md")
args = parser.parse_args(argv)
if args.write:
write_projection()
print(f"generated {PROJECTION_PATH}")
return 0
if projection_is_current():
print(f"up to date: {PROJECTION_PATH}")
return 0
print(
f"stale projection: run `python -m tht.pi_skill_projection --write` from {HARNESS_ROOT}",
file=sys.stderr,
)
return 1
if __name__ == "__main__":
raise SystemExit(main())