"""Provenance helpers: code commit, environment identity, artifact manifests.""" from __future__ import annotations import json, os, platform, subprocess from .contracts import dir_manifest, sha256_text, utcnow def code_commit(repo_root) -> str: try: r = subprocess.run(["git", "-C", repo_root, "rev-parse", "HEAD"], capture_output=True, text=True, timeout=20) dirty = subprocess.run(["git", "-C", repo_root, "status", "--porcelain"], capture_output=True, text=True, timeout=20).stdout.strip() return r.stdout.strip() + ("-dirty" if dirty else "") except Exception: # noqa: BLE001 return "unknown" def node_environment(node, ssh_fn) -> dict: cmd = ("export PATH=/opt/homebrew/bin:$PATH; echo claude=$(claude --version 2>/dev/null | head -1); echo os=$(sw_vers -productVersion); echo arch=$(uname -m); " "echo py=$(~/wp29/venv/bin/python -c 'import sys,numpy,pandas,scipy,statsmodels; print(sys.version.split()[0], numpy.__version__, pandas.__version__, scipy.__version__, statsmodels.__version__)' 2>/dev/null); " "echo gate=$(shasum -a 256 ~/wp29/gates/pgar_gate.py 2>/dev/null | cut -c1-16); echo host=$(hostname)") r = ssh_fn(node, cmd) d = {"node": node} for l in r.stdout.splitlines(): if "=" in l: k, v = l.split("=", 1); d[k] = v.strip() d["environment_id"] = sha256_text(json.dumps(d, sort_keys=True))[:16] d["recorded_utc"] = utcnow() return d def artifact_manifest(local_out_dir) -> dict: return {"root": local_out_dir, "files": dir_manifest(local_out_dir), "recorded_utc": utcnow()}