Skip to content

fix(frontend): stop four newly-live toast storms from starving the notification surface - #972

Merged
ryaneggz merged 3 commits into
developmentfrom
fix/966-toast-audit
Aug 8, 2026
Merged

fix(frontend): stop four newly-live toast storms from starving the notification surface#972
ryaneggz merged 3 commits into
developmentfrom
fix/966-toast-audit

Conversation

@ryaneggz

@ryaneggz ryaneggz commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Closes #966

PR #965 mounted <Toaster visibleToasts={3} /> for the first time, making ~101 previously-dormant toast.* calls across 25 files go live at once. #965 fixed 8 of them; everything #966 reported has been live in development since. This fixes all five reported items plus one of the same class the issue did not name.

Status per item

# Item Status
1 useChat.ts:703 wedges the notification surface Fully fixed — and the underlying event was also unreachable; see below
2 useMessageQueue.ts ~30 toasts per outage Fully fixed
3 ChatContext.tsx:777 re-toasts on a loop Fully fixed, including the correctness bug underneath
4 useChat.ts:319 per-event fire Fully fixed
5 Eight double-toast pairs in the schedules stack Fully fixed (12 statements — the "eight pairs" count was low)
ESLint rule / toast wrapper (the issue's trailing suggestion) Deferred to #971 with the analysis recorded

Item 1 — the priority, and what it turned up

toast.error("MCP sandbox unreachable", { duration: Infinity }) fired per SSE payload with no id and no guard. Three of those permanently occupied every visible slot and starved every later toast app-wide, meaning a real outage could silence the notification surface during the exact debugging session you need it in.

It is no longer a toast. An unreachable sandbox is a terminal failure of a run the transcript is visibly waiting on — the user pressed Send, the spinner vanished, and no assistant message ever appeared — which is precisely what RunErrorBanner exists for; the streamMode === "error" branch fifteen lines above already routes there. A setRunError state write is also idempotent by construction, so N events collapse to one banner with no id bookkeeping for a future change to get wrong.

Found while implementing it: the branch was near-unreachable to begin with. The backend emits mcp_sandbox_unreachable (agents/__init__.py:353), but convertEventToLegacy had no case for it and both parseEvent switches in lib/utils/fetchStreamReader.ts fell through to default: return null. On the unified stream path the event was silently dropped and the run simply stopped with no explanation — a worse failure than the one reported, and only the legacy fallback path ever reached the storming toast. The event is now typed in lib/entities/stream.ts and wired through both readers and the converter, so the banner is genuinely reachable.

Three supporting corrections came with it. The DLQ Replay button rendered unconditionally while runError.recoverable was declared and never read (replaying a run that never reached the DLQ dead-ends). metadata is captured by closure at startManagedStream render time while run_id arrives in a later event, so the banner's correlation id was never populated. And the event was missing from both terminal-event sets (streamSource.ts's hasTerminalEvent and clearDistributedRecovery) — it survived only because the distributed emitter happens to append a trailing done that the sync emitter does not; without that accident the reader retried five times and then took the non-recovery onError branch, firing a blocking alert() and wiping the user's message behind the new banner.

Judgment calls

  • Item 1 is a banner, not a deduped toast. The issue floated both. A toast needs a developer to remember the id forever; a state write cannot regress.
  • Sandbox failures are recoverable: false and hide Replay. The message tells the user to send again instead. Offering a Replay that POSTs /llm/dlq/<runId>/replay for a run with no DLQ entry is worse than offering nothing.
  • Item 2 keeps the per-retry warning (under its own stable id) rather than deleting it. Deleting a user-facing signal is a larger behavioral change than the conservative option calls for; the storm is fixed either way.
  • Item 3 does not advance lastSavedPersistentSignatureRef in the catch. That is the tempting one-liner and it is a data-loss bug — the ref is also the dirty indicator and the autosave-skip guard, so advancing it marks unsaved text as saved. Autosave backs off exponentially instead (2s → 30s cap) and re-arms a real retry at the deadline: the only other autosave trigger is a signature change, so a user who types, sees the failure and stops typing would otherwise be stranded with no pending timer and no signal (hasUnsavedPersistentChanges has no UI consumer). Manual save always bypasses the backoff.
  • Item 3 toasts rather than staying silent. The issue offered useScheduleExecutions.ts:40 as the precedent for a background loop that declines to toast. Rejected here because unsaved user text is at stake; it earns exactly one.
  • Post-write schedule refreshes are now silent. Beyond removing the duplicate pairs, a create whose refetch failed emitted 2 success + 1 error simultaneously. Even reduced to 1+1, "created successfully" beside "failed to load" and a list missing the new schedule reads as failure — the user retries and creates a duplicate. The write reports its own outcome once. A proposed inline stale-list banner was rejected as scope.
  • Stable ids applied narrowly. Load failures fire from mount effects and get ids (there are two live useSchedules consumers — the page and the sidebar panel — so one outage stacked a toast each, doubled again under <StrictMode>). Create/update/delete deliberately get none: they answer a click and must fire every time, or two identical saves are indistinguishable.
  • Id constants are colocated beside their call sites, following connectionToast.ts, rather than hoisted into a shared barrel that adds an import edge and enforces nothing.
  • pages/memories/edit.tsx fixed although the issue does not list it — a mount effect that toasted and navigated with no id and no guard, giving two of each under <StrictMode>. Same class as item 1 and cheap. The audit reviewed all ~101 sites but changes only these; nothing else surfaced as actively harmful.

