#!/usr/bin/env python3 """Read a bounded missing patch anchor from the current implementation packet.""" import argparse import fcntl import hashlib import itertools import json import os import shlex from pathlib import Path import sys ROOT = Path(__file__).resolve().parents[1] PACKET = ROOT / "docs/current/module-implementation-packet.json" MAX_WINDOWS = 4 MAX_WINDOW_BYTES = 12000 def packet_paths(packet): names = {row.get("path") for key in ("supportFiles", "editableExcerpts") for row in packet.get(key, [])} for row in packet.get("records", []): source = row.get("source") if source: names.add(source if (ROOT / source).is_file() else str(Path("freecad-source") / source)) names.update(match.get("path") for match in row.get("nativeMatches", [])) for gap in row.get("implementationGaps", []): names.update(match.get("path") for match in gap.get("sourceCandidates", [])) return {(ROOT / name).resolve() for name in names if isinstance(name, str) and name} def packet_summary(packet): # Task metadata is structured JSON, not a source file to slice by lines. result = {"batchId": packet.get("batchId"), "records": [], "supportPaths": [], "instruction": "This is task metadata, not source lines. Use sourceReadCommand below; changing --start on the packet does not read C++ or Python."} for row in packet.get("records", []): entry = {key: row[key] for key in ("apiId", "name", "source", "excerptLine") if key in row} if isinstance(row.get("source"), str) and row.get("excerptLine"): source = row["source"] if not (ROOT / source).is_file(): source = str(Path("freecad-source") / source) entry["sourceReadCommand"] = shlex.join([ "python3", "tools/read_implementation_source.py", source, "--start", str(max(1, int(row["excerptLine"]))), "--lines", "60", "--reason", "Read selected command native source anchor", ]) entry["gaps"] = [{key: gap[key] for key in ("stage", "classification", "reason") if key in gap} for gap in row.get("implementationGaps", [])] result["records"].append(entry) if len(json.dumps(result).encode("utf-8")) > MAX_WINDOW_BYTES - 1000: result["records"].pop() break result["omittedRecords"] = len(packet.get("records", [])) - len(result["records"]) for row in packet.get("supportFiles", []): if not isinstance(row.get("path"), str): continue result["supportPaths"].append(row["path"]) if len(json.dumps(result).encode("utf-8")) > MAX_WINDOW_BYTES - 200: result["supportPaths"].pop() break return json.dumps(result) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("path") parser.add_argument("--start", required=True, type=int) parser.add_argument("--lines", type=int, default=60) parser.add_argument("--reason", required=True) args = parser.parse_args() try: marker_value = os.environ.get("FREECAD_SOURCE_BUDGET_MARKER") if not marker_value: raise ValueError("This reader requires a scheduler-owned turn budget") if not 1 <= args.lines <= 100 or args.start < 1: raise ValueError("Use a positive --start and 1..100 --lines") if not args.reason.strip() or len(args.reason) > 240: raise ValueError("Explain the missing anchor in --reason (1..240 characters)") packet = json.loads(PACKET.read_text(encoding="utf-8")) path = (ROOT / args.path).resolve() metadata = path == PACKET.resolve() if not path.is_relative_to(ROOT) or (not metadata and path not in packet_paths(packet)) or not path.is_file(): raise ValueError("Use an exact source/support/native-match file named by the current packet; no directory scans") if path.stat().st_size > 16 * 1024 * 1024: raise ValueError("This file exceeds the bounded source reader size limit") if metadata: excerpt = packet_summary(packet) args.start = 1 else: with path.open(encoding="utf-8") as source: excerpt = "".join(itertools.islice(source, args.start - 1, args.start - 1 + args.lines)) if not excerpt: raise ValueError("The requested anchor is beyond EOF; use the packet's named line") size = len(excerpt.encode("utf-8")) if size > MAX_WINDOW_BYTES: raise ValueError("Excerpt exceeds 12000 bytes; reduce --lines, do not dump a minified file") relative = str(path.relative_to(ROOT)) fingerprint = hashlib.sha256(f"{relative}:{args.start}:{excerpt}".encode("utf-8")).hexdigest() # Per-turn file locking prevents parallel requests from exceeding the # four-window allowance. Repeated unchanged windows add no context. budget_path = Path(marker_value + ".windows.json") with budget_path.open("a+", encoding="utf-8") as budget: fcntl.flock(budget, fcntl.LOCK_EX) budget.seek(0) data = budget.read() state = json.loads(data) if data else {"batchId": packet["batchId"], "windows": [], "bytes": 0} if state["batchId"] != packet["batchId"]: raise ValueError("Batch changed during this turn; retain the current implementation checkpoint") if any(row["fingerprint"] == fingerprint for row in state["windows"]): print(json.dumps({"status": "already_in_context", "path": relative, "start": args.start, "instruction": "Reuse the unchanged excerpt already returned in this turn"})) return 0 if len(state["windows"]) >= MAX_WINDOWS: raise ValueError("Four supplemental windows used; implement from retained excerpts or record the exact remaining dependency") state["windows"].append({"path": relative, "start": args.start, "lines": args.lines, "fingerprint": fingerprint, "bytes": size, "reason": args.reason}) state["bytes"] += size budget.seek(0) budget.truncate() budget.write(json.dumps(state, separators=(",", ":"))) budget.flush() print(json.dumps({"status": "packet_summary" if metadata else "source_window", "batchId": packet["batchId"], "path": relative, "start": args.start, "bytes": size, "remainingWindows": MAX_WINDOWS - len(state["windows"]), "excerpt": excerpt}, ensure_ascii=False)) return 0 except (OSError, ValueError, KeyError) as error: print(f"IMPLEMENTATION_SOURCE_WINDOW_REJECTED: {error}", file=sys.stderr) return 64 if __name__ == "__main__": raise SystemExit(main())