Adds the slice 6.6 migration as a library helper (lore_engine_poc.migration: scan_codex_for_planes / apply_plane_migration) plus a thin CLI wrapper at scripts/05_migrate_planes.py. Discriminator: frontmatter signals — plane: true, tags contains plane, OR type: plane — promote an entry to :Plane. Default Material Plane convention remains mardonari.material (added by the 6.4 backfill). Idempotent: re-running on the same codex produces the same graph state. LAYER_OF edges are created only between co-referenced Planes (markdown body [[X]] → Plane node X). Direction follows docs/17-planes.md: (:Plane A)-[:LAYER_OF]-> (:Plane B) where A is the layer and B is the parent. In the voldramir fixture, Voldramir(demiplane) LAYER_OF Underdark(plane). The script supports --dry-run (print planned changes, exit 0) and --codex / --setting overrides. Suite: 747 → 756 tests (+9). No regressions. Co-Authored-By: Claude <noreply@anthropic.com>
111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
"""05_migrate_planes — promote codex Regions tagged as Planes.
|
|
|
|
Per ``docs/plan/exec/06-planes.md`` sub-slice 6.6, this script
|
|
reads the codex's markdown frontmatter and promotes any entry
|
|
with a ``:Plane`` signal to a ``:Plane`` node, creating
|
|
``LAYER_OF`` edges for the in-markdown cross-references.
|
|
|
|
Run:
|
|
|
|
python3 scripts/05_migrate_planes.py [--codex ROOT] [--setting ID] [--dry-run]
|
|
|
|
Discriminator (what makes an entry a Plane):
|
|
|
|
- ``plane: true`` in the frontmatter, OR
|
|
- ``plane`` in the entry's ``tags`` list, OR
|
|
- ``type: plane`` in the frontmatter.
|
|
|
|
The ``--dry-run`` flag prints the list of planned promotions
|
|
and exits without touching the graph. Default behaviour
|
|
applies the migration in-process: the codex's own graph
|
|
(built via ``load_graph``) is the one mutated, and a small
|
|
summary is printed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from lore_engine_poc.graph_backend import InMemoryGraph
|
|
from lore_engine_poc.migration import (
|
|
apply_plane_migration,
|
|
scan_codex_for_planes,
|
|
)
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
p.add_argument(
|
|
"--codex",
|
|
default=str(ROOT / "lore_engine_poc" / "seed"),
|
|
help="Codex root (the directory containing 'Campaign Codex - Regions/').",
|
|
)
|
|
p.add_argument(
|
|
"--setting",
|
|
default="mardonari",
|
|
help="Setting id to materialise (default: mardonari).",
|
|
)
|
|
p.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Print the list of planned promotions and exit without writing.",
|
|
)
|
|
return p.parse_args()
|
|
|
|
|
|
def _print_plans(plans: list) -> None:
|
|
if not plans:
|
|
print(" (no plane promotions planned)")
|
|
return
|
|
for p in plans:
|
|
print(
|
|
f" + {p.entity_name:32s} → {p.plane_id:36s} kind={p.plane_kind} "
|
|
f"({p.source_path})"
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
codex_root = Path(args.codex)
|
|
|
|
if not codex_root.is_dir():
|
|
print(f"[05_migrate_planes] codex not found: {codex_root}", file=sys.stderr)
|
|
return 1
|
|
|
|
plans = scan_codex_for_planes(codex_root, setting_id=args.setting)
|
|
|
|
print(f"[05_migrate_planes] scanned {codex_root}")
|
|
print(f"[05_migrate_planes] setting: {args.setting}")
|
|
print(f"[05_migrate_planes] planned {len(plans)} promotion(s):")
|
|
_print_plans(plans)
|
|
|
|
if args.dry_run:
|
|
print("[05_migrate_planes] --dry-run: no graph changes applied")
|
|
return 0
|
|
|
|
# Build a fresh in-memory graph. The migration is a
|
|
# stand-alone step — it does not depend on the
|
|
# ``01_ingest``-built graph. The slice 6.5 settings
|
|
# filter, slice 7's MCP tools, and slice 6.6's
|
|
# migration each materialise what they need; this
|
|
# keeps the script a clean re-run point.
|
|
g = InMemoryGraph()
|
|
summary = apply_plane_migration(g, plans, setting_id=args.setting)
|
|
|
|
print(
|
|
f"[05_migrate_planes] applied: "
|
|
f"setting_added={summary.setting_added} "
|
|
f"planes_added={summary.planes_added} "
|
|
f"layer_of_edges_added={summary.layer_of_edges_added}"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|