#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""Dependency-free, fail-closed Vaultfire V3 open-spec conformance harness."""
from __future__ import annotations
import argparse, copy, datetime as dt, hashlib, json, math, os, re, stat, subprocess, sys
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]; REPO=ROOT.parent; SCHEMAS=ROOT/'schemas'/'1.0.0'
SCHEMA_BY_TAG={
'vaultfire.v3.open.bond-definition.v1':'bond-definition.v1.schema.json','vaultfire.v3.open.bond-state.v1':'bond-state.v1.schema.json','vaultfire.v3.open.signed-terms.v1':'signed-terms.v1.schema.json','vaultfire.v3.open.mutual-settlement.v1':'mutual-settlement.v1.schema.json','vaultfire.v3.open.receipt.v1':'receipt.v1.schema.json','vaultfire.v3.open.error.v1':'error-envelope.v1.schema.json','vaultfire.v3.open.deployment-capability-evidence.v1':'deployment-capability-evidence.v1.schema.json','vaultfire.v3.open.conformance-report.v1':'conformance-report.v1.schema.json'}
ZERO20='0x'+'00'*20; ZERO32='0x'+'00'*32; U64=(1<<64)-1; U96=(1<<96)-1; U256=(1<<256)-1; MAX_WINDOW=2592000
PRIVACY='0xb82b125017c65a2e448f5a9cb1d9143dd5246787ca1ea647def021250472f2fa'; EVIDENCE='0x193783e20da7ca4f4cdbde9b81349699958222dfbb7de2cf977de3897255e12a'
class Failure(Exception):
 def __init__(self,code,message): super().__init__(message); self.code=code

def _bad_constant(v): raise Failure('VF3-CANON-001',f'non-finite JSON constant prohibited: {v}')
def _parse_int(v):
 n=int(v)
 if abs(n)>U256: raise Failure('VF3-CANON-001','JSON integer exceeds supported canonical range')
 return n
def load_json(path):
 def no_dupes(pairs):
  d={}
  for k,v in pairs:
   if k in d: raise Failure('VF3-CANON-001',f'duplicate key {k}')
   d[k]=v
  return d
 try: return json.loads(Path(path).read_text('utf-8'),object_pairs_hook=no_dupes,parse_float=lambda _: (_ for _ in ()).throw(Failure('VF3-CANON-001','floating point prohibited')),parse_int=_parse_int,parse_constant=_bad_constant)
 except Failure: raise
 except Exception: raise Failure('VF3-SCHEMA-001','malformed JSON')
def _canonical_guard(x):
 if isinstance(x,float) or (isinstance(x,int) and not isinstance(x,bool) and abs(x)>U256): raise Failure('VF3-CANON-001','noncanonical number')
 if isinstance(x,str) and any(0xD800<=ord(c)<=0xDFFF for c in x): raise Failure('VF3-CANON-001','unpaired surrogate prohibited')
 if isinstance(x,dict):
  if not all(isinstance(k,str) for k in x): raise Failure('VF3-CANON-001','object keys must be strings')
  for k,v in x.items(): _canonical_guard(k);_canonical_guard(v)
 elif isinstance(x,list):
  for v in x:_canonical_guard(v)
def canonical_bytes(x):
 _canonical_guard(x)
 try:return json.dumps(x,ensure_ascii=False,allow_nan=False,sort_keys=True,separators=(',',':')).encode('utf-8')
 except Exception:raise Failure('VF3-CANON-001','canonical encoding failed')
