Found while implementing #4225 / #4316 (PR #4327). Not fixed there: that PR's
scope is read-state and the duplicate read, and this is a counting question the
two cards never agreed on. Pre-existing, and unchanged by that PR.
What
Two panels on /home badge "what needs me", and they count different things:
| surface |
badge |
window |
bell — layout/InboxPopover.tsx |
distinct unread topics + pending approvals |
the feed's full 20 rows |
Home action centre — console/home/HomeRail.tsx |
pendingApprovalsCount + notifications.length |
the list it renders, capped at limit (5) |
HomeActionCenter computes const total = pendingApprovalsCount + notifications.length,
and notifications is what the card renders — which useHomeInbox caps at
limit, default 5. So the badge is "how many rows are on show", not "how much is
waiting".
With nine unread messages a user sees 9 on the bell and 5 two hundred
pixels below, for the same question about the same rows.
Measured
PR #4327 pins the current numbers, so the disagreement is in-tree and explicit —
packages/app-shell/src/hooks/__tests__/sharedInboxFeed.twoSurfaces.test.tsx,
case "an unread message with no receipt at all still counts": nine unread rows,
homeBadge() is '5' while inbox-bell-badge reads '9'. That case asserts
both numbers deliberately; it is describing today's behaviour, not endorsing it.
Observation-class, and why it is filed anyway
The card is self-consistent — it lists five things and badges five — and nothing
is stale or wrong on either surface. No user is blocked. So this is a finding,
not a defect, and it is left unlabelled for pm:queue deliberately.
What makes it worth recording is that the ceiling moved. Before #4225 Home's
read was $top: 5, so the card genuinely could not know there were nine — the
badge could not have said anything else. Both surfaces now derive from one feed
holding the bell's 20 already-joined rows, so the true unread count is in
hand at Home's call site; the cap is now a presentation slice over data the card
already has. Making the badge count the unread set while the list stays capped is
a two-line change today and was impossible last week.
It also sits directly against the theme both #4225 and #4316 were closed on —
two surfaces reading one feed should not present two numbers for one question —
so leaving it unrecorded invites someone to rediscover it as a bug report.
Options, if it is ever picked up
- Badge the unread count, keep the list capped (recommended shape): badge 9,
list the newest 5. Matches the bell's own semantics, and matches how a capped
list normally behaves elsewhere. Costs a new prop on HomeActionCenter, since
the count would no longer be derivable from notifications.length.
- Leave it. Defensible: "5 things shown, badge 5" is honest about the card,
and the bell is the surface that owns the total.
Not obviously worth doing on its own — most likely a rider on the next change to
this card.
Pointers
packages/app-shell/src/console/home/HomeRail.tsx — HomeActionCenter, total
packages/app-shell/src/hooks/useHomeInbox.ts — the .slice(0, limit) cap
packages/app-shell/src/layout/InboxPopover.tsx — unreadTopics, the bell's fold
packages/app-shell/src/hooks/sharedUserFeeds.ts — the shared 20-row window
Generated by Claude Code
Found while implementing #4225 / #4316 (PR #4327). Not fixed there: that PR's
scope is read-state and the duplicate read, and this is a counting question the
two cards never agreed on. Pre-existing, and unchanged by that PR.
What
Two panels on
/homebadge "what needs me", and they count different things:layout/InboxPopover.tsxconsole/home/HomeRail.tsxpendingApprovalsCount + notifications.lengthlimit(5)HomeActionCentercomputesconst total = pendingApprovalsCount + notifications.length,and
notificationsis what the card renders — whichuseHomeInboxcaps atlimit, default 5. So the badge is "how many rows are on show", not "how much iswaiting".
With nine unread messages a user sees 9 on the bell and 5 two hundred
pixels below, for the same question about the same rows.
Measured
PR #4327 pins the current numbers, so the disagreement is in-tree and explicit —
packages/app-shell/src/hooks/__tests__/sharedInboxFeed.twoSurfaces.test.tsx,case "an unread message with no receipt at all still counts": nine unread rows,
homeBadge()is'5'whileinbox-bell-badgereads'9'. That case assertsboth numbers deliberately; it is describing today's behaviour, not endorsing it.
Observation-class, and why it is filed anyway
The card is self-consistent — it lists five things and badges five — and nothing
is stale or wrong on either surface. No user is blocked. So this is a
finding,not a defect, and it is left unlabelled for
pm:queuedeliberately.What makes it worth recording is that the ceiling moved. Before #4225 Home's
read was
$top: 5, so the card genuinely could not know there were nine — thebadge could not have said anything else. Both surfaces now derive from one feed
holding the bell's 20 already-joined rows, so the true unread count is in
hand at Home's call site; the cap is now a presentation slice over data the card
already has. Making the badge count the unread set while the list stays capped is
a two-line change today and was impossible last week.
It also sits directly against the theme both #4225 and #4316 were closed on —
two surfaces reading one feed should not present two numbers for one question —
so leaving it unrecorded invites someone to rediscover it as a bug report.
Options, if it is ever picked up
list the newest 5. Matches the bell's own semantics, and matches how a capped
list normally behaves elsewhere. Costs a new prop on
HomeActionCenter, sincethe count would no longer be derivable from
notifications.length.and the bell is the surface that owns the total.
Not obviously worth doing on its own — most likely a rider on the next change to
this card.
Pointers
packages/app-shell/src/console/home/HomeRail.tsx—HomeActionCenter,totalpackages/app-shell/src/hooks/useHomeInbox.ts— the.slice(0, limit)cappackages/app-shell/src/layout/InboxPopover.tsx—unreadTopics, the bell's foldpackages/app-shell/src/hooks/sharedUserFeeds.ts— the shared 20-row windowGenerated by Claude Code