108 lines
3.3 KiB
Python
108 lines
3.3 KiB
Python
"""Materialize built-in persona seed versions on demand.
|
|
|
|
Default mode is a dry run: report the repository persona seed manifest without
|
|
touching the database. Use --apply when an operator intentionally wants to run
|
|
the existing idempotent DB materializer.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
API_ROOT = REPO_ROOT / "apps" / "api"
|
|
if str(API_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(API_ROOT))
|
|
|
|
|
|
from app.persona_repository import ( # noqa: E402
|
|
SEED_VERSION,
|
|
built_in_personas,
|
|
materialize_seed_personas,
|
|
seed_persona_id,
|
|
)
|
|
from app.db import close_pool, init_pool # noqa: E402
|
|
|
|
|
|
def _manifest_rows() -> list[dict[str, Any]]:
|
|
rows: list[dict[str, Any]] = []
|
|
for card in built_in_personas():
|
|
rows.append(
|
|
{
|
|
"code": card.code,
|
|
"version": SEED_VERSION,
|
|
"persona_id": str(seed_persona_id(card.code)),
|
|
"display_name": card.display_name,
|
|
"difficulty": card.difficulty,
|
|
"theory_target": list(card.theory_target),
|
|
"source_provenance": card.source_provenance,
|
|
"synthetic": bool(card.is_synthetic),
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
def _print_text(*, applied: bool, count: int | None, rows: list[dict[str, Any]]) -> None:
|
|
mode = "APPLY" if applied else "DRY-RUN"
|
|
print(f"persona seed materializer: {mode}")
|
|
print(f"seed_version: {SEED_VERSION}")
|
|
print(f"manifest_count: {len(rows)}")
|
|
if count is not None:
|
|
print(f"materialize_calls: {count}")
|
|
for row in rows:
|
|
theories = ",".join(row["theory_target"])
|
|
print(
|
|
f"- {row['code']} v{row['version']} {row['persona_id']} "
|
|
f"{row['display_name']} [{theories}]"
|
|
)
|
|
|
|
|
|
async def _main_async(args: argparse.Namespace) -> int:
|
|
rows = _manifest_rows()
|
|
count: int | None = None
|
|
if args.apply:
|
|
await init_pool()
|
|
try:
|
|
count = await materialize_seed_personas()
|
|
finally:
|
|
await close_pool()
|
|
payload = {
|
|
"applied": bool(args.apply),
|
|
"seed_version": SEED_VERSION,
|
|
"manifest_count": len(rows),
|
|
"materialize_calls": count,
|
|
"personas": rows,
|
|
}
|
|
if args.json:
|
|
print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True))
|
|
else:
|
|
_print_text(applied=bool(args.apply), count=count, rows=rows)
|
|
return 0
|
|
|
|
|
|
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description=(
|
|
"Report or apply the repository persona seed/version manifest. "
|
|
"Dry-run is the default; --apply performs DB writes through the "
|
|
"existing idempotent materializer."
|
|
)
|
|
)
|
|
parser.add_argument("--apply", action="store_true", help="write missing seed versions to DB")
|
|
parser.add_argument("--json", action="store_true", help="emit machine-readable JSON")
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(list(argv if argv is not None else sys.argv[1:]))
|
|
return asyncio.run(_main_async(args))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|