Repository navigation
Fix live-preview flicker and changing announcer audio during generation - #30
Conversation
Announcer/character/meta preview emits ran outside Inngest steps, so they re-fired on every step replay and the announcer audio (a Math.random pick) was re-rolled each replay. The client accepted every emit, causing the preview to constantly re-refresh and the announcer voice to change mid-run. Wrap those emits in step.run and restore liveState via Object.assign on replay (the pattern roundBundle/finalisePipeline already use), so each emit fires once and the random announcer audio is memoized. Add a monotonic version guard on the client so stale/duplicate realtime deliveries can't rewind the preview. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Free Run ID: 📒 Files selected for processing (3)
📝 WalkthroughSummary by CodeRabbit
Walkthrough
ChangesMonotonic preview updates
Replay-safe bundle emissions
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant characterBundle
participant stepRun
participant emitAnnouncer
participant stateUpdateAndEmit
characterBundle->>stepRun: run emit-character-cards
stepRun->>emitAnnouncer: emit when announcer is not ready
stepRun->>stateUpdateAndEmit: update character1 and character2
stateUpdateAndEmit-->>stepRun: return updated liveState
stepRun-->>characterBundle: return cached liveState on replay
stepRun-->>characterBundle: restore liveState.data
Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
Problem
While a video generates, the live preview page constantly re-refreshed and the announcer audio changed partway through the run.
Root cause — Inngest determinism violation. Inngest re-executes the function body from the top on every step boundary, running any non-step code again. In
characterBundleandmetaBundle, the announcer/character/meta emits (stateUpdateAndEmit) ran outsidestep.run, so:<Player>re-rendered repeatedly ("video keeps refreshing"), andcreateCommonPreviewAssets()picks the announcer audio withMath.random(), and running outside a step re-rolled it on each replay → the announcer voice changed mid-preview.roundBundleandfinalisePipelinealready avoided this by emitting inside a step and restoringliveStateviaObject.assignon replay.Fix
Apply that same, already-established pattern to the two bundles that didn't follow it:
characterBundle.ts— announcer + char1 + char2 emits moved intostep.run("emit-character-cards", …), thenObject.assign(liveState.data, emittedState.data). Each emit now fires once, and the random announcer audio is memoized (rolled once, reused on replay).metaBundle.ts— meta emit moved intostep.run("emit-meta", …)with the same restore.livePlayer.tsx— realtimeonDatanow guardssetLiveStateondata.data.version > prev.data.version, so stale/duplicate/out-of-order deliveries (realtime is at-least-once) can't rewind the preview. Backstop only; the completed-manifest path is unchanged.roundBundle/finalisePipelineuntouched (already correct).Why
Object.assignis requiredstateUpdateAndEmitmutates the sharedliveState. A memoized step's body doesn't re-run on replay, so without the restore, earlier segments would be dropped fromliveStateand the final saved manifest.Object.assignre-hydrates the cached snapshot, keepingversionmonotonic across the stream.Verification
pnpm typecheck— cleanpnpm test— 33/33 passNot exercised end-to-end here (needs the Inngest dev server + external AI/TTS/R2 calls); recommend a live generation run to confirm the announcer audio stays fixed and the preview stops re-refreshing.
🤖 Generated with Claude Code