Skip to content

Harden app global inbox lifecycle#334

Merged
TraderSamwise merged 3 commits into
masterfrom
chore/core-sidecar-next-59
Jul 6, 2026
Merged

Harden app global inbox lifecycle#334
TraderSamwise merged 3 commits into
masterfrom
chore/core-sidecar-next-59

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a shared global inbox resource store for notifications and threads
  • preserve last good aggregate rows across refresh churn and total refresh failures
  • guard stale global refresh completions with request-owned pending keys
  • migrate global notifications and global threads off screen-local fetch/loading/error state
  • update the core sidecar north-star progress notes

Verification

  • yarn --cwd app test stores/globalInbox.test.ts
  • yarn --cwd app typecheck
  • yarn --cwd app test
  • yarn --cwd app lint (known unrelated warnings only)
  • yarn typecheck
  • yarn lint
  • yarn build
  • yarn vitest
  • pre-push gate

Summary by CodeRabbit

  • New Features

    • Global notifications and threads now refresh through a shared inbox state, improving consistency across the app.
    • Loading, error, and “stale” statuses are now preserved more reliably during refreshes.
  • Bug Fixes

    • Prevents older network responses from overwriting newer notification or thread data.
    • Keeps the last successful results visible when a refresh fails.
  • Documentation

    • Updated project guidance to reflect the improved refresh and status behavior.

@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jul 6, 2026 8:45am

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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

Run ID: 34735446-922e-45d7-8d53-d204bc6d2e1b

📥 Commits

Reviewing files that changed from the base of the PR and between 9931617 and 2222667.

📒 Files selected for processing (4)
  • app/app/(main)/global-notifications.tsx
  • app/app/(main)/global-threads.tsx
  • app/stores/globalInbox.test.ts
  • app/stores/globalInbox.ts
📝 Walkthrough

Walkthrough

This PR adds a shared globalInbox Jotai store providing generic resource state modeling (pending/success/failure/settle with staleness guarding via request keys) for notifications and threads. The global-notifications.tsx and global-threads.tsx screens are migrated from local useState to this shared store, and docs/tests are updated accordingly.

Changes

Global inbox shared resource store

Layer / File(s) Summary
Store contracts and generic transitions
app/stores/globalInbox.ts
Defines GlobalNotificationRow, GlobalThreadRow, resource/value types, and generic begin/success/failure/settle transition helpers keyed by pendingRequestKey.
Resource atoms and request key helper
app/stores/globalInbox.ts
Exposes notification- and thread-specific action atoms and globalInboxRequestKey(kind, sourceKey, sequence) for building deterministic request keys.
Notifications screen migration
app/app/(main)/global-notifications.tsx
Replaces local useState/NotificationRecord typing with globalNotificationResourceAtom and begin/apply/settle actions, guarding staleness via request id and online-project-source key.
Threads screen migration
app/app/(main)/global-threads.tsx
Replaces local useState/ThreadSummaryResponse typing with globalThreadResourceAtom and begin/apply/settle actions, similarly guarding staleness.
Store tests and docs
app/stores/globalInbox.test.ts, docs/core-sidecar-north-star.md
Adds tests for stale retention, scoped settling, recovery, and failure handling; updates docs to describe last-good-snapshot behavior and revised migration scope.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Screen
  participant BeginRefreshAtom
  participant API
  participant ApplySuccessAtom
  participant ApplyFailureAtom
  participant SettleRefreshAtom

  Screen->>BeginRefreshAtom: beginRefresh({ requestKey })
  Screen->>API: fetch rows
  alt success and not stale
    API-->>Screen: rows
    Screen->>ApplySuccessAtom: applySuccess({ rows, fetchedAt })
  else failure and not stale
    API-->>Screen: error
    Screen->>ApplyFailureAtom: applyFailure({ message })
  else stale request
    Screen->>SettleRefreshAtom: settleRefresh({ requestKey })
  end
Loading

Possibly related PRs

  • TraderSamwise/aimux#59: Introduced the global-notifications.tsx and global-threads.tsx screens with local fetch state that this PR migrates to the shared globalInbox store.
🚥 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 captures the main change: hardening the app's global inbox lifecycle.
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
  • Commit unit tests in branch chore/core-sidecar-next-59

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
app/stores/globalInbox.ts (1)

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

Notification and thread atom wiring is fully duplicated.

The four action atoms (begin*, applySuccess*, applyFailure*, settle*) are mirrored line-for-line for notifications and threads, differing only in which resource atom and payload type they close over. A small generic factory would remove ~50 lines of duplication and reduce the surface for future divergence (e.g., the ownership-check fix above would otherwise need to be applied in four places instead of one).

