Skip to content

fix(replay): surface compressed observation content in replay detail#1023

Open
xianfengpolo wants to merge 1 commit into
rohitg00:mainfrom
xianfengpolo:fix/replay-compressed-observation-detail
Open

fix(replay): surface compressed observation content in replay detail#1023
xianfengpolo wants to merge 1 commit into
rohitg00:mainfrom
xianfengpolo:fix/replay-compressed-observation-detail

Conversation

@xianfengpolo

@xianfengpolo xianfengpolo commented Jul 7, 2026

Copy link
Copy Markdown

Problem

The Replay tab in the viewer steps through session events correctly, but the detail panel is empty for every event — only a label and timestamp are shown.

Root Cause

rawFromCompressed() in src/functions/replay.ts was the compatibility shim between the compressed storage format and the RawObservation shape expected by projectTimeline. It had two bugs:

  1. Wrong hookType for all events — hardcoded "post_tool_use" regardless of observation type, so conversation events never rendered as prompts.
  2. Tool fields discardedtoolName, toolInput, and toolOutput were all undefined. The compressed format does retain this data under different field names (title = tool name, subtitle = raw input, narrative = combined output), but the shim never mapped them.

Since the runtime compresses every observation (both live-capture and jsonl-import paths), every replay session was affected.

Fix

Branch on obs.type in rawFromCompressed:

  • "conversation"hookType: "prompt_submit", userPrompt ← narrative
  • everything else (command_run, file_read, …) → hookType: "post_tool_use", toolName ← title, toolInput ← subtitle, toolOutput ← narrative

assistantResponse stays undefined — the compression pass doesn't preserve a separate assistant-response field.

The function is also exported (it was previously unexported) so it can be directly unit-tested. Precedent: src/cli/doctor-diagnostics.ts does the same for its pure helpers.

Tests

Four new test cases added to test/replay.test.ts in the new rawFromCompressed describe block:

Test Verifies
command_run with subtitle hookType, toolName, toolInput, toolOutput all populated
conversation hookType:"prompt_submit", userPrompt populated, tool fields undefined
tool obs with no subtitle gracefully maps toolInput to undefined without throwing
end-to-end via projectTimeline TimelineEvent carries toolName/toolInput/toolOutput/body
Test Files  1 passed (1)
     Tests  17 passed (17)   ← 13 original + 4 new
  Duration  235ms

TypeScript typecheck (npx tsc --noEmit): zero errors in changed files (pre-existing errors in unrelated files are unchanged).

Checklist

  • Reproduces the bug (empty detail panel for every event)
  • All 17 replay tests pass
  • No new TypeScript errors introduced
  • Comments explain the why (compression-drops-raw regression context)

Summary by CodeRabbit

  • Bug Fixes

    • Timeline and replay views now show the correct details for conversation and tool events.
    • Prompt submissions and tool runs are mapped to the right display fields, so event content is no longer missing.
    • Optional event details are handled more gracefully, avoiding blank or incomplete timeline entries.
  • Tests

    • Added coverage for replay-to-timeline conversion to verify the expected event content appears end to end.

Replay sessions built from compressed observations (the only format
stored after the live-capture and jsonl-import compression pass) showed
empty detail panels: every event rendered only a label and timestamp
with no tool name, input, output, or prompt text.

Root cause: rawFromCompressed() mapped all observation types uniformly
to hookType:"post_tool_use" while leaving toolName/toolInput/toolOutput
all undefined. Only conversation.userPrompt was partially handled, but
the hook type was still wrong (post_tool_use instead of prompt_submit).

Fix: branch on obs.type —
- "conversation" → hookType:"prompt_submit", userPrompt ← narrative
- everything else → hookType:"post_tool_use", toolName ← title,
  toolInput ← subtitle (optional), toolOutput ← narrative

The compressed schema stores exactly these fields during compression
(title = tool name, subtitle = raw input, narrative = combined output),
so the mapping is lossless for all data retained after compression.

Also exports rawFromCompressed as a pure helper for direct unit
testing (precedent: src/cli/doctor-diagnostics.ts does the same).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

@xianfengpolo is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The rawFromCompressed function in replay.ts is now exported and rewritten to correctly translate CompressedObservation into RawObservation, branching on observation type to produce prompt submission or tool events with proper field mapping. Corresponding tests were added.

Changes

rawFromCompressed Mapping Fix

Layer / File(s) Summary
Type-aware observation mapping
src/functions/replay.ts
rawFromCompressed is exported and now branches on obs.type: conversation observations map to hookType: "prompt_submit" with userPrompt from narrative; other observations map to hookType: "post_tool_use" with toolName/toolInput/toolOutput from title/subtitle/narrative.
Regression and integration tests
test/replay.test.ts
Adds rawFromCompressed imports, a makeCompressed factory, and tests validating tool vs conversation mapping, missing subtitle handling, and timeline event integration via projectTimeline.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: exposing compressed observation content in replay detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/functions/replay.ts (1)

49-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the WHAT-explaining comment.

Lines 60-64 restate the mapping logic that's already self-evident from the code (isConversation ternaries). Per path instructions, prefer clear naming over comments explaining what the code does; keep only the rationale/history if useful.

As per path instructions: "In TypeScript source code, avoid code comments explaining WHAT — use clear naming instead."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/functions/replay.ts` around lines 49 - 66, The block comment above
replayObservation is too detailed about WHAT the code does and duplicates the
branch logic already shown by the isConversation ternaries. Trim it down by
removing the step-by-step mapping bullets and keep only the brief
rationale/history for why the shim exists, so the function name and branching
remain the primary documentation.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/functions/replay.ts`:
- Around line 49-66: The block comment above replayObservation is too detailed
about WHAT the code does and duplicates the branch logic already shown by the
isConversation ternaries. Trim it down by removing the step-by-step mapping
bullets and keep only the brief rationale/history for why the shim exists, so
the function name and branching remain the primary documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b2f35464-7af5-49b2-a76f-ebc186c1035e

📥 Commits

Reviewing files that changed from the base of the PR and between 93ae9bc and 2e6dfc6.

📒 Files selected for processing (2)
  • src/functions/replay.ts
  • test/replay.test.ts

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant