"""Metadata-only audit trail for what ops-mason built. Same invariant as ops-warden's own audit trail (`warden activity`): object names and plan references only, never secret material. """ from __future__ import annotations import json from dataclasses import asdict, dataclass from datetime import UTC, datetime from pathlib import Path _DEFAULT_LOG = Path(__file__).resolve().parents[2] / "audit" / "build-log.jsonl" @dataclass class BuildRecord: plan_id: str plan_path: str approved_by: str approved_at: str objects: dict[str, str] built_at: str def record_build( *, plan_id: str, plan_path: str, approved_by: str, approved_at: str, objects: dict[str, str], log_path: Path | None = None, ) -> BuildRecord: record = BuildRecord( plan_id=plan_id, plan_path=plan_path, approved_by=approved_by, approved_at=approved_at, objects=objects, built_at=datetime.now(UTC).isoformat(), ) path = log_path or _DEFAULT_LOG path.parent.mkdir(parents=True, exist_ok=True) with path.open("a") as fh: fh.write(json.dumps(asdict(record)) + "\n") return record