Assistant: codex Assistant-Model: gpt-5.6-luna Assistant-Session: 01a087b0-7b1e-7531-8ef3-6525ebaf7ee7
63 lines
3 KiB
Python
Executable file
63 lines
3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Render FIAM identity query semantics without destructively merging entities."""
|
|
from __future__ import annotations
|
|
import argparse
|
|
from pathlib import Path
|
|
import yaml
|
|
|
|
ROOT=Path(__file__).resolve().parents[1]
|
|
CANON=ROOT/'canon/FIAM-Canon-v0.2.yaml'
|
|
|
|
def evidence_records(e): return e if isinstance(e,list) else ([e] if isinstance(e,dict) else [])
|
|
|
|
def source_signature(r):
|
|
s=r.get('source')
|
|
if isinstance(s,dict): return (s.get('type'),s.get('ref'),s.get('locator'))
|
|
return ('source',str(s),None)
|
|
|
|
def main():
|
|
ap=argparse.ArgumentParser(description=__doc__); ap.add_argument('model',type=Path); ap.add_argument('-o','--output',type=Path)
|
|
args=ap.parse_args(); doc=yaml.safe_load(args.model.read_text()); canon=yaml.safe_load(CANON.read_text())
|
|
policy=canon.get('semantic_policies',{}).get('identity_relations',{})
|
|
threshold=float(policy.get('default_reconciliation_threshold',0.98)); need=int(policy.get('required_independent_signals_for_inferred_promotion',2))
|
|
parent={}
|
|
def find(x):
|
|
parent.setdefault(x,x)
|
|
if parent[x]!=x: parent[x]=find(parent[x])
|
|
return parent[x]
|
|
def union(x,y):
|
|
rx,ry=find(x),find(y)
|
|
if rx!=ry: parent[ry]=rx
|
|
same=[]; corr=[]
|
|
for x in doc.get('assertions',[]) or []:
|
|
if x.get('assertion_kind')!='relation' or x.get('polarity')!='asserted': continue
|
|
if x.get('predicate')=='fiam.relation.same-entity-as':
|
|
union(x['subject'],x['object_ref']); same.append(x)
|
|
elif x.get('predicate')=='fiam.relation.corresponds-to': corr.append(x)
|
|
members={}
|
|
for x in list(parent): members.setdefault(find(x),[]).append(x)
|
|
candidates=[]
|
|
for x in corr:
|
|
recs=evidence_records(x.get('evidence'))
|
|
inferred=[r for r in recs if r.get('mode')=='fiam.evidence.inferred' and float(r.get('confidence',0))>=threshold]
|
|
distinct=len({source_signature(r) for r in inferred})
|
|
candidates.append({
|
|
'subject':x['subject'],'object_ref':x['object_ref'],'assertion_id':x.get('id'),
|
|
'evidence_confidences':[r.get('confidence') for r in recs],
|
|
'meets_default_inferred_signal_threshold': len(inferred)>=need and distinct>=need,
|
|
'automatic_promotion':'never',
|
|
'recommended_next_state':'governed identity review' if len(inferred)>=need and distinct>=need else 'remain corresponds-to'
|
|
})
|
|
result={
|
|
'policy':{
|
|
'query_semantics':'Asserted same-entity-as defines query-time equivalence only; ids and evidence remain distinct.',
|
|
'default_inferred_threshold':threshold,'required_independent_signals':need,
|
|
'automatic_promotion':'disabled'
|
|
},
|
|
'equivalence_classes':[{'members':sorted(v),'merged':False} for v in members.values() if len(v)>1],
|
|
'correspondence_candidates':candidates
|
|
}
|
|
text=yaml.safe_dump(result,sort_keys=False,allow_unicode=True,width=120)
|
|
if args.output: args.output.write_text(text,encoding='utf-8')
|
|
else: print(text,end='')
|
|
if __name__=='__main__': main()
|