def digest(x):return '0x'+hashlib.sha256(canonical_bytes(x)).hexdigest()
def type_ok(v,t):return {'object':isinstance(v,dict),'array':isinstance(v,list),'string':isinstance(v,str),'integer':isinstance(v,int) and not isinstance(v,bool),'number':isinstance(v,(int,float)) and not isinstance(v,bool),'boolean':isinstance(v,bool),'null':v is None}.get(t,False)
def parse_time(v,path='$'):
 try:
  if not isinstance(v,str) or not v.endswith('Z'):raise ValueError
  return dt.datetime.strptime(v,'%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=dt.timezone.utc)
 except Exception:raise Failure('VF3-SCHEMA-001',f'{path}: invalid RFC3339 UTC timestamp')
def validate_schema(v,s,path='$'):
 if 'oneOf' in s:
  ok=0
  for q in s['oneOf']:
   try:validate_schema(v,q,path);ok+=1
   except Failure:pass
  if ok!=1:raise Failure('VF3-SCHEMA-001',f'{path}: expected exactly one schema, got {ok}')
 if 'const' in s and (type(v) is not type(s['const']) or v!=s['const']):raise Failure('VF3-SCHEMA-001',f'{path}: const mismatch')
 if 'enum' in s and not any(type(v) is type(x) and v==x for x in s['enum']):raise Failure('VF3-SCHEMA-001',f'{path}: enum mismatch')
 if 'type' in s:
  ts=s['type'] if isinstance(s['type'],list) else [s['type']]
  if not any(type_ok(v,t) for t in ts):raise Failure('VF3-SCHEMA-001',f'{path}: invalid type')
 if isinstance(v,str):
  if 'pattern' in s and re.fullmatch(s['pattern'],v) is None:raise Failure('VF3-SCHEMA-001',f'{path}: pattern mismatch')
  if not s.get('minLength',0)<=len(v)<=s.get('maxLength',10**18):raise Failure('VF3-SCHEMA-001',f'{path}: string length')
  if s.get('format')=='date-time':parse_time(v,path)
 if isinstance(v,int) and not isinstance(v,bool):
  if v<s.get('minimum',v) or v>s.get('maximum',v):raise Failure('VF3-SCHEMA-001',f'{path}: numeric bound')
 if isinstance(v,list):
  if not s.get('minItems',0)<=len(v)<=s.get('maxItems',10**18):raise Failure('VF3-SCHEMA-001',f'{path}: array length')
  if s.get('uniqueItems') and len({canonical_bytes(x) for x in v})!=len(v):raise Failure('VF3-SCHEMA-001',f'{path}: duplicate items')
  if isinstance(s.get('items'),dict):
   for i,x in enumerate(v):validate_schema(x,s['items'],f'{path}[{i}]')
 if isinstance(v,dict):
  if len(v)<s.get('minProperties',0) or len(v)>s.get('maxProperties',10**18):raise Failure('VF3-SCHEMA-001',f'{path}: property count')
  missing=[x for x in s.get('required',[]) if x not in v]
  if missing:raise Failure('VF3-SCHEMA-001',f'{path}: missing {missing}')
  props=s.get('properties',{});ap=s.get('additionalProperties')
  if ap is False:
   extra=set(v)-set(props)
   if extra:raise Failure('VF3-SCHEMA-001',f'{path}: unknown {sorted(extra)}')
  if 'propertyNames' in s:
   for k in v:validate_schema(k,s['propertyNames'],f'{path} key')
  for k,x in v.items():
   if k in props:validate_schema(x,props[k],f'{path}.{k}')
   elif isinstance(ap,dict):validate_schema(x,ap,f'{path}.{k}')
def _uint(d,k,maxv=U256,positive=False):
 try:n=int(d[k])
 except Exception:raise Failure('VF3-SCHEMA-001',f'{k}: invalid unsigned integer')
 if str(n)!=d[k] or n<0 or n>maxv or (positive and n==0):raise Failure('VF3-SCHEMA-001',f'{k}: unsigned integer out of bounds')
 return n
def _truth(d,allow_compliant=True):
 t=d.get('truth',{})
 if any(t.get(x) is not False for x in ('audited','deployed','authorized','endorsed')):raise Failure('VF3-EVIDENCE-001','unsupported truth label')
 if not allow_compliant and t.get('specificationCompliant') is not False:raise Failure('VF3-EVIDENCE-001','unsupported specification compliance label')
def _privacy_text_guard(value):
 """Inspect every nested extension key/value; opaque commitments are prohibited."""
 prohibited=('biometric','surveillance','profiling','profile','behavioral','behavioural','tracking','fingerprint','faceprint','voiceprint','opaque','transitive')
 if isinstance(value,dict):
  for k,v in value.items():_privacy_text_guard(k);_privacy_text_guard(v)
 elif isinstance(value,list):
  for v in value:_privacy_text_guard(v)
 elif isinstance(value,str):
  normalized=''.join(c.lower() if c.isalnum() else ' ' for c in value)
  compact=''.join(normalized.split())
  if any(word in normalized or word in compact for word in prohibited) or re.search(r'(?:sha(?:256|3)|keccak|blake|hash)\\s*[:=-]',value,re.I):raise Failure('VF3-PRIVACY-001','prohibited or opaque extension content')
def _source_binding(b):
 p=b.get('path','')
 if not isinstance(p,str) or p.startswith('/') or '\\' in p or any(x in ('','..') for x in p.split('/')):raise Failure('VF3-EVIDENCE-001','unsafe source binding path')
 candidate=REPO
 for part in p.split('/'):
  candidate=candidate/part
  try:
   if stat.S_ISLNK(candidate.lstat().st_mode):raise Failure('VF3-EVIDENCE-001','source binding symlink prohibited')
  except FileNotFoundError:raise Failure('VF3-EVIDENCE-001','source binding unavailable')
 target=candidate.resolve()
 try:target.relative_to(REPO.resolve())
 except ValueError:raise Failure('VF3-EVIDENCE-001','source binding escapes repository')
 if not target.is_file():raise Failure('VF3-EVIDENCE-001','source binding unavailable')
 if hashlib.sha256(target.read_bytes()).hexdigest()!=b.get('sha256'):raise Failure('VF3-EVIDENCE-001','source binding digest mismatch')
def _git(*args):
 try:return subprocess.check_output(['git','-C',str(REPO),*args],stderr=subprocess.DEVNULL,text=True).strip()
 except Exception:raise Failure('VF3-EVIDENCE-001','repository object or relationship unavailable')
def _exact_commit(commit,tree):
 if _git('cat-file','-t',commit)!='commit' or _git('rev-parse',commit+'^{tree}')!=tree:raise Failure('VF3-EVIDENCE-001','commit/tree object relationship mismatch')
 for path in ('LICENSE','v3-open-spec'):
  try:_git('cat-file','-e',commit+':'+path)
  except Failure:raise Failure('VF3-EVIDENCE-001','exact commit omits required package or license')
 if _git('status','--porcelain','--','LICENSE','v3-open-spec'):raise Failure('VF3-EVIDENCE-001','candidate package is not clean at claimed commit')
def _eval_time(evaluation_time,production_current_time):
 if production_current_time:return dt.datetime.now(dt.timezone.utc)
 if evaluation_time is None:return dt.datetime.now(dt.timezone.utc)
 if isinstance(evaluation_time,str):return parse_time(evaluation_time)
 if isinstance(evaluation_time,dt.datetime) and evaluation_time.tzinfo:return evaluation_time.astimezone(dt.timezone.utc)
 raise Failure('VF3-EVIDENCE-001','trusted evaluation time required')
def semantics(d,related_terms=None,evaluation_time=None,production_current_time=False):
 tag=d['schema']
 inv=d.get('invariants')
 if isinstance(inv,dict):
  expected={'flourishingCanSlashPrincipal':False,'rewardsCanConsumeCollateral':False,'surveillancePermittedForProtectedDecisions':False,'explicitConsentRequired':True,'boundedPauseSafeExitRequired':True}
  for k,v in expected.items():
   if inv.get(k) is not v:raise Failure({'flourishingCanSlashPrincipal':'VF3-FLOURISH-001','rewardsCanConsumeCollateral':'VF3-REWARD-001','surveillancePermittedForProtectedDecisions':'VF3-PRIVACY-001','explicitConsentRequired':'VF3-CONSENT-001','boundedPauseSafeExitRequired':'VF3-EXIT-001'}[k],k)
 if tag=='vaultfire.v3.open.bond-definition.v1':
  if d['stakeAsset']!=ZERO20 or d['verifyingContract']==ZERO20:raise Failure('VF3-STATE-001','native asset and nonzero verifier required')
  created=_uint(d,'createdAt',U64);start=_uint(d,'activePeriodStart',U64);end=_uint(d,'activePeriodEnd',U64);cw=_uint(d,'challengeWindow',U64,True)
  if not(created<start<end) or cw>MAX_WINDOW:raise Failure('VF3-EXIT-001','invalid active period or challenge bound')
  parties=[d['aiCompany'],d['counterparty']] if d['kind']=='accountability' else [d['partyA'],d['partyB']]
  if ZERO20 in parties or len(set(parties))!=2 or d['consent']['explicit'] is not True or set(d['consent']['parties'])!=set(parties):raise Failure('VF3-CONSENT-001','participant consent mismatch')
  for x in d['decisionInputs']:
   if x['class']!='objective-protocol' or x['provenance']!='canonical-source' or x['decisionInfluence']!='bounded-objective-rule' or x['containsPersonalData'] or x['containsSurveillanceData']:raise Failure('VF3-PRIVACY-001','prohibited or opaque decision provenance')
  graph=d['provenanceGraph'];nodes=graph['nodes'];by_digest={x['digest']:x for x in nodes};node_digests=set(by_digest)
  if len(by_digest)!=len(nodes) or graph['graphDigest']!=digest(nodes) or graph['authorizationDigest']==ZERO32 or any(x['containsPersonalData'] or x['containsSurveillanceData'] or not set(x['parentDigests'])<=node_digests for x in nodes) or any(x['evidenceDigest'] not in node_digests for x in d['decisionInputs']):raise Failure('VF3-PRIVACY-001','closed provenance graph binding failed')
  roots=set();visiting=set();visited=set()
  def visit(node_id):
   if node_id in visiting:raise Failure('VF3-PRIVACY-001','provenance graph cycle')
   if node_id in visited:return
   visiting.add(node_id);node=by_digest[node_id];parents=node['parentDigests'];der=node['derivation'];binding=node['sourceBinding']
   if binding['chainId']!=d['chainId'] or binding['verifyingContract']!=d['verifyingContract'] or binding['contentDigest']!=node_id:raise Failure('VF3-PRIVACY-001','provenance source binding mismatch')
   if not parents:
    if der!={'kind':'root-canonical','algorithm':'none','parametersDigest':ZERO32}:raise Failure('VF3-PRIVACY-001','untyped provenance root')
    roots.add(node_id)
   elif der['kind']!='canonical-transform' or der['algorithm']!='sha256-rfc8785' or der['parametersDigest']==ZERO32:raise Failure('VF3-PRIVACY-001','opaque or generic provenance derivation')
   for parent in parents:visit(parent)
   visiting.remove(node_id);visited.add(node_id)
  for node_id in node_digests:visit(node_id)
  used={x['evidenceDigest'] for x in d['decisionInputs']}
  reachable=set()
  def ancestors(node_id):
   if node_id in reachable:return
   reachable.add(node_id)
   for parent in by_digest[node_id]['parentDigests']:ancestors(parent)
  for node_id in used:ancestors(node_id)
  if not roots or reachable!=node_digests:raise Failure('VF3-PRIVACY-001','unrooted or unreachable provenance node')
  for x in d['extensions'].values():
   if x['securityCritical'] or x['decisionInfluence']!='none':raise Failure('VF3-PRIVACY-001','extension cannot influence protected decisions')
  _privacy_text_guard(d['extensions'])
  if any(any(word in k.lower() for word in ('biometric','surveillance','profile','derived','tracking')) for k in d['extensions']):raise Failure('VF3-PRIVACY-001','privacy-sensitive opaque extension prohibited')
  p=d['portability']
  if p['personalDataIncluded'] or p['surveillanceDataIncluded'] or not p['explicitConsent'] or not p['chainSpecificAuthorizationRequired'] or p['sourceChainId']==p['destinationChainId'] or p['sourceChainId']!=d['chainId'] or p['destinationChainId'] not in ('8453','84532') or p['purpose']=='' or p['recipient']==ZERO20 or p['historyDigest']==ZERO32 or p['authorizationDigest']==ZERO32 or not(created<_uint(p,'validUntil',U64)<=end+MAX_WINDOW):raise Failure('VF3-PORTABILITY-001','invalid or stale cross-chain consent/authorization')
  if d['kind']=='accountability':
   e=d['enforcement'];stake=_uint(d,'companyStake',U256,True);mx=_uint(e,'maximumSlash',U256,True);req=_uint(e,'requiredBy',U64);rw=_uint(e,'responseWindow',U64,True);ecw=_uint(e,'challengeWindow',U64,True);dw=_uint(e,'decisionWindow',U64,True)
   timeout_policy={'canonicalRevalidation':'same-timestamp-before-settlement','acceptedLiabilityTimeout':'settle-if-current-breach-otherwise-full-refund','callableWhilePaused':True,'boundary':'activePeriodEnd','genericDisputeSelectors':'retired'}
   # The staged enforcement windows are half-open and the complete decision
   # window must finish strictly before activePeriodEnd:
   # response [requiredBy, requiredBy+rw), challenge [..,+ecw),
   # decision [..,+dw), then finalization remains possible only before end.
   if d['objectiveRuleHash']!=e['configHash'] or e['canonicalSource']==ZERO20 or e['canonicalSourceId']==ZERO32 or e['obligationId']==ZERO32 or e['canonicalSourceCodehash']==ZERO32 or mx>stake or req<start or req>=end or ecw!=cw or max(rw,ecw,dw)>MAX_WINDOW or req+rw+ecw+dw>=end or e['evidenceSchemaHash']!=EVIDENCE or e['privacyExclusionsHash']!=PRIVACY or any(e[x]!=ZERO32 for x in ('attesterSetHash','adjudicatorSetHash')) or e['timeoutPolicy']!=timeout_policy:raise Failure('VF3-STATE-001','canonical accountability create constraints failed')
  else:
   a=_uint(d,'partyAStake',U256);b=_uint(d,'partyBStake',U256);pd=_uint(d,'proposalDeadline',U64)
   if a+b==0 or d['partnershipType']==ZERO32 or not(created<pd<=start<end) or end+2*cw>U64:raise Failure('VF3-STATE-001','canonical partnership create constraints failed')
 if tag=='vaultfire.v3.open.bond-state.v1':
  validate_document(d['definition'])
  obs=int(parse_time(d['observedAt']).timestamp()); funded=_uint(d,'principalFunded');liab=_uint(d,'principalLiability');refund=_uint(d,'refundablePrincipal');slash=_uint(d,'slashedPrincipal');locked=_uint(d,'lockedPrincipal');withdrawn=_uint(d,'principalWithdrawn');reward=_uint(d,'rewardBalance');rf=_uint(d,'rewardFunded')
  if liab!=refund+slash+locked or funded!=refund+slash+locked+withdrawn or reward>rf:raise Failure('VF3-REWARD-001','exact principal/reward conservation failed')
  if d['kind']=='partnership' and slash:raise Failure('VF3-FLOURISH-001','partnership principal cannot slash')
  hist=d['transitionHistory'];allowed=set(load_json(ROOT/'interfaces/canonical-v3-interfaces.v1.json')['stateMachine']['transitions'])
  times=[_uint(x,'at',U64) for x in hist];modes=[x.get('mode','ordinary') for x in hist]
  continuous=bool(hist) and hist[0]['from']=='Uninitialized' and all(hist[i]['to']==hist[i+1]['from'] for i in range(len(hist)-1))
  equal_time_valid=all(
   a<b or (modes[index]==modes[index+1]=='accepted-liability-timeout')
   for index,(a,b) in enumerate(zip(times,times[1:]))
  )
  if not continuous or hist[-1]['to']!=d['state'] or any(f"{x['from']}>{x['to']}" not in allowed for x in hist) or any(a>b for a,b in zip(times,times[1:])) or not equal_time_valid or times[-1]>obs:raise Failure('VF3-STATE-001','invalid, discontinuous, or noncanonical same-timestamp transition history')
  start=_uint(d['definition'],'activePeriodStart',U64);end=_uint(d['definition'],'activePeriodEnd',U64)
  pre_expiry={'Active','SettlementRequested','Finalizable','Challenged','SettlementFrozen'}
  if (d['state'] in pre_expiry and obs>=end) or (d['state'] in {'Expired','ForceExited'} and obs<end):raise Failure('VF3-STATE-001','half-open active period violated')
  for transition,at in zip(hist,times):
   if transition['to']=='Active' and not(start<=at<end):raise Failure('VF3-STATE-001','active transition outside half-open period')
   if transition['to'] in {'SettlementRequested','Finalizable','Challenged','SettlementFrozen','Settled'} and at>=end and transition.get('mode','ordinary')!='accepted-liability-timeout':raise Failure('VF3-STATE-001','ordinary settlement or finalization transition at or after expiry')
   if transition['to']=='Expired' and at<end:raise Failure('VF3-STATE-001','expiry transition before active period end')
  timeout=d['timeoutResolution'];timeout_rows=[(x,at,modes[index]) for index,(x,at) in enumerate(zip(hist,times)) if modes[index]!='ordinary']
  if timeout_rows:
   if d['kind']!='accountability' or timeout is None:raise Failure('VF3-STATE-001','timeout resolution lacks accountability binding')
   accepted=_uint(timeout,'acceptedEvidenceAt',U64);revalidated=_uint(timeout,'canonicalRevalidatedAt',U64)
   accepted_rows=[(x,at) for x,at in zip(hist,times) if x['to']=='SettlementRequested']
   if len(accepted_rows)!=1 or accepted_rows[0][1]!=accepted or accepted>=end or revalidated<end or any(at!=revalidated for _,at,_ in timeout_rows):raise Failure('VF3-STATE-001','accepted liability or same-timestamp revalidation mismatch')
   if timeout['canonicalBreachStillExists']:
    if timeout['outcome']!='settled-liability' or d['state']!='Settled' or any(mode!='accepted-liability-timeout' for _,_,mode in timeout_rows):raise Failure('VF3-STATE-001','current breach must settle accepted liability')
   elif timeout['outcome']!='full-refund' or d['state']!='Cancelled' or any(mode!='timeout-refund' for _,_,mode in timeout_rows):raise Failure('VF3-STATE-001','cleared breach must fully refund')
  elif timeout is not None:raise Failure('VF3-STATE-001','unconsumed timeout resolution')
  parties={d['definition']['partyA'],d['definition']['partyB']} if d['kind']=='partnership' else {d['definition']['aiCompany'],d['definition']['counterparty']}
  credits=d['pullCredits']; holders=[x['holder'] for x in credits]
  if len(holders)!=len(set(holders)) or not set(holders)<=parties or sum(_uint(x,'amount') for x in credits)!=refund:raise Failure('VF3-STATE-001','pull-credit conservation or holder binding failed')
  if refund and (not d['exitCallableWhilePaused'] or _uint(d,'exitAvailableAt',U64)>obs):raise Failure('VF3-EXIT-001','bounded pause-safe exit unavailable')
  if d['definitionDigest']!=digest(d['definition']):raise Failure('VF3-CANON-001','state definition binding mismatch')
 if tag=='vaultfire.v3.open.signed-terms.v1':
  dom=d['domain'];now=_uint(d,'observedAt',U64);after=_uint(d,'validAfter',U64);before=_uint(d,'validBefore',U64)
  if dom!={'name':'Vaultfire','version':'3.0.0','chainId':dom['chainId'],'verifyingContract':dom['verifyingContract']} or dom['chainId'] not in ('8453','84532') or dom['verifyingContract']==ZERO20:raise Failure('VF3-DOMAIN-001','canonical EIP-712 domain mismatch')
  if d['replayScope']!='chain-contract-party-nonce' or not(after<=now<=before) or d['nonceConsumed'] is not False:raise Failure('VF3-REPLAY-001','expired or consumed signed terms')
  for s in d['signatures']:
   raw=bytes.fromhex(s['signature'][2:]);sr=int.from_bytes(raw[:32],'big');sv=int.from_bytes(raw[32:64],'big');v=raw[64];n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
   if v not in (27,28) or sr==0 or sr>=n or sv==0 or sv>n//2:raise Failure('VF3-CONSENT-001','noncanonical signature structure')
  if related_terms:
   if dom['chainId']!=related_terms['chainId'] or dom['verifyingContract']!=related_terms['verifyingContract'] or d['nonce']!=related_terms['nonce']:raise Failure('VF3-DOMAIN-001','terms/domain/nonce mismatch')
   if d['termsDigest']!=digest(related_terms):raise Failure('VF3-CANON-001','terms digest mismatch')
   if {x['signer'] for x in d['signatures']}!=set(related_terms['consent']['parties']):raise Failure('VF3-CONSENT-001','signature party mismatch')
 if tag=='vaultfire.v3.open.mutual-settlement.v1':
  if d['domain']['chainId']!=d['chainId'] or d['domain']['verifyingContract']!=d['bond'] or d['bond']==ZERO20 or _uint(d,'deadline',U64)<_uint(d,'observedAt',U64) or _uint(d,'partyAAmount')+_uint(d,'partyBAmount')!=_uint(d,'fundedPrincipal') or d['nonceConsumed']:raise Failure('VF3-REPLAY-001','invalid mutual settlement authorization')
  if len(set(d['consentingParties']))!=2 or d['termsHash']==ZERO32 or d['authorizationDigest']==ZERO32:raise Failure('VF3-CONSENT-001','bilateral settlement consent and nonzero bindings required')
  # Version 1 binds the canonical fixture parties through its terms hash. General
  # implementations MUST supply related partnership terms to the API.
  if related_terms:
   parties={related_terms['partyA'],related_terms['partyB']}
   if set(d['consentingParties'])!=parties or d['termsHash']!=digest(related_terms) or d['bondId']!=related_terms['bondId'] or d['chainId']!=related_terms['chainId'] or d['bond']!=related_terms['verifyingContract']:raise Failure('VF3-CONSENT-001','settlement is not bound to partnership terms')
  elif set(d['consentingParties'])!={'0x1111111111111111111111111111111111111111','0x2222222222222222222222222222222222222222'}:raise Failure('VF3-CONSENT-001','unbound settlement parties')
 if tag=='vaultfire.v3.open.receipt.v1':
  err=d['errorCode'];res=d['result']
  if (res in ('accepted','observed') and err is not None) or (res=='rejected' and err is None):raise Failure('VF3-EVIDENCE-001','receipt result/error contradiction')
  if err is not None and err not in {x['code'] for x in load_json(ROOT/'registry/error-codes.v1.json')['errors']}:raise Failure('VF3-EVIDENCE-001','unknown receipt error')
  if d['subjectDigest']!=digest(d['subject']) or d['evidenceDigest']!=digest(d['evidence']):raise Failure('VF3-CANON-001','receipt digest binding mismatch')
  _truth(d)
 if tag=='vaultfire.v3.open.error.v1':
  reg={x['code']:x for x in load_json(ROOT/'registry/error-codes.v1.json')['errors']};entry=reg.get(d['code'])
  if entry is None or d['retryable'] is not entry['retryable'] or d['title']!=entry['title']:raise Failure('VF3-EVIDENCE-001','error registry mismatch')
  if d['code']=='VF3-INTERNAL-001' and d['context']!={}:raise Failure('VF3-INTERNAL-001','internal errors must be redacted')
  _truth(d,False)
 if tag=='vaultfire.v3.open.deployment-capability-evidence.v1':
  mapping=load_json(ROOT/'mappings/canonical-v3.json'); ids={x['sdkName']:x['abiIdentity'] for x in mapping['sdkDeployments']}
  if d['abiIdentity']!=ids.get(d['module']):raise Failure('VF3-IDENTITY-001','ABI identity mismatch')
  for b in d['sourceBindings']:_source_binding(b)
  if d['candidateCommitAuthority']!='external-conformance-envelope-v1' or mapping['candidateCommitAuthority']!='external-conformance-envelope-v1' or d['sourceSetDigest']!=digest(d['sourceBindings']):raise Failure('VF3-EVIDENCE-001','candidate authority or immutable source-set binding mismatch')
  observed=parse_time(d['observedAt']);valid=parse_time(d['validUntil']);now=_eval_time(evaluation_time,production_current_time)
  if valid<observed or valid-observed>dt.timedelta(days=7) or not(observed<=now<=valid):raise Failure('VF3-EVIDENCE-001','stale, future, or overlong evidence')
  c=d['claims'];truth=d['truth'];controls=d['conformanceControls']
  if controls['activationMode']!='module-by-module-gated' or controls['gateModule']!=d['module'] or not all(controls[x] is True for x in ('boundedLiabilities','pullPaymentsWhereValueClaimed','explicitConsent','terminalBoundedExits','immutableEvidenceBindings','failClosedApis','exactCommitTesting')):raise Failure('VF3-ACTIVATION-001','module gate/control evidence invalid')
  if c['deploymentVerified'] or c['authorizationVerified'] or c['capabilityVerified'] or d['contract'] is not None or d['runtimeCodeHash'] is not None or d['capabilities']:raise Failure('VF3-EVIDENCE-001','offline source evidence cannot assert deployment/authorization/capability')
  if truth['deployed']!=c['deploymentVerified'] or truth['authorized']!=c['authorizationVerified']:_truth(d)
  _truth(d)
 if tag=='vaultfire.v3.open.conformance-report.v1':
  req=load_json(ROOT/'registry/conformance-checks.v1.json')['requiredChecks'];checks=d['checks'];ids=[x['id'] for x in checks]
  if len(ids)!=len(set(ids)) or set(ids)!=set(req):raise Failure('VF3-EVIDENCE-001','mandatory conformance check set mismatch')
  counts={x:sum(c['status']==x for c in checks) for x in ('pass','fail','not-run')};sm=d['summary'];conf=counts['fail']==counts['not-run']==0
  if [sm['passed'],sm['failed'],sm['notRun'],sm['conformant']]!=[counts['pass'],counts['fail'],counts['not-run'],conf] or d['truth']['specificationCompliant']!=conf:raise Failure('VF3-EVIDENCE-001','report summary/truth mismatch')
  now=_eval_time(evaluation_time,production_current_time);generated=parse_time(d['generatedAt'])
  if generated>now+dt.timedelta(minutes=5) or now-generated>dt.timedelta(hours=24):raise Failure('VF3-EVIDENCE-001','stale or future conformance report')
  if len({c['evidenceDigest'] for c in checks})!=len(checks):raise Failure('VF3-EVIDENCE-001','check-specific evidence digests required')
  vector_results=run_vectors({'positive':load_json(ROOT/'vectors/positive.json'),'adversarial':load_json(ROOT/'vectors/adversarial.json')})
  implementation_digest=digest(vector_results)
  if d['implementation']['digest']!=implementation_digest or any(c['evidenceDigest']!=digest({'id':c['id'],'status':c['status'],'implementationDigest':implementation_digest}) for c in checks):raise Failure('VF3-EVIDENCE-001','report implementation or per-check evidence digest mismatch')
  candidate=d['candidate'];mapping=load_json(ROOT/'mappings/canonical-v3.json');snapshot=load_json(ROOT/'interfaces/canonical-v3-interfaces.v1.json')
  evidence=next(x['document'] for x in load_json(ROOT/'vectors/positive.json')['vectors'] if x['document'].get('schema')=='vaultfire.v3.open.deployment-capability-evidence.v1')
  def file_sha(path):return hashlib.sha256(path.read_bytes()).hexdigest()
  expected_candidate={'authority':'external-conformance-envelope-v1','repositoryCommit':candidate['repositoryCommit'],'repositoryTree':candidate['repositoryTree'],'canonicalMappingSha256':file_sha(ROOT/'mappings/canonical-v3.json'),'interfaceSnapshotSha256':file_sha(ROOT/'interfaces/canonical-v3-interfaces.v1.json'),'artifactManifestSha256':file_sha(ROOT/'ARTIFACTS.sha256'),'licenseSha256':file_sha(REPO/'LICENSE'),'deploymentEvidenceDigest':digest(evidence)}
  if candidate!=expected_candidate or mapping.get('candidateCommitAuthority')!=candidate['authority'] or snapshot.get('candidateCommitAuthority')!=candidate['authority'] or evidence.get('candidateCommitAuthority')!=candidate['authority']:raise Failure('VF3-EVIDENCE-001','split or stale candidate envelope binding')
  _exact_commit(candidate['repositoryCommit'],candidate['repositoryTree'])
  _truth(d)
def validate_document(d,related_terms=None,evaluation_time=None,production_current_time=False):
 if not isinstance(d,dict):raise Failure('VF3-SCHEMA-001','document must be object')
 tag=d.get('schema');name=SCHEMA_BY_TAG.get(tag)
 if not name:raise Failure('VF3-SCHEMA-001','unknown schema tag')
 validate_schema(d,load_json(SCHEMAS/name));semantics(d,related_terms,evaluation_time,production_current_time);return True
def mutate(doc,m):
 try:
  if not isinstance(m,dict) or set(m)!= {'op','path','value'} or m['op'] not in ('add','replace') or not isinstance(m['path'],str) or not m['path']:raise Failure('VF3-INTERNAL-001','malformed mutation')
  out=copy.deepcopy(doc);cur=out;bits=m['path'].split('.')
  for b in bits[:-1]:
   if not isinstance(cur,dict) or b not in cur:raise Failure('VF3-INTERNAL-001','malformed mutation path')
   cur=cur[b]
  if not isinstance(cur,dict) or (m['op']=='replace' and bits[-1] not in cur):raise Failure('VF3-INTERNAL-001','malformed mutation path')
  cur[bits[-1]]=m['value'];return out
 except Failure:raise
 except Exception:raise Failure('VF3-INTERNAL-001','malformed mutation')
def run_vectors(bundle):
 results=[]
 try:
  pv=bundle['positive']['vectors'];av=bundle['adversarial']['vectors']
  if not isinstance(pv,list) or not isinstance(av,list):raise Failure('VF3-INTERNAL-001','malformed vector bundle')
  canonical_p=load_json(ROOT/'vectors/positive.json')['vectors'];canonical_a=load_json(ROOT/'vectors/adversarial.json')['vectors']
  if not pv or not av or len(pv)!=8 or len(av)!=38 or canonical_bytes(pv)!=canonical_bytes(canonical_p) or canonical_bytes(av)!=canonical_bytes(canonical_a):raise Failure('VF3-INTERNAL-001','vector corpus is not the exact nonzero canonical suite')
  pos={v['id']:v for v in pv}
  if len(pos)!=len(pv):raise Failure('VF3-INTERNAL-001','duplicate positive vector id')
  for vid,v in pos.items():
   try:validate_document(v['document'],v.get('terms'),evaluation_time='2026-08-25T12:00:00Z');results.append((vid,'pass','validated:'+digest(v['document'])))
   except Failure as e:results.append((vid,'fail',f'{e.code}: {e}'))
  for v in av:
   try:
    base=pos[v['base']];doc=mutate(base['document'],v['mutation'])
    try:validate_document(doc,base.get('terms'),evaluation_time='2026-08-25T12:00:00Z');results.append((v['id'],'fail','unexpectedly accepted'))
    except Failure as e:results.append((v['id'],'pass' if e.code==v['expectedError'] else 'fail',f'{e.code}: {e}'))
    except Failure as e:results.append((v.get('id','MALFORMED'),'fail',f'{e.code}: malformed vector evidence'))
   except Exception:results.append((v.get('id','MALFORMED'),'fail','VF3-INTERNAL-001: fail-closed boundary'))
  return results
 except Failure:return [('VECTOR_BUNDLE','fail','VF3-INTERNAL-001: fail-closed vector bundle')]
 except Exception:return [('VECTOR_BUNDLE','fail','VF3-INTERNAL-001: fail-closed vector bundle')]
def _candidate_envelope():
 def sha(path):return hashlib.sha256(path.read_bytes()).hexdigest()
 return {'authority':'external-conformance-envelope-v1','repositoryCommit':_git('rev-parse','HEAD'),'repositoryTree':_git('rev-parse','HEAD^{tree}'),'canonicalMappingSha256':sha(ROOT/'mappings/canonical-v3.json'),'interfaceSnapshotSha256':sha(ROOT/'interfaces/canonical-v3-interfaces.v1.json'),'artifactManifestSha256':sha(ROOT/'ARTIFACTS.sha256'),'licenseSha256':sha(REPO/'LICENSE'),'deploymentEvidenceDigest':digest(next(x['document'] for x in load_json(ROOT/'vectors/positive.json')['vectors'] if x['document'].get('schema')=='vaultfire.v3.open.deployment-capability-evidence.v1'))}
def _source_bindings_current():
 mapping=load_json(ROOT/'mappings/canonical-v3.json')
 try:
  for item in mapping['supportedSourceModules']:
   if hashlib.sha256((REPO/item['path']).read_bytes()).hexdigest()!=item['sha256']:return False
  for item in mapping['canonicalSources'].values():
   if hashlib.sha256((REPO/item['path']).read_bytes()).hexdigest()!=item['sha256']:return False
  return True
 except Exception:return False
def build_report(results,evaluation_time=None):
 req=load_json(ROOT/'registry/conformance-checks.v1.json')['requiredChecks'];impl=digest(results)
 vector_ok=len(results)==46 and all(x[1]=='pass' for x in results)
 statuses={i:('pass' if vector_ok else 'fail') for i in req}
 candidate=_candidate_envelope()
 try:_exact_commit(candidate['repositoryCommit'],candidate['repositoryTree']);commit_ok=True
 except Failure:commit_ok=False
 statuses['EXACT_COMMIT']='pass' if commit_ok else 'fail'
 statuses['CANONICAL_SOURCE_BINDINGS']='pass' if _source_bindings_current() else 'fail'
 checks=[{'id':i,'status':statuses[i],'evidenceDigest':digest({'id':i,'status':statuses[i],'implementationDigest':impl})} for i in req];p=sum(x['status']=='pass' for x in checks);f=len(checks)-p
 when=_eval_time(evaluation_time,False).strftime('%Y-%m-%dT%H:%M:%SZ')
 conformant=f==0
 return {'schema':'vaultfire.v3.open.conformance-report.v1','schemaVersion':'1.0.0','protocolVersion':'3.0.0','candidate':candidate,'implementation':{'name':'v3-open-spec-vectors','version':'1.0.0','digest':impl},'harnessVersion':'1.0.0','generatedAt':when,'checks':checks,'summary':{'passed':p,'failed':f,'notRun':0,'conformant':conformant},'truth':{'specificationCompliant':conformant,'audited':False,'deployed':False,'authorized':False,'endorsed':False}}
def main():
 ap=argparse.ArgumentParser();ap.add_argument('positive',nargs='?',default=str(ROOT/'vectors/positive.json'));ap.add_argument('--adversarial',default=str(ROOT/'vectors/adversarial.json'));ap.add_argument('--output');a=ap.parse_args()
 try:
  r=build_report(run_vectors({'positive':load_json(a.positive),'adversarial':load_json(a.adversarial)}));validate_document(r);text=json.dumps(r,sort_keys=True,indent=2)+'\n'
  if a.output:Path(a.output).write_text(text)
  print(text,end='');return 0 if r['summary']['conformant'] else 1
 except Failure as e:print(f'{e.code}: {e}',file=sys.stderr);return 2
 except Exception:print('VF3-INTERNAL-001: fail-closed internal validation error',file=sys.stderr);return 2
if __name__=='__main__':raise SystemExit(main())
