40-validation/validate-validation-contract.py

#!/usr/bin/env python3
from __future__ import annotations
import argparse, json, os, sys
from pathlib import Path
from evidence_lifecycle import seal_evidence

VALIDATOR_REF="engineering-tools:40-validation/validate-validation-contract.py"
REPOS=["enterprise-architecture","solution-architecture","engineering-platform","engineering-tools","runtime"]
REQUIRED_ARTIFACTS=[
 "enterprise-architecture:85-reference-artifacts/reference-model/validation-contract-model.md",
 "solution-architecture:98-tests/MVP-001-validation-contract.md",
 "engineering-platform:00-overview/VALIDATION_CONTRACT.md",
 "engineering-platform:96-documentation/VALIDATION_CONTRACT_EVIDENCE.md",
 "engineering-tools:40-validation/VALIDATION_EXECUTION_CONTRACT.md",
 "engineering-tools:40-validation/VALIDATION_CONTRACT_VALIDATION.md",
]
REQUIRED_EVIDENCE=[
 "runtime:test/reports/validation/artifact-manifests.json",
 "runtime:test/reports/validation/build-contract.json",
 "runtime:test/reports/validation/deployment-contract.json",
 "runtime:test/reports/validation/workspace-contract.json",
]

def atomic_json(path:Path,data:dict)->None:
 path.parent.mkdir(parents=True,exist_ok=True); tmp=path.with_suffix(path.suffix+'.tmp')
 tmp.write_text(json.dumps(data,indent=2,ensure_ascii=False)+'\n',encoding='utf-8'); os.replace(tmp,path)

def roots(container:Path)->dict[str,Path]:
 out={}
 for rid in REPOS:
  p=container/rid
  m=p/'manifest.json'
  if not m.is_file(): raise RuntimeError(f'missing repository manifest: {rid}')
  d=json.loads(m.read_text()); ident=d.get('artifactName') or d.get('repository',{}).get('id')
  if ident!=rid: raise RuntimeError(f'repository identity mismatch: {rid}')
  out[rid]=p
 return out

def resolve(rs:dict[str,Path],ref:str)->Path:
 rid,rel=ref.split(':',1); p=rs[rid]/rel
 if not p.is_file(): raise RuntimeError(f'missing required artifact: {ref}')
 return p

def evidence_passed(data:dict)->bool:
 for key in ('status','result','overallStatus'):
  v=data.get(key)
  if isinstance(v,str) and v.upper() in {'PASSED','PASS','SUCCESS','SUCCEEDED'}: return True
 return data.get('freshness')=='fresh' and data.get('result') not in {'failed','FAILED'}

def main()->None:
 ap=argparse.ArgumentParser(); ap.add_argument('repository_roots',type=Path); ap.add_argument('runtime_root',type=Path); ap.add_argument('--work-item',default='KT-0016'); a=ap.parse_args()
 container=a.repository_roots.resolve(); rt=a.runtime_root.resolve(); rs=roots(container)
 for ref in REQUIRED_ARTIFACTS: resolve(rs,ref)
 checks=[]
 for ref in REQUIRED_EVIDENCE:
  p=resolve(rs,ref); d=json.loads(p.read_text())
  ok=evidence_passed(d)
  checks.append({'reference':ref,'required':True,'status':'PASSED' if ok else 'FAILED'})
  if not ok: raise RuntimeError(f'required validation evidence not successful: {ref}')
 manifest={'schemaVersion':'1.0','validationId':a.work_item,'workItem':a.work_item,'chain':'RC-0006','status':'PASSED','subject':'MVP-001 contract realization coverage','repositories':REPOS,'checks':checks,'findings':[],'sourcesModified':False,'deploymentPerformed':False,'promotionPerformed':False}
 mp=rt/'test/validations'/a.work_item/'validation-manifest.json'; atomic_json(mp,manifest)
 refs=REQUIRED_ARTIFACTS+REQUIRED_EVIDENCE+[f'runtime:test/validations/{a.work_item}/validation-manifest.json']
 ev=seal_evidence(manifest,container,VALIDATOR_REF,refs,work_item=a.work_item,chain='RC-0006')
 atomic_json(rt/'test/reports/validation/validation-contract.json',ev)
 print(json.dumps({'status':'PASSED','chain':'RC-0006','checks':len(checks),'findings':0},indent=2))
if __name__=='__main__':
 try: main()
 except Exception as exc: print('ERROR: '+str(exc),file=sys.stderr); raise SystemExit(1)