@@ -129,6 +129,13 @@ export function SwarmViz({ state, onNavigateToChat }: SwarmVizProps) {
129129 const [ currentTurn , setCurrentTurn ] = useState ( 0 ) ;
130130 const [ playing , setPlaying ] = useState ( false ) ;
131131 const [ speed , setSpeed ] = useState ( 1 ) ;
132+ // Replay (`?replay=<id>`) is cached playback (no new LLM cost). The run
133+ // reducer still flips `isRunning` true while replayed status events
134+ // stream, so gate on replay too: treat cached playback as non-live so
135+ // the VIZ suppresses the ambient wash just like completed/loaded runs.
136+ const isReplaying = typeof window !== 'undefined'
137+ && new URLSearchParams ( window . location . search ) . has ( 'replay' ) ;
138+ const isLiveRun = state . isRunning && ! isReplaying ;
132139
133140 // Grid mode (new living-colony grid). Shared across both leaders so
134141 // tabs toggle in lockstep. Persisted to localStorage so the user's
@@ -1313,7 +1320,7 @@ export function SwarmViz({ state, onNavigateToChat }: SwarmVizProps) {
13131320 >
13141321 < LivingSwarmGrid
13151322 snapshot = { snapA }
1316- isLiveRun = { state . isRunning }
1323+ isLiveRun = { isLiveRun }
13171324 previousSnapshot = { prevSnapA }
13181325 snapshotHistory = { snapsA }
13191326 actorName = { leaderA ?. name ?? 'Leader A' }
@@ -1356,7 +1363,7 @@ export function SwarmViz({ state, onNavigateToChat }: SwarmVizProps) {
13561363 >
13571364 < LivingSwarmGrid
13581365 snapshot = { snapB }
1359- isLiveRun = { state . isRunning }
1366+ isLiveRun = { isLiveRun }
13601367 previousSnapshot = { prevSnapB }
13611368 snapshotHistory = { snapsB }
13621369 actorName = { leaderB ?. name ?? 'Leader B' }
0 commit comments