fix(app-shell): the bell polls the inbox on every console surface, not only inside an app (#4110) - #4199
Merged
Conversation
…t only inside an app (#4110) The top-bar bell's `sys_inbox_message` + `sys_notification_receipt` poll was gated on the header's `isApp` variant flag — the flag that exists to hide the app-only presence avatars and connection dot. The bell itself renders in every variant, so on Home, Organizations and the full-page AI screen its Notifications tab held `[]` forever: "Unread" read "You're all caught up" and "All" — which applies no predicate at all — read "No notifications", on the very page whose To-do card (`useHomeInbox`, ungated) was listing the same row from the same object. The popover's own filter and its (topic, title) coalescing (#2765) are innocent: given the row they render it. The inbox is scoped to the signed-in user, not to the app in the URL, so the poll is now scoped by `user?.id` only. Regression test renders the header against a fake adapter holding one canonical MessagingService-emitted row and asserts the bell lists it in every variant; the `variant="app"` case is the control that never broke.
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 10, 2026 20:38
This was referenced Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4110
Root cause
Not the popover.
InboxPopovernever filters a canonical inbox row away — its(topic, title)coalescing (#2765) and its topic/source handling are innocent, and the "All" sub-filter applies no predicate at all, which is why "All" reading "No notifications" was the tell: the component was handed an empty array.The array is empty because the bell's poller never ran.
AppHeader's inbox readwas gated on the header's
isAppvariant flag:isAppisvariant === 'app', true only underConsoleLayout(/apps/:appName/*).HomeLayoutrendersAppHeader variant="home",OrganizationsLayoutrendersvariant="orgs", andAiChatPagerendersvariant="home"— the bell renders in all of them, but on all of them the effect returns before issuing a request. So the tab was empty forever, independent of row count, exactly as reported.Meanwhile Home's To-do card reads the same object through
useHomeInbox, which carries no such gate — hence one page showing the notification in a card and nothing in the bell.This matches the reporter's own network trace. The only inbox request observed was
top=5— that isuseHomeInbox(5), the card. The bell's read is$top: 20plus asys_notification_receiptread; both are absent from the trace because the effect returned early. "The popover issues no request on open" was correct and, it turns out, load-bearing: nothing ever issued the bell's request.Origin:
20255e5411—feat(app-shell): notifications bell in AppHeader (M10.8), 2026-05-18. The gate arrived with the feature (copied from the presence/connection-status block above it, which is genuinely app-only chrome) and has never changed since.Reported as "worked under 17.0.0-rc.3 on 2026-07-31, broken under rc.5". The predicate that empties the tab is ~3 months old and untouched in that window:
git log -Sfinds it entering at20255e5411(2026-05-18) and nowhere else, andgit blameon the line agrees. The Home bell has never polled.The most likely reading of the passing 2026-07-31 e2e is that it asserted the popover on an app page (
/apps/...), where this bug does not reproduce — thevariant="app"control case in the new test passes both before and after this change. Worth confirming on the reporting side, because if the bell was genuinely working on Home under rc.3 then there is a second, platform-side cause that this PR does not address. Nothing found here explains it.Fix
Drop
isAppfrom the inbox poller's gate (and its dependency array). The inbox is scoped to the signed-in user, not to the app in the URL;user?.idremains the scope. Nothing else changes — no tab restructuring, no fetch-on-open (the shared-data architecture is untouched), no change to the badge logic from #4073.Reproduction and reverse verification
New test
packages/app-shell/src/layout/__tests__/AppHeader.inboxVariant.test.tsxrenders the header against a fake adapter holding one canonical MessagingService-materialized row (user_id,topic,title,body_md,action_url,notification_id,created_at) plus itsstate: 'delivered'receipt, and asserts the bell lists it per variant.On
origin/main, before the fix — the defect and its exoneration in one run:After the fix: 6 passed.
Reverse verification restored
!isAppinto the gate; predicted direction was 5 red / 1 green with theappcontrol surviving, and that is what ran. Note the sixth case, "shows the empty state only when the inbox is genuinely empty": it asserts that the read happened as well as that the copy appears, so it goes red with the gate restored. Without the read assertion it would have stayed green for the wrong reason — the empty-state copy is present either way.Local gates
No user-visible copy added or changed, so no i18n keys move.
Out of scope, filed separately
#4197 — the same
isAppgate still sits on the approvals-count poll and the activity fetch, so the bell's other two tabs stay dead off-app, and the badge (unreadTopics + pendingApprovalsCount) is now variant-dependent. Filed rather than fixed here to keep this PR to the reported tab; it also carries a real trade-off (duplicate reads withuseHomeInboxon Home) that deserves its own decision.Changeset
.changeset/inbox-popover-bell-polls-off-app-4110.md—@object-ui/app-shellpatch.Generated by Claude Code