from pathlib import Path
import json,sys
ROOT=Path(__file__).resolve().parents[1]; checks=[]
def ck(n,v): checks.append((n,bool(v)))
base=json.loads((ROOT/'fixture/baseline-state.json').read_text()); cases=json.loads((ROOT/'fixture/probe-cases.json').read_text())['cases']; pol=json.loads((ROOT/'fixture/golden-policy.json').read_text()); req=json.loads((ROOT/'requirements.json').read_text()); tool=json.loads((ROOT/'toolchain-policy.json').read_text()); dec=json.loads((ROOT/'submission/decision.template.json').read_text()); status=json.loads((ROOT/'evidence/validation-status.json').read_text()); run=(ROOT/'src/run_local_lab.py').read_text(); fix=(ROOT/'src/outbox_fixture.py').read_text()
ids=['baseline_transactional_outbox_commit','direct_dual_write_publish_failure','relay_publish_before_mark_crash_retry','reconciliation_old_pending_intent','evidence_field_removed']
ck('order starts pending',base['service']['initial_order_state']=='PENDING'); ck('stable event id',base['intent']['event_id']=='evt-order-O-1001-confirmed'); ck('aggregate key',base['intent']['aggregate_key']=='order:O-1001'); ck('payload order parity',base['intent']['payload']['order_id']==base['service']['order_id']); ck('initial mark pending',base['intent']['initial_mark_state']=='PENDING'); ck('threshold 300',base['reconciliation']['stuck_threshold_seconds']==300); ck('old age above threshold',base['reconciliation']['old_pending_age_seconds']>base['reconciliation']['stuck_threshold_seconds'])
ck('five exact cases',[c['id'] for c in cases]==ids); ck('expected semantics exact',pol.get('required_case_semantics')=={c['id']:c['expected_class'] for c in cases})
ck('five dimensions',req['critical_dimensions']==['dual_write_gap','durable_intent','outbox_transaction','relay_retry','reconciliation']); ck('eight evidence items',len(req['required_evidence'])==8); ck('local-only runtime','External network, broker, database server, cloud service, production data, and production behavior are out of scope.' in req.get('runtime_rule','')); ck('identity payload rule','identity collision' in req.get('event_identity_payload_rule','')); ck('threshold rule','greater than or equal' in req.get('reconciliation_threshold_rule','')); ck('crash truth rule','actual outbox row PENDING' in req.get('crash_window_state_rule',''))
ck('external network forbidden',tool['external_network_forbidden'] is True); ck('external service forbidden',tool['external_service_forbidden'] is True); ck('database server not required',tool['database_server_required'] is False); ck('broker runtime false',tool['broker_runtime_claimed'] is False); ck('production runtime false',tool['production_runtime_claimed'] is False); ck('runner v2',tool.get('runner_contract_version')=='DAT018-RUNNER-CONTRACT-v2'); ck('expected ids exact',tool.get('expected_case_ids')==ids); ck('prediction ids exact',tool.get('required_prediction_ids')==ids); ck('eight provenance keys',len(tool.get('runtime_input_hashes_required',[]))==8); ck('bounded evidence scope',tool.get('evidence_scope')=='BOUNDED_LOCAL_FIXTURE_ONLY')
ck('five blank predictions',list(dec.get('predictions',{}).keys())==ids and all(v is None for v in dec['predictions'].values())); ck('authoring runtime unclaimed',status.get('runtime_observations_claimed') is False); ck('authoring final decision unclaimed',status.get('final_reliability_decision_claimed') is False); ck('authoring db false',status.get('database_server_runtime_claimed') is False); ck('authoring broker false',status.get('broker_runtime_claimed') is False); ck('authoring production false',status.get('production_runtime_claimed') is False)
ck('confirmation executable gate',"if not args.confirm_local:" in run and 'if False' not in run); ck('runner exact expected execution',"obs=[run_probe(cid) for cid in expected]" in run); ck('runner exact prediction keys',"list(d.get('predictions',{}).keys()) != expected" in run); ck('runner provenance has policy',"'golden_policy_sha256':'fixture/golden-policy.json'" in run); ck('runner provenance has decision',"'learner_decision_sha256':'submission/decision.json'" in run); ck('identity collision guard',"EVENT_IDENTITY_COLLISION_REJECTED" in fix and "current_fp != row['payload_fingerprint']" in fix); ck('crash does not mark',"if crash_after_publish:\n            return" in fix and "if crash_after_publish:\n            row['mark_state']='PUBLISHED'" not in fix); ck('reconciliation threshold executable',"v['age_seconds']>=threshold_seconds" in fix); ck('no network imports',not any(x in fix for x in ['requests','urllib','socket.connect','http://','https://']))
ck('six forbidden claims',len(pol['forbidden_conclusions'])==6); ck('exactly once forbidden',any('exactly-once' in x.lower() for x in pol['forbidden_conclusions'])); ck('duplicate delivery risk preserved',any('retry cannot duplicate delivery' in x.lower() for x in pol['forbidden_conclusions'])); ck('reconciliation overclaim forbidden',any('reconciliation' in x.lower() and 'downstream' in x.lower() for x in pol['forbidden_conclusions'])); ck('payload alias forbidden',any('divergent aggregate or payload' in x.lower() for x in pol['forbidden_conclusions']))
for n,v in checks: print(('PASS' if v else 'FAIL'),n)
print(f'{sum(v for _,v in checks)}/{len(checks)} PASS'); sys.exit(0 if all(v for _,v in checks) else 1)