Scope notes for the reviewer

  • Two defects surfaced under item 5 that the issue did not mention. useAgentSchedules.createSchedule/updateSchedule early-returned on a falsy agentId before any toast and before any API call, so the panel was reporting success for a write that never happened; deleting the duplicate toast would have made it silent, so the hook now reports and throws. And the components' try/catch blocks are kept — deleteSchedule rethrows, so removing them trades a duplicate toast for an unhandled rejection.
  • The banner is scoped to the chat surface where the toast was app-wide, so a user who navigates away mid-run no longer sees the sandbox error follow them. The pre-existing error branch already accepts that tradeoff.

Adversarial review round

The diff was audited before this was marked ready, and the audit found a defect I introduced that was worse than the bug it fixed, plus four smaller ones. All are fixed in dc6e6787; the findings are recorded here because a green suite is what let the first one through.

  • The backoff retry could survive unmount and PATCH forever. It is armed from the promise catch, outside the render that scheduled it, so it could not rely on the autosave effect's cleanup — React only runs the cleanup the most recent effect run returned, and that effect returns early with no cleanup on four branches (unauthenticated, skip counter, unchanged signature, streaming). A timer armed after one of those kept PATCHing an unmounted tree with a stale auth token until page reload. Fixed with a mount-scoped teardown and a live token re-read. The new test reproduces the exact escape path rather than a convenient one — the naive version (edit → fail → unmount) passes pre-fix, because there the autosave effect's own cleanup is still the most recent one.
  • A failed manual save did not re-arm, leaving the user in precisely the stranded state the backoff exists to prevent. Now re-arms for both reasons, on one shared counter — it tracks the health of the PATCH endpoint, not which trigger issued it, and manual saves bypass the cooldown anyway.
  • The queue drop counter never reset on success, so it was cumulative rather than per-outage: a degraded backend interleaving drops and successes would eventually report "150 messages dropped" for an outage that never happened.
  • RunError.title was dead code — no call site set it, yet two tests asserted a field the app cannot produce. Removed rather than wired up.
  • Failed to load schedule vs Failed to load schedules differed by one character on the same screen; the edit-load failure now reads Failed to open schedule.

Findings deliberately not acted on: metadataRef.current = metadata is a write during render (idempotent, and matches existing practice in useMessageQueue.ts), and the StrictMode double-invoke motivation cited for two of the stable ids is development-only — those fixes stand on their other merits (two concurrent useSchedules consumers in production).

Verification

npm run test && npx tsc -b && npm run lint && npm run build — 468 passed, 3 skipped; typecheck and build clean; the 3 lint warnings are pre-existing in files this PR does not touch. Note npx tsc --noEmit as written in the issue's suggested command is a no-op here — frontend/tsconfig.json is solution-style with "files": [], so it compiles zero files and exits 0 regardless; tsc -b is the real gate.

