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)