            original=sum((datetime.fromisoformat(r['end'])-datetime.fromisoformat(r['start'])).total_seconds()/3600 for r in data['entries']);people=defaultdict(list);overlaps=[]
            for r in data['entries']:people[r['person']].append(r)
            unique=0
            for person,entries in people.items():
                ordered=sorted(entries,key=lambda r:r['start']);merged=[]
                for r in ordered:
                    start,end=datetime.fromisoformat(r['start']),datetime.fromisoformat(r['end'])
                    for other in ordered:
                        if other['id']>=r['id']:continue
                        overlap=(min(end,datetime.fromisoformat(other['end']))-max(start,datetime.fromisoformat(other['start']))).total_seconds()/3600
                        if overlap>0:overlaps.append({'person':person,'first':other['id'],'second':r['id'],'hours':overlap})
                    if merged and start<=merged[-1][1]:merged[-1]=(merged[-1][0],max(merged[-1][1],end))
                    else:merged.append((start,end))
                unique+=sum((end-start).total_seconds()/3600 for start,end in merged)
            check('原记录7小时不被偷偷删除',original==7);check('按人区间并集6小时',unique==6);check('跨项目重叠一小时可回查',overlaps==[{'person':'A','first':'T1','second':'T2','hours':1.0}]);cout('overlap-anomalies.csv',overlaps);result={'raw_hours':original,'union_hours':unique,'overlaps':overlaps,'model_reasons':output['reasons']}