Tests pin the anti-storm invariant, not merely that a toast fires — an assertion that toast.error was called passes before and after these fixes. Each new suite was run against the pre-fix code to confirm it actually fails there:

  • 5 consecutive mcp_sandbox_unreachable payloads → zero toast.error calls and exactly one run-error state (pre-fix: 5 toasts, no banner).
  • One stream emitting 4 recoveryMode error events → 1 toast; a new stream erroring afterwards still toasts, proving the latch is per-stream rather than global.
  • A 404 onError fires its informational toast without the recovery id — sonner merges by id, so sharing it would have overwritten the error text with a reassurance.
  • A 3-message drop cascade → every call carries the literal drop id, and the title matches the aggregate count. ("At most N distinct ids" was rejected as an assertion: pre-fix every call passes undefined, which is one distinct value, so it passes pre-fix.)
  • patchDefaults rejecting across 4 successive edits → 1 PATCH and 1 toast; the PATCH count is what pins the real bug. Dirty state still reports dirty, guarding against the forbidden fix.
  • Create/update/delete on each schedules surface → exactly 1 success or 1 error toast; the compound create-succeeds-refetch-fails case → 1 success and 0 error.
  • <StrictMode> mount with a rejecting memory fetch → 1 toast and 1 navigate, with the effect confirmed to have run twice.

Where a test is a regression guard rather than a falsifier it is marked as such — the schedules page's delete handler and the manual-save bypass both already behaved correctly.

CI verifies the same four commands plus the backend suite. Not verified here: the playwright e2e suite, which CI runs only on manual dispatch. It covers the DLQ replay button this PR now gates, so a vitest test pinning that recoverable: true with a truthy runId still renders the button was added specifically to guard that path without playwright.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling for chat runs, including clearer retry guidance and replay controls only when recovery is available.
    • Prevented duplicate or stacked notifications across streaming, messaging, memory loading, and schedules.
    • Added retry backoff for failed automatic saves while keeping manual saves available.
    • Improved schedule error handling, including missing-agent scenarios and refresh failures.
    • Reduced connection-error noise during sandbox and stream failures.
  • Tests

    • Expanded coverage for error states, notification deduplication, retries, stream recovery, and schedule operations.

