"""Shadow-owned store for dispatch and attribution evidence. Deliberately NOT the job record. Two constraints force this: * phase 1 froze the registry record schema, and * P2A-03 forbids this phase from writing the registry at all. The record has none of `attempt_id`, `agent_session_uuid` or `pane_process_generation`, so four-fold attribution has nowhere to live in it. Keeping the evidence here means shadow mode can carry full attribution while the registry stays byte-for-byte untouched. Whether these fields are promoted into the record is a phase 2b decision this phase does not prejudge. """ from __future__ import annotations import json import os import tempfile from pathlib import Path from typing import Any, Dict, Optional SHADOW_DIRNAME = os.path.join(".mam", "shadow") def shadow_root(workspace: Optional[str] = None) -> Path: return Path(workspace or os.getcwd()) / SHADOW_DIRNAME def attribution_path(job_id: str, workspace: Optional[str] = None) -> Path: return shadow_root(workspace) / "attribution" / "{}.json".format(job_id) def decisions_path(job_id: str, workspace: Optional[str] = None) -> Path: return shadow_root(workspace) / "{}.ndjson".format(job_id) def _atomic_write(path: Path, text: str) -> None: path.parent.mkdir(parents=True, exist_ok=True) fd, tmp = tempfile.mkstemp(dir=str(path.parent), prefix=".{}.".format(path.name), suffix=".tmp") try: with os.fdopen(fd, "w", encoding="utf-8") as fh: fh.write(text) fh.flush() os.fsync(fh.fileno()) os.replace(tmp, path) except BaseException: if os.path.exists(tmp): os.unlink(tmp) raise def record_dispatch(job_id, attempt_id, agent_session_uuid, pane_process_generation, dispatch_ack=True, workspace=None, dispatch_token=None) -> Dict[str, Any]: """Persist the four identifiers at dispatch time. The execution anchor starts false on purpose: dispatch being acknowledged is not evidence that the attempt ran. """ context = { "job_id": job_id, "attempt_id": attempt_id, "agent_session_uuid": agent_session_uuid, "pane_process_generation": pane_process_generation, "dispatch_ack": bool(dispatch_ack), "execution_anchor_observed": False, # The record's token for the attempt this evidence belongs to. Carrying # it here is what lets the gate prove the evidence and the commit are # about the same attempt. "dispatch_token": dispatch_token, } _atomic_write(attribution_path(job_id, workspace), json.dumps(context, ensure_ascii=False, indent=2) + "\n") return context def mark_execution_anchor(job_id, workspace=None) -> Optional[Dict[str, Any]]: """Record that this attempt was actually observed executing after dispatch.""" context = load_dispatch(job_id, workspace) if context is None: return None context["execution_anchor_observed"] = True _atomic_write(attribution_path(job_id, workspace), json.dumps(context, ensure_ascii=False, indent=2) + "\n") return context def load_dispatch(job_id, workspace=None) -> Optional[Dict[str, Any]]: path = attribution_path(job_id, workspace) if not path.exists(): return None try: return json.loads(path.read_text(encoding="utf-8")) except Exception: return None def append_decision(job_id, entry: Dict[str, Any], workspace=None) -> None: """Append one would-complete decision. The only thing shadow mode emits.""" path = decisions_path(job_id, workspace) path.parent.mkdir(parents=True, exist_ok=True) with open(path, "a", encoding="utf-8") as fh: fh.write(json.dumps(entry, ensure_ascii=False) + "\n") def read_decisions(job_id, workspace=None) -> list: path = decisions_path(job_id, workspace) if not path.exists(): return [] out = [] for line in path.read_text(encoding="utf-8").splitlines(): if line.strip(): out.append(json.loads(line)) return out