Repository navigation
feat(rendering): block-level un-collapse — retire SemanticStreamingTurn (#491) - #492
Conversation
…rn, ledger is sole live-turn decider (#491) The last slice of the rendering rewrite. The ledger already decided live-turn visibility at BLOCK grain, but the view bridge collapsed every turn back into one item so SemanticStreamingTurn could render it — and that component then RE-DERIVED the same suppression (buildSemanticRenderUnits: text dedup, committed-ownership, collapsed-running). Two decision-makers for live turns, kept in sync by hand. This kills the duplicate. - View bridge (ledgerFeedItems.ts) no longer collapses: it emits ONE FeedRenderItem per ledger-approved semantic block — `semantic-block`, `semantic-collapsed-activity` (churn fold), or blockless `semantic-text`. Groups by turnId (not adjacency) so equal-timestamp interleaved turns still emit contiguously. Fetches each block's rich payload from turnsById (the candidate carries only turnId+blockIndex). - `groupSemanticActivity` (renderUnits.ts): pure presentation grouping extracted from buildSemanticRenderUnits — NO suppression (the ledger owns that now). - Feed renders the three block items via the dumb SemanticLiveBlockRow / SemanticCollapsedActivityRow / MarkerRow+StreamingProse. history/current debug + work-hint signals re-derived from the items' `owner` tag. - DELETED: SemanticStreamingTurn (StreamingTurn.tsx), and the now-dead deriveFeedCommittedProjection / committedClaudeMessageTurnIds / committedMessageIds / visibleDecisionForEntry it only fed. - RETAINED with non-render owners (documented in the manifest): buildSemanticRenderUnits (the #239 semanticTurnHasRenderableContent predicate + debug bundles), selectMergedEntries (Feed's ghost-folded entries source), ghost-semantic-owned (a ghost-vs-live-turn dedup, not a component artifact). RISK: shadow parity only ever verified TURN grain — this exposes the ledger's per-block decisions to the pixels for the first time. Guarded by a new block-grain bridge test (text + churn-fold + non-churn block + payload identity) and the corpus; the residual is VISUAL and needs an eyeball on a build before merge. Gates: tsc node+web clean; rendering 164, unit 430, renderer 14, integration 28. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ion parity + bridge accounting A 2-Claude + 2-Codex orchestration review of the un-collapse found 4 real issues (and validated the grouping/interleave/block-fetch structure as correct). All fixed + locked with block-grain tests: - semantic.ts finding 1 (Claude, CONFIRMED): a write_stdin tool block always has text:'' — the chars ride parsedInput/argumentsJson/inputJson. The collector checked b.text and so suppressed EVERY write_stdin, hiding real Codex stdin from the live feed. Now reads the chars via writeStdinChars (mirrors the retired renderUnits helper). Latent ledger bug the un-collapse exposed. - semantic.ts finding 2 (Claude): text ownership keyed on finalized===true only, but a Codex message can be status:'completed' without finalized (the isTerminalProxyBlock equivalence). A completed-but-unfinalized reply escaped text dedup and could double-render under the resp_* vs rollout id split. Now keys on `finalized || status==='completed'`. - ledgerFeedItems.ts finding (Codex): a RUNNING collapsed_activity was emitted as an item, but CollapsedActivityRow null-paints while running — a null-painting row the ledger counted as content (false-ownership). The bridge now skips it (mirrors semanticRenderUnitPaintsDom). - ledgerFeedItems.ts finding (both Codex): a missing multi-block turn only pushed its first candidate to dropped[] (turnId marked emitted before the lookup), so the rest were silently skipped — the #239 accounting gap. Now pushes all the turn's candidate ids. Codex #1's "grouping reorders ledger rows" was REFUTED by Claude: order.ts breaks equal-timestamp ties on SOURCE_RANK before per-turn sequence, so a committed row can never fall between a turn's blocks — group-by-turn is safe. New tests: write_stdin chars (parsedInput + argumentsJson), status:completed text key, running-churn emits no collapsed item. Gates: tsc clean; rendering 166, unit 432, renderer 14, integration 28. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Orchestration review (2 Claude + 2 Codex) — findings addressedDispatched a 4-agent review fleet via the built-in orchestration MCP. It validated the structure (grouping is a faithful copy of the old grouping half; group-by-turn interleave is safe per
Refuted: Codex #1's "grouping reorders ledger rows" — Claude proved Findings 1 & 2 were latent ledger bugs that only mattered once this PR made the ledger's per-block decisions authoritative for the pixels — exactly the block-grain risk the PR flagged, now pinned and fixed. New tests cover all three suppression paths + the accounting. Gates: tsc node+web clean · rendering 166 · unit 432 · renderer 14 · integration 28. |
Closes #491 — the final slice of the rendering rewrite.
What
The ledger already decided live-turn visibility at block grain, but the view bridge collapsed every turn into one item so
SemanticStreamingTurncould draw it — and that component then re-derived the same suppression (buildSemanticRenderUnits). Two decision-makers for live turns, synced by hand. This removes the duplicate: the bridge now emits one item per ledger-approved block, drawn by a dumb row.ledgerFeedItems.ts): emitssemantic-block/semantic-collapsed-activity(churn fold) / blocklesssemantic-text. Groups by turnId (not adjacency) so equal-timestamp interleaved turns stay contiguous; fetches each block's rich payload fromturnsById.groupSemanticActivity: pure presentation grouping extracted frombuildSemanticRenderUnits— no suppression (the ledger owns it).SemanticLiveBlockRow/SemanticCollapsedActivityRow/MarkerRow+StreamingProse; debug/work-hint signals re-derived from each item'sowner.SemanticStreamingTurn+ the now-deadderiveFeedCommittedProjection/committedClaudeMessageTurnIds.buildSemanticRenderUnits(Optimistic submitted user prompts are buried behind semantic/work rows #239 predicate + debug),selectMergedEntries(entries source),ghost-semantic-owned(ghost-vs-live dedup — not a component artifact).Shadow parity only ever verified turn grain — this exposes the ledger's per-block decisions to the pixels for the first time. It's guarded by a new block-grain bridge test (text + churn-fold + non-churn block + payload identity) and the full corpus, but the residual risk is visual. Please eyeball a build across: Claude + Codex · streaming text · live tool_use (Edit/Write/TodoWrite/Bash/MCP) · tool_result · thinking · collapsed churn ("worked: N reads") · AskUserQuestion · no-blocks Codex text.
Gates
tsc node+web clean · rendering 164 · unit 430 · renderer 14 · integration 28 — all green. −345/+374,
SemanticStreamingTurn(149 lines) deleted.🤖 Generated with Claude Code