C.W.K.
Lesson 04 of 06 · published

The Healing Layer — Reconstruction + Normalization

~16 min · healing, normalization

Level 0Curious
0 XP0/52 lessons0/16 achievements
0/100 XP to next level100 XP to go0% complete

What healing does

On every conversation read, two functions run: _heal_incomplete_turns and _cleanup_dangling_parent_ids.

Reconstruction

Walk the JSONL, find any user message with no completed assistant child, accumulate the streaming deltas (keyed by pippa.user_msg_id), write a synthesized assistant row to SQLite. Timestamp it at user.created_at + 1µs, not at heal-time — so ORDER BY created_at produces the natural chain.

Normalization

Cleanup any parent_id that doesn't reference an existing message, re-parent it to the previous message in created_at order. This kills the ghost-branch mechanism at its root — removing the dangling references entirely so no future placeholder can match against them.

The principle: The persistence layer is the long-lived part of the system. Bugs in client code can leave permanent footprints in it. Healing is the last line of defense against those footprints reaching presentation.

Why on every read

Healing runs idempotently on every GET. There's no separate 'repair' button anymore. The cost is low (it's reading the JSONL anyway), and it means corrupted state from any past version of any client can never reach the screen.

Code

The healing pass — sketch·python
async def get_conversation(conversation_id: str) -> Conversation:
    rows = await db.fetch_messages(conversation_id)
    # Reconstruct any user message with no completed assistant child
    rows = await _heal_incomplete_turns(conversation_id, rows)
    # Normalize any parent_id that doesn't resolve
    rows = _cleanup_dangling_parent_ids(rows)
    return Conversation(id=conversation_id, messages=rows)

Progress

Progress is local-only — sign in to sync across devices.