Skip to content

bug(renderer): superseded ghosts of the live turn are evicted and re-minted un-superseded #730

Closed

Description

@Juliusolsson05

Problem (found during the #725 review; not introduced by it)

gcSupersededGhosts (src/renderer/src/session-runtime/ghosts.ts) evicts a superseded ghost 5 s after supersedure regardless of whether its turn is still semantic.currentTurn. ghostsFromSemanticTurn runs on every semantic tick and re-mints any block of currentTurn whose uuid is missing from the map (if (!existing) createGhost(...)).

So for a turn whose committed JSONL entry lands while the turn is still live (a tool still running, the next block still streaming), the sequence is: supersede → 5 s → evict → next tick re-mints the same (turnId, blockIndex) uuid un-superseded with a fresh updatedAt. The authoritative entry is already in runtime.entries, so reconcileUpstream never sees a new match for it; the ghost orphans 30 s later with updatedAt newer than the JSONL tail and, once the turn leaves semantic history, rule 4 passes and rule 5 only catches single short text blocks.

Expected

A superseded ghost of the live current turn stays in the map (hidden by rule 1) until the turn is no longer current, the same exemption #725 added to gcHiddenOrphanGhosts.

Evidence / impact

Plausible contributor to the 917-ghost session on the 2026-09-01 → 09-03 journal and to duplicate-row paints of long-running tool turns. Needs a reproduction (long tool call, JSONL lands mid-turn, wait > 35 s) before fixing.

Refs #724, #725.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    class:C8-renderingRendering order/duplicatessev:P2Real bug with a workaroundtype:bugSomething works wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions