healing 이 하는 일
모든 conversation read 마다 두 함수 실행: _heal_incomplete_turns + _cleanup_dangling_parent_ids.
재구성
JSONL walk, completed assistant child 없는 user message 찾기, streaming delta 누적 (pippa.user_msg_id 로 keyed), synthesized assistant row 를 SQLite 에 기록. user.created_at + 1µs 로 timestamp — heal 시점 아님. 그래야 ORDER BY created_at 이 자연 chain.
정규화
conversation 의 existing message 를 reference 안 하는 parent_id cleanup, created_at 순 이전 message 로 re-parent. ghost-branch mechanism 의 root 죽임 — dangling reference 완전 제거하니까 미래 placeholder 가 match 못함.
왜 모든 read 마다
healing 이 모든 GET 에 idempotently. 별도 'repair' 버튼 없음. 비용 낮음 (어차피 JSONL 읽고 있으니), 어떤 과거 client 버전의 corrupted 상태도 화면에 도달 못 함 의미.
Healing이 무서운 이유도 있어. repair code가 원본보다 더 많은 사실을 상상하면 조용한 data corruption machine이 돼. 그래서 reconstruction은 JSONL에 이미 있는 delta와 stable id만 쓰고, normalization도 resolve되지 않는 parent를 이미 정렬된 이전 message에 붙이는 좁은 규칙만 써. "아마 이런 대화였겠지"는 금지야.
Idempotence를 꼭 검증해. 같은 conversation을 열 번 읽어도 첫 pass 뒤에는 row 수와 parent chain이 더 바뀌지 않아야 해. healing이 매 read마다 조금씩 다른 답을 만들면 치료가 아니라 drift야. 자동 복구는 사람이 버튼을 누르지 않아도 된다는 장점만큼, 반복해도 같은 결과라는 증명이 필요해.