… surface (#966)

PR #965 mounted <Toaster /> for the first time, making ~101 dormant toast.*
calls across 25 files go live at once. Four of them storm.

- useChat.ts MCP sandbox unreachable: was a per-payload duration:Infinity
  toast with no id, so three of them permanently occupied every slot under
  visibleToasts={3} and starved every later toast app-wide. Now routes to the
  existing RunErrorBanner, which is idempotent by construction. Also wires the
  event through both parseEvent switches and convertEventToLegacy — it was
  silently dropped on the unified stream path, so the run stopped with no
  explanation at all.
- useChat.ts recoveryMode error: closes the stream and notifies once per
  stream behind a latch and a stable id.
- useMessageQueue.ts: ~30 toasts per outage collapse into one accumulating
  summary toast under a fixed id.
- ChatContext.tsx: fixes the retry loop under the toast — the save signature
  stayed dirty on failure, re-firing a failing PATCH every keystroke. Autosave
  now backs off and re-arms; manual save always bypasses.
- schedules stack: "the hook owns the toast" applied across four files,
  removing 12 duplicate toast statements.
- memories/edit.tsx: StrictMode double-fire (not in the issue, same class).

Background/effect-driven toasts get stable ids; user-initiated toasts
deliberately do not.

Refs #966

Signed-off-by: ryaneggz <kre8mymedia@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ryaneggz, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3850bd7d-aa67-4173-a25e-677d1939203f

📥 Commits

Reviewing files that changed from the base of the PR and between dc6e678 and 128da57.

📒 Files selected for processing (2)
  • Changelog.md
  • frontend/src/components/lists/ChatMessages.tsx
📝 Walkthrough

Walkthrough

The PR audits frontend toast behavior, adds MCP sandbox stream-error propagation, restricts run-error replay, adds autosave backoff, aggregates queue-drop notifications, centralizes schedule notifications, and prevents duplicate memory-load toasts.

Changes

Stream and run-error handling

Layer / File(s) Summary
Stream events and run-error handling
frontend/src/lib/entities/stream.ts, frontend/src/lib/utils/fetchStreamReader.ts, frontend/src/lib/utils/streamSource.ts, frontend/src/hooks/useChat.ts, frontend/src/components/lists/ChatMessages.tsx, related tests
The stream pipeline recognizes mcp_sandbox_unreachable as a terminal event. useChat creates persistent non-recoverable run errors for sandbox failures and deduplicates recovery toasts. ChatMessages shows replay only when the error is recoverable and has a run ID.

Autosave and queue notifications

Layer / File(s) Summary
Autosave and message-queue notification control
frontend/src/context/ChatContext.tsx, frontend/src/hooks/useMessageQueue.ts, related tests
Autosaves use exponential retry backoff capped at 30 seconds and cancel retries on unmount. Manual saves bypass backoff. Dropped-message errors aggregate within a quiet window, and retry notifications use a separate stable ID.

Schedule notification ownership

Layer / File(s) Summary
Schedule notification ownership and refresh handling
frontend/src/hooks/useAgentSchedules.ts, frontend/src/hooks/useSchedules.ts, frontend/src/components/panels/AgentSchedulesPanel.tsx, frontend/src/pages/schedules/index.tsx, related tests
Schedule hooks own operation notifications. Refreshes after writes can suppress load-failure toasts. Missing agent IDs now produce errors, and schedule load failures use stable toast IDs.

Memory load handling

Layer / File(s) Summary
Memory load failure deduplication
frontend/src/pages/memories/edit.tsx, frontend/src/pages/memories/edit.test.tsx, Changelog.md
Memory-load failure handling runs once per mount and uses a stable toast ID for non-network errors. The changelog records the stream, toast, autosave, and schedule changes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SSEBackend
  participant FetchStreamReader
  participant StreamSource
  participant useChat
  participant ChatMessages
  SSEBackend->>FetchStreamReader: Send mcp_sandbox_unreachable
  FetchStreamReader->>StreamSource: Emit terminal event
  StreamSource->>useChat: Deliver event
  useChat->>ChatMessages: Set non-recoverable run error
  ChatMessages->>ChatMessages: Hide replay control
Loading

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address all coding objectives in [#966], including toast deduplication, queue aggregation, autosave backoff, stream handling, and schedule notification ownership.
Out of Scope Changes check ✅ Passed The implementation and tests remain focused on toast storms, duplicate notifications, related error handling, and the explicitly stated memory-page StrictMode fix.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: preventing newly introduced frontend toast storms and notification starvation.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/966-toast-audit

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.

An audit of the diff found one defect worse than the bug it fixed, plus
four smaller ones.

- ChatContext: the backoff retry is armed from the promise catch, outside
  the render that scheduled it, so it could not rely on the autosave
  effect's cleanup — that effect returns early with no cleanup on four
  branches. A timer armed after one of those could survive unmount and
  PATCH an unmounted tree forever, holding a stale auth token. Adds a
  mount-scoped teardown plus a live token re-read, and a test that
  reproduces the exact escape path (it fails without the teardown).
- ChatContext: a failed manual save now re-arms too. It previously left
  the user in exactly the stranded state the backoff exists to prevent.
- useMessageQueue: the drop counter never reset on success, so a degraded
  backend interleaving drops and successes accumulated indefinitely and
  eventually reported a mass failure that never happened.
- useChat: RunError.title was dead code — no call site set it — with two
  tests asserting a field the app cannot produce. Removed.
- streamSource/useChat: mcp_sandbox_unreachable was missing from both
  terminal-event sets and worked only because the distributed emitter
  appends a trailing done; the sync emitter does not.
- useSchedules: "Failed to load schedule" was one character from the
  list-load message on the same screen; now "Failed to open schedule".

Refs #966

Signed-off-by: ryaneggz <kre8mymedia@gmail.com>

@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.

Actionable comments posted: 1

🧹 Nitpick comments (4)
frontend/src/pages/memories/edit.test.tsx (1)

23-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test the failed request through the shared API mock.

Configure the failed memory request in src/tests/mocks/. Remove the mockGet call-count assertion. Wait for the toast and redirect instead. This keeps the test focused on observable behavior.

As per coding guidelines, “Mock API calls using mocks in src/tests/mocks/” and “test observable behavior rather than implementation details.”

Also applies to: 60-63

🤖 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 `@frontend/src/pages/memories/edit.test.tsx` around lines 23 - 32, Move the
failed memory request setup from the local mockGet in the edit test to the
shared API mocks under src/tests/mocks/, and remove the mockGet call-count
assertion. Update the test to await the observable error toast and redirect
behavior instead of inspecting service implementation details.

Source: Coding guidelines

Changelog.md (1)

11-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Condense these changelog entries.

Keep each entry to the user-visible fix and its effect. Move implementation paths, event sequencing, measurements, and investigation details to the PR description or linked issues.

As per coding guidelines, include “concise change notes.”

🤖 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 `@Changelog.md` around lines 11 - 14, Condense the four fix/966-toast-audit
entries in Changelog.md to concise, user-visible change notes describing each
fix and its effect. Remove implementation paths, internal symbols, event
sequencing, measurements, investigation history, and rejected alternatives;
retain only outcomes such as preventing toast storms, surfacing sandbox failures
in the run error banner, retrying failed autosaves with backoff, and eliminating
duplicate schedule notifications.

Source: Coding guidelines

frontend/src/lib/utils/fetchStreamReader.test.ts (1)

3-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the @/ alias for imports from src.

These relative imports target modules under frontend/src. Replace them with the configured alias.

  • frontend/src/lib/utils/fetchStreamReader.test.ts#L3-L3: import from @/lib/utils/fetchStreamReader.
  • frontend/src/hooks/useChat.test.tsx#L4-L4: import from @/hooks/useChat.
  • frontend/src/components/lists/ChatMessages.test.tsx#L27-L27: import from @/components/lists/ChatMessages.

As per coding guidelines, use the @/ import alias for imports from src.

🤖 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 `@frontend/src/lib/utils/fetchStreamReader.test.ts` at line 3, Replace the
relative imports in frontend/src/lib/utils/fetchStreamReader.test.ts lines 3-3,
frontend/src/hooks/useChat.test.tsx lines 4-4, and
frontend/src/components/lists/ChatMessages.test.tsx lines 27-27 with the
configured `@/` alias imports, preserving each referenced module and test
behavior.

Source: Coding guidelines

frontend/src/lib/entities/stream.ts (1)

71-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use two-space indentation in the changed frontend code.

The changed code uses tabs. Run Prettier with the repository-required two-space indentation.

  • frontend/src/lib/entities/stream.ts#L71-L80: reformat the changed interface documentation and declaration.
  • frontend/src/lib/utils/fetchStreamReader.ts#L184-L185: reformat the new parser cases.
  • frontend/src/lib/utils/fetchStreamReader.test.ts#L6-L7: reformat the test helper.
  • frontend/src/lib/utils/streamSource.ts#L221-L228: reformat the terminal-event branch.
  • frontend/src/hooks/useChat.ts#L30-L36: reformat the exported toast identifier block.
  • frontend/src/hooks/useChat.test.tsx#L88-L135: reformat the controllable stream mock.
  • frontend/src/components/lists/ChatMessages.tsx#L272-L275: reformat replay eligibility logic.
  • frontend/src/components/lists/ChatMessages.test.tsx#L29-L32: reformat the render helper.
  • frontend/src/context/ChatContext.tsx#L41-L61: reformat autosave constants.
  • frontend/src/tests/context/ChatContext.test.tsx#L10-L17: reformat the hoisted test mocks.
  • frontend/src/hooks/useMessageQueue.ts#L21-L42: reformat notification constants.
  • frontend/src/tests/hooks/useMessageQueue.test.ts#L10-L15: reformat the toast mock.

As per coding guidelines, format frontend TypeScript files with Prettier using two-space indentation.

🤖 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 `@frontend/src/lib/entities/stream.ts` around lines 71 - 80, Reformat the
changed frontend TypeScript code with the repository’s Prettier configuration
and two-space indentation. Apply this to
frontend/src/lib/entities/stream.ts:71-80,
frontend/src/lib/utils/fetchStreamReader.ts:184-185,
frontend/src/lib/utils/fetchStreamReader.test.ts:6-7,
frontend/src/lib/utils/streamSource.ts:221-228,
frontend/src/hooks/useChat.ts:30-36, frontend/src/hooks/useChat.test.tsx:88-135,
frontend/src/components/lists/ChatMessages.tsx:272-275,
frontend/src/components/lists/ChatMessages.test.tsx:29-32,
frontend/src/context/ChatContext.tsx:41-61,
frontend/src/tests/context/ChatContext.test.tsx:10-17,
frontend/src/hooks/useMessageQueue.ts:21-42, and
frontend/src/tests/hooks/useMessageQueue.test.ts:10-15; preserve the existing
logic and only normalize formatting.

Source: Coding guidelines

🤖 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 `@frontend/src/components/panels/AgentSchedulesPanel.test.tsx`:
- Around line 27-35: Replace the inline scheduleService mock in
AgentSchedulesPanel.test.tsx and index.test.tsx with the shared schedule API
mock from src/tests/mocks/, preserving the existing mocked methods and test
behavior in both files.

---

Nitpick comments:
In `@Changelog.md`:
- Around line 11-14: Condense the four fix/966-toast-audit entries in
Changelog.md to concise, user-visible change notes describing each fix and its
effect. Remove implementation paths, internal symbols, event sequencing,
measurements, investigation history, and rejected alternatives; retain only
outcomes such as preventing toast storms, surfacing sandbox failures in the run
error banner, retrying failed autosaves with backoff, and eliminating duplicate
schedule notifications.

In `@frontend/src/lib/entities/stream.ts`:
- Around line 71-80: Reformat the changed frontend TypeScript code with the
repository’s Prettier configuration and two-space indentation. Apply this to
frontend/src/lib/entities/stream.ts:71-80,
frontend/src/lib/utils/fetchStreamReader.ts:184-185,
frontend/src/lib/utils/fetchStreamReader.test.ts:6-7,
frontend/src/lib/utils/streamSource.ts:221-228,
frontend/src/hooks/useChat.ts:30-36, frontend/src/hooks/useChat.test.tsx:88-135,
frontend/src/components/lists/ChatMessages.tsx:272-275,
frontend/src/components/lists/ChatMessages.test.tsx:29-32,
frontend/src/context/ChatContext.tsx:41-61,
frontend/src/tests/context/ChatContext.test.tsx:10-17,
frontend/src/hooks/useMessageQueue.ts:21-42, and
frontend/src/tests/hooks/useMessageQueue.test.ts:10-15; preserve the existing
logic and only normalize formatting.

In `@frontend/src/lib/utils/fetchStreamReader.test.ts`:
- Line 3: Replace the relative imports in
frontend/src/lib/utils/fetchStreamReader.test.ts lines 3-3,
frontend/src/hooks/useChat.test.tsx lines 4-4, and
frontend/src/components/lists/ChatMessages.test.tsx lines 27-27 with the
configured `@/` alias imports, preserving each referenced module and test
behavior.

In `@frontend/src/pages/memories/edit.test.tsx`:
- Around line 23-32: Move the failed memory request setup from the local mockGet
in the edit test to the shared API mocks under src/tests/mocks/, and remove the
mockGet call-count assertion. Update the test to await the observable error
toast and redirect behavior instead of inspecting service implementation
details.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc3c39fb-cac9-42a8-b48c-cd4f7781fe47

📥 Commits

Reviewing files that changed from the base of the PR and between 9481591 and dc6e678.

📒 Files selected for processing (21)
  • Changelog.md
  • frontend/src/components/lists/ChatMessages.test.tsx
  • frontend/src/components/lists/ChatMessages.tsx
  • frontend/src/components/panels/AgentSchedulesPanel.test.tsx
  • frontend/src/components/panels/AgentSchedulesPanel.tsx
  • frontend/src/context/ChatContext.tsx
  • frontend/src/hooks/useAgentSchedules.ts
  • frontend/src/hooks/useChat.test.tsx
  • frontend/src/hooks/useChat.ts
  • frontend/src/hooks/useMessageQueue.ts
  • frontend/src/hooks/useSchedules.ts
  • frontend/src/lib/entities/stream.ts
  • frontend/src/lib/utils/fetchStreamReader.test.ts
  • frontend/src/lib/utils/fetchStreamReader.ts
  • frontend/src/lib/utils/streamSource.ts
  • frontend/src/pages/memories/edit.test.tsx
  • frontend/src/pages/memories/edit.tsx
  • frontend/src/pages/schedules/index.test.tsx
  • frontend/src/pages/schedules/index.tsx
  • frontend/src/tests/context/ChatContext.test.tsx
  • frontend/src/tests/hooks/useMessageQueue.test.ts

Comment thread frontend/src/components/panels/AgentSchedulesPanel.test.tsx
…audit

Signed-off-by: ryaneggz <kre8mymedia@gmail.com>

# Conflicts:
#	Changelog.md
#	frontend/src/components/lists/ChatMessages.tsx
@ryaneggz
ryaneggz merged commit f4db4ab into development Aug 8, 2026
4 checks passed
@ryaneggz
ryaneggz deleted the fix/966-toast-audit branch August 8, 2026 04:14
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.

fix(frontend): audit newly-live toast call sites for duplicate-fire and storms

1 participant