♻️ Proposed factory to eliminate duplication
+function createGlobalInboxActions<T>(resourceAtom: typeof globalNotificationResourceAtom) {
+  return {
+    begin: atom(null, (get, set, { requestKey }: BeginGlobalInboxRefreshInput) => {
+      set(resourceAtom, beginGlobalInboxRefresh(get(resourceAtom), requestKey));
+    }),
+    applySuccess: atom(null, (_get, set, { value, updatedAt }: ApplyGlobalInboxSuccessInput<T>) => {
+      set(resourceAtom, applyGlobalInboxSuccess(value, updatedAt));
+    }),
+    applyFailure: atom(null, (get, set, { error }: ApplyGlobalInboxFailureInput) => {
+      set(resourceAtom, applyGlobalInboxFailure(get(resourceAtom), error));
+    }),
+    settle: atom(null, (get, set, { requestKey }: SettleGlobalInboxRefreshInput) => {
+      set(resourceAtom, settleGlobalInboxRefresh(get(resourceAtom), requestKey));
+    }),
+  };
+}
🤖 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 `@app/stores/globalInbox.ts` around lines 118 - 187, The notification and
thread action atoms are duplicated across beginGlobalNotificationRefreshAtom,
applyGlobalNotificationSuccessAtom, applyGlobalNotificationFailureAtom,
settleGlobalNotificationRefreshAtom, and their globalThread* counterparts.
Refactor this wiring into a small generic factory/helper that takes the resource
atom and row type so the shared begin/applySuccess/applyFailure/settle logic
lives in one place, reducing repeated code and keeping future behavior changes
consistent.
app/app/(main)/global-notifications.tsx (1)

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

Refresh lifecycle duplicated with global-threads.tsx.

This refresh callback, the ref-sync effect, and the staleness-guard pattern are nearly identical to app/app/(main)/global-threads.tsx (Lines 75-126 there), differing only in the fetch call, row-shaping, sort function, and error text. Consider extracting a shared hook (e.g., useGlobalInboxRefresh(kind, fetchRows, sortRows, atoms)) parameterized by these differences to avoid maintaining two copies of the same lifecycle logic (including the request-key generation this logic relies on for staleness detection).

🤖 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 `@app/app/`(main)/global-notifications.tsx around lines 94 - 147, The refresh
lifecycle in global-notifications duplicates the same request-key, ref-sync, and
staleness-guard flow used in global-threads, so it should be consolidated.
Extract the shared logic from the refresh callback and related sync/guard
behavior into a reusable hook or helper (for example, a
useGlobalInboxRefresh-style abstraction) and parameterize only the differing
parts such as the fetch call, row mapping, sort function, and error message.
Keep the requestId/requestKey handling and stale-response checks centralized so
both global-notifications and global-threads use the same implementation.
🤖 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 `@app/app/`(main)/global-notifications.tsx:
- Around line 94-147: The refresh lifecycle in global-notifications duplicates
the same request-key, ref-sync, and staleness-guard flow used in global-threads,
so it should be consolidated. Extract the shared logic from the refresh callback
and related sync/guard behavior into a reusable hook or helper (for example, a
useGlobalInboxRefresh-style abstraction) and parameterize only the differing
parts such as the fetch call, row mapping, sort function, and error message.
Keep the requestId/requestKey handling and stale-response checks centralized so
both global-notifications and global-threads use the same implementation.

In `@app/stores/globalInbox.ts`:
- Around line 118-187: The notification and thread action atoms are duplicated
across beginGlobalNotificationRefreshAtom, applyGlobalNotificationSuccessAtom,
applyGlobalNotificationFailureAtom, settleGlobalNotificationRefreshAtom, and
their globalThread* counterparts. Refactor this wiring into a small generic
factory/helper that takes the resource atom and row type so the shared
begin/applySuccess/applyFailure/settle logic lives in one place, reducing
repeated code and keeping future behavior changes consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 20131900-5a46-4741-a518-14866651332d

📥 Commits

Reviewing files that changed from the base of the PR and between 11352e7 and 9931617.

📒 Files selected for processing (5)
  • app/app/(main)/global-notifications.tsx
  • app/app/(main)/global-threads.tsx
  • app/stores/globalInbox.test.ts
  • app/stores/globalInbox.ts
  • docs/core-sidecar-north-star.md

@TraderSamwise
TraderSamwise merged commit 4303871 into master Jul 6, 2026
3 checks passed
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