#!/usr/bin/env python3 from __future__ import annotations import argparse import re import shutil from pathlib import Path from common import ( ARCHIVE_PATH, IMPLEMENTATION_ARCHIVE_DIR, IMPLEMENTATION_PATH, PROOF_PATH, RESEARCH_ACTIVE_PATH, RESEARCH_ARCHIVE_DIR, append_archive_line, git_commit, load_text, save_text, slugify, timestamp_slug, today_iso, ) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Archive and close the current implementation or research turn.") parser.add_argument("--lane", required=True, choices=("implementation", "research")) parser.add_argument( "--status", required=True, choices=("passed", "failed", "paused", "abandoned"), help="Outcome of the turn.", ) parser.add_argument("--summary", required=True, help="One-line closure summary.") parser.add_argument( "--commit", action="store_true", help="Commit the archive change automatically.", ) return parser.parse_args() def title_from_content(content: str, prefix: str) -> str: match = re.search(rf"^# {re.escape(prefix)}: (.+)$", content, flags=re.MULTILINE) if not match: raise SystemExit(f"could not find title for {prefix.lower()}") return match.group(1).strip() def close_implementation_turn(status: str, summary: str) -> tuple[str, list[Path]]: proof_content = load_text(PROOF_PATH) implementation_content = load_text(IMPLEMENTATION_PATH) title = title_from_content(proof_content, "Implementation Proof") slug = slugify(title) stamp = timestamp_slug() IMPLEMENTATION_ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) proof_archive = IMPLEMENTATION_ARCHIVE_DIR / f"{stamp}-{slug}-proof.md" implementation_archive = IMPLEMENTATION_ARCHIVE_DIR / f"{stamp}-{slug}-implementation.md" shutil.copyfile(PROOF_PATH, proof_archive) shutil.copyfile(IMPLEMENTATION_PATH, implementation_archive) save_text( PROOF_PATH, """# Implementation Proof Status: idle No approved implementation proof is active yet. """, ) save_text( IMPLEMENTATION_PATH, """# Implementation Turn Status: idle No approved implementation turn is active yet. """, ) append_archive_line( "implementation", f"- {today_iso()}: `{slug}` closed with status `{status}`. {summary}", ) return title, [proof_archive, implementation_archive] def close_research_turn(status: str, summary: str) -> tuple[str, list[Path]]: research_content = load_text(RESEARCH_ACTIVE_PATH) title = title_from_content(research_content, "Research Turn") slug = slugify(title) stamp = timestamp_slug() RESEARCH_ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) research_archive = RESEARCH_ARCHIVE_DIR / f"{stamp}-{slug}.md" shutil.copyfile(RESEARCH_ACTIVE_PATH, research_archive) save_text( RESEARCH_ACTIVE_PATH, """# Research Turn Status: idle No approved research turn is active yet. """, ) append_archive_line( "research", f"- {today_iso()}: `{slug}` closed with status `{status}`. {summary}", ) return title, [research_archive] def main() -> None: args = parse_args() if args.lane == "implementation" and "Status: idle" in load_text(PROOF_PATH): raise SystemExit("no active implementation turn to close") if args.lane == "research" and "Status: idle" in load_text(RESEARCH_ACTIVE_PATH): raise SystemExit("no active research turn to close") if args.lane == "implementation": title, archived_paths = close_implementation_turn(args.status, args.summary) else: title, archived_paths = close_research_turn(args.status, args.summary) if args.commit: paths = [ARCHIVE_PATH] if args.lane == "implementation": paths.extend([PROOF_PATH, IMPLEMENTATION_PATH]) else: paths.append(RESEARCH_ACTIVE_PATH) paths.extend(archived_paths) git_commit( f"""Archive {args.lane} turn: {title} Proof: Preserve the completed {args.lane} turn and record its outcome in the tracked archive. Assumptions: The archived files capture the relevant planning state for the completed turn. Still fake: Archiving does not validate the work by itself; external evidence still governs whether the result is trustworthy.""", paths=paths, ) for archived_path in archived_paths: print(archived_path.relative_to(RESEARCH_ACTIVE_PATH.parent.parent)) if __name__ == "__main__": main()