40-validation/validate-workspace-contract.py

#!/usr/bin/env python3
from __future__ import annotations
import argparse, hashlib, json, os, sys
from pathlib import Path
from evidence_lifecycle import seal_evidence
VALIDATOR_REF="engineering-tools:40-validation/validate-workspace-contract.py"
SCOPE=["enterprise-architecture","solution-architecture","engineering-platform","engineering-tools","runtime"]

def sha256(p):
 h=hashlib.sha256();
 with p.open("rb") as f:
  for c in iter(lambda:f.read(1024*1024),b""): h.update(c)
 return h.hexdigest()

def atomic_json(p,d):
 p.parent.mkdir(parents=True,exist_ok=True); t=p.with_suffix(p.suffix+".tmp"); t.write_text(json.dumps(d,indent=2,ensure_ascii=False)+"\n"); os.replace(t,p)

def main():
 ap=argparse.ArgumentParser(); ap.add_argument("repository_roots",type=Path); ap.add_argument("runtime_root",type=Path); ap.add_argument("--work-item",default="KT-0015"); a=ap.parse_args()
 roots=a.repository_roots.resolve(); rt=a.runtime_root.resolve()
 required=[roots/"enterprise-architecture/85-reference-artifacts/reference-model/workspace-contract-model.md",roots/"solution-architecture/70-data/MVP-001-workspace-contract.md",roots/"engineering-platform/00-overview/WORKSPACE_CONTRACT.md",roots/"engineering-platform/96-documentation/WORKSPACE_CONTRACT_EVIDENCE.md"]
 missing=[str(p) for p in required if not p.is_file()]
 if missing: raise RuntimeError("missing workspace-contract artifacts: "+", ".join(missing))
 repos={}
 for rid in SCOPE:
  m=roots/rid/"manifest.json"
  if not m.is_file(): raise RuntimeError("missing repository manifest: "+rid)
  d=json.loads(m.read_text()); name=d.get("artifactName") or d.get("repository",{}).get("id")
  if name!=rid: raise RuntimeError("repository identity mismatch: "+rid)
  repos[rid]={"version":d.get("version") or d.get("repository",{}).get("version"),"manifestSha256":sha256(m),"mode":"writable"}
 manifest={"schemaVersion":"1.0","workspaceId":a.work_item,"workItem":a.work_item,"chain":"RC-0005","storageClass":"WORKSPACE","status":"VALIDATED","allowedTargetRepositories":SCOPE,"readOnlyRepositories":[],"repositories":repos,"implicitDeletions":False,"promotionPerformed":False,"handoffState":"HANDOFF_READY"}
 mp=rt/"test/workspaces"/a.work_item/"workspace-manifest.json"; atomic_json(mp,manifest)
 ev=seal_evidence(manifest,roots,VALIDATOR_REF,[f"runtime:test/workspaces/{a.work_item}/workspace-manifest.json","enterprise-architecture:85-reference-artifacts/reference-model/workspace-contract-model.md","solution-architecture:70-data/MVP-001-workspace-contract.md","engineering-platform:00-overview/WORKSPACE_CONTRACT.md"],work_item=a.work_item,chain="RC-0005")
 atomic_json(rt/"test/reports/validation/workspace-contract.json",ev)
 print(json.dumps({"status":"PASSED","chain":"RC-0005","workspaceId":a.work_item,"repositories":len(repos)},indent=2))
if __name__=="__main__":
 try: main()
 except Exception as e: print("ERROR: "+str(e),file=sys.stderr); raise SystemExit(1)