fix(chat): flush batched updates at top of onExit on rc (#1022)#1052
fix(chat): flush batched updates at top of onExit on rc (#1022)#1052chr1syy wants to merge 2 commits into
Conversation
) rc companion to RunMaestro#1023. After commit e1b8eed split useAgentListeners into per-channel sub-listeners, the queued-message dispatch race lives in useAgentExitListener. Without a pre-mutation flush, trailing batched stdout chunks from the just-finished response land in tab.logs after the new user log entry, merging with the next response's bubble in TerminalOutput's collapsedLogs grouping. - Plumb batchedUpdater through UseAgentExitListenerDeps and the useAgentListeners coordinator. - Call deps.batchedUpdater.flushNow() at the top of onExit, after the -terminal- guard. - Extend the local BatchedUpdater interface in hooks/agent/internal/types.ts with flushNow (matches the real useBatchedSessionUpdates surface). - Add flushNow: vi.fn() to the makeBatched factory in the 4 sibling listener tests and to the new makeDeps in useAgentExitListener.test. - Add regression test asserting flushNow is called on AI exit. Targeted tests: 27/27 pass across the 5 internal listener test files.
📝 WalkthroughWalkthroughThis pull request adds a ChangesBatchedUpdater flushNow implementation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR ports the batched-update flush fix from
Confidence Score: 4/5Safe to merge; the fix is a minimal, targeted flush call that closes the described visual race without touching the broader exit-handling logic. The core change is correct and well-tested. Two minor observations: src/renderer/hooks/agent/internal/useAgentExitListener.ts — the Important Files Changed
Sequence DiagramsequenceDiagram
participant Agent as AI Agent Process
participant IPC as window.maestro.process.onExit
participant Flush as batchedUpdater.flushNow()
participant Store as useSessionStore (setSessions)
participant UI as TerminalOutput (collapsedLogs)
Agent->>IPC: exit event (sessionId, code)
Note over IPC: guard: skip -terminal- tab IDs
IPC->>Flush: flushNow() ← NEW: drains 200ms timer buffer
Flush->>Store: pending stdout chunks committed atomically
IPC->>Store: setSessions → append user log for queued message
Store->>UI: re-render with correct log order
Note over UI: response chunks appear BEFORE user entry ✓
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@src/renderer/hooks/agent/internal/useAgentExitListener.ts`:
- Around line 62-70: The useEffect in useAgentExitListener registers
window.maestro.process.onExit and calls deps.batchedUpdater.flushNow(), but the
dependency array omits deps.batchedUpdater causing a stale reference; update the
useEffect dependency array to include deps.batchedUpdater (and any stable
identity for deps if needed) so flushNow() is always the current function,
keeping the effect and the window.maestro.process.onExit handler in sync with
changes to deps.batchedUpdater.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 77cdfc7e-22cb-4a85-ae45-094c80a41068
📒 Files selected for processing (8)
src/__tests__/renderer/hooks/agent/internal/useAgentDataListener.test.tsxsrc/__tests__/renderer/hooks/agent/internal/useAgentExitListener.test.tsxsrc/__tests__/renderer/hooks/agent/internal/useAgentSessionIdListener.test.tsxsrc/__tests__/renderer/hooks/agent/internal/useAgentStderrListener.test.tsxsrc/__tests__/renderer/hooks/agent/internal/useAgentUsageListener.test.tsxsrc/renderer/hooks/agent/internal/types.tssrc/renderer/hooks/agent/internal/useAgentExitListener.tssrc/renderer/hooks/agent/useAgentListeners.ts
|
Thanks for porting this over, @chr1syy — appreciate you catching the post-decompose layout difference on One blocking item before this can land: 🔴 CI
|
Addresses CI failure and review feedback from PR RunMaestro#1052: 1. `createMockBatchedUpdater` in `src/__tests__/renderer/hooks/useAgentListeners.test.ts` was missing `flushNow: vi.fn()`, causing 15 failures in the coordinator suite (TypeError: flushNow is not a function). 2. Move `flushNow()` past a new `-batch-` early-return guard at the top of onExit so it doesn't fire as a no-op for batch sessions (Greptile/pedramamini nit). The existing `-batch-` branch in the detection chain at line ~89 is now dead but left in place defensively — happy to remove if preferred. 3. Add `deps.batchedUpdater` to the `useEffect` dependency array for consistency with every other `deps.*` (CodeRabbit nit). Targeted tests: 94/94 pass across the coordinator + 5 internal listener suites.
|
Pushed
Local verification:
CI should turn green on the next run. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/renderer/hooks/agent/internal/useAgentExitListener.ts (1)
61-61: 💤 Low valueOptional: Consolidate duplicate
-batch-checks for clarity.The
-batch-session guard appears twice: at line 61 (beforeflushNow) and again at lines 91-92 (in the sessionId parsing logic). Per the commit message, the early guard was added to preventflushNowfrom running for batch sessions, and the later branch was left defensively. While intentional, future maintainers might find the duplication confusing.Also applies to: 91-92
🤖 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/renderer/hooks/agent/internal/useAgentExitListener.ts` at line 61, Consolidate the duplicate "-batch-" guards in useAgentExitListener by introducing a small helper (e.g., isBatchSession(sessionId)) and using it both before calling flushNow and in the sessionId parsing/branching logic instead of repeating sessionId.includes('-batch-'); update the early guard (the check that short-circuits flushNow) and remove the duplicate literal check in the later parsing branch so both sites call isBatchSession(sessionId) for clarity and maintainability.
🤖 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/renderer/hooks/agent/internal/useAgentExitListener.ts`:
- Line 61: Consolidate the duplicate "-batch-" guards in useAgentExitListener by
introducing a small helper (e.g., isBatchSession(sessionId)) and using it both
before calling flushNow and in the sessionId parsing/branching logic instead of
repeating sessionId.includes('-batch-'); update the early guard (the check that
short-circuits flushNow) and remove the duplicate literal check in the later
parsing branch so both sites call isBatchSession(sessionId) for clarity and
maintainability.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 75e62d17-fe11-4724-96c4-c4debba1a1a6
📒 Files selected for processing (2)
src/__tests__/renderer/hooks/useAgentListeners.test.tssrc/renderer/hooks/agent/internal/useAgentExitListener.ts
rc companion to #1023. Same race, different file after the post-decompose layout on rc.
Closes the same gap reported in #1022 for users on the RC channel (the reporter is on `0.16.18-RC`).
Root cause (recap)
Stdout chunks from the agent are accumulated in `useBatchedSessionUpdates` and flushed on a 200ms timer. When the agent exits, `onExit` synchronously calls `setSessions` to append a new `user` log entry for the next queued message. If the batched flush hasn't fired yet, trailing chunks of the just-finished response land in `tab.logs` after the new user entry. `TerminalOutput`'s `collapsedLogs` memo groups consecutive non-user/tool/thinking entries into one bubble, so the late chunks of response 1 get coalesced with response 2 — visually merging them.
Why a separate PR
After commit `e1b8eed29 refactor(agent): decompose useAgentListeners (1,977 -> 104 LOC, -95%)` on rc, the `onExit` handler now lives in `src/renderer/hooks/agent/internal/useAgentExitListener.ts` and does not receive `batchedUpdater`. PR #1023 targets `main` (where `useAgentListeners.ts` is still monolithic), so its diff cannot apply cleanly to rc. When rc next syncs from main, the structural conflict means the fix would most likely silently drop unless re-applied — this PR does that.
Fix
Test plan
Related
Summary by CodeRabbit
Bug Fixes
Tests