#!/usr/bin/env python3 """Rotate generated task context between isolated task executions.""" from __future__ import annotations import argparse import hashlib import json from datetime import datetime, timezone from pathlib import Path ROOT = Path(__file__).resolve().parents[1] CURRENT = ROOT / "docs" / "current" CONTEXT_DIR = CURRENT / "context" STATE = CURRENT / "task-state.json" SUMMARY = CURRENT / "FREECAD_WEB_CONTEXT_MIN.md" ROTATION = CURRENT / "context-rotation.json" def digest(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--reason", choices=("completed", "timeout", "interrupted"), required=True) parser.add_argument("--next-task", required=True) args = parser.parse_args() if not CONTEXT_DIR.is_dir(): raise SystemExit(f"missing generated context directory: {CONTEXT_DIR}") state = json.loads(STATE.read_text(encoding="utf-8")) previous = state.get("active_task") removed = [] for manifest in sorted(CONTEXT_DIR.glob("FW-*.json")): removed.append({"path": str(manifest.relative_to(ROOT)), "bytes": manifest.stat().st_size, "sha256": digest(manifest)}) manifest.unlink() ROTATION.write_text(json.dumps({ "schema": "freecad-web-context-rotation/v1", "rotatedAt": datetime.now(timezone.utc).isoformat(), "reason": args.reason, "previousTask": previous, "nextTask": args.next_task, "removedGeneratedManifests": removed, "preserved": ["docs/current/task-state.json", "docs/current/FREECAD_WEB_HANDOFF.md", "nextTask.md", "docs/tasks/**", "docs/p6/*.json", "docs/*.log", "freecad-source/**"], "nextStep": "Run sync_web_task_state.py --active NEXT and immediately run prepare_web_task_context.py." }, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") SUMMARY.write_text( "# FreeCAD Web 最小续接上下文\n\n" f"上下文已轮换(reason={args.reason},previous={previous},next={args.next_task})。\n" "加载下一任务前必须运行 `python3 tools/prepare_web_task_context.py`,然后只读取新 manifest 的 `mode=read` 文件。\n", encoding="utf-8", ) print(json.dumps({"previousTask": previous, "nextTask": args.next_task, "removed": len(removed), "reason": args.reason})) if __name__ == "__main__": main()