Skip to content

[console] InboxPopover 通知页签恒为空:sys_inbox_message 有未读行、REST 正常返回、Home 卡片可渲染,弹层打开不发请求且过滤成空(rc.5 回归,rc.3 正常) #4110

Description

@baozhoutao

Part of steedos-labs/os-project-titanwind-ehr#976

现象

PC Console 顶栏铃铛弹层(InboxPopover)的「通知」页签恒为空——「未读」显示「已读完所有通知」、「全部」显示「暂无通知」,而同一时刻、同一会话:

  • Home 首页「待办事项」卡片正常显示该通知(标题 + 时间 + 未读计数 1);
  • 消息中心(收件箱/通知列表)与详情浮层正常显示,正文完整;
  • 平台三表写入正常:sys_notification / sys_inbox_message / sys_notification_receipt(state=delivered);
  • GET /api/v1/notifications 返回该条,read:false、unreadCount:1。

预期(契约:ADR-0030 / #1429「Console bell 重指 sys_inbox_message + receipts」):弹层「通知」页签列出该用户的收件箱通知。
实际:通知发得出、存得下、Home 卡片和消息中心都看得见,唯独铃铛弹层渲染为空。

最小复现(不依赖我们的 app)

  1. @objectstack/* 17.0.0-rc.5 空库起 dev;
  2. 给任一用户经 MessagingService 落一条收件箱通知,emit 形状:{topic, audience:[userId], payload:{title, body, url}, dedupKey, source}(payload.url 为 SPA 内部路由 /apps/.../record/...);确认三表有行、receipt state=delivered;
  3. 以该用户登录 Console,点顶栏铃铛。

「通知」页签为空,与通知条数无关(1 条或多条同样为空)。

已排除的成因(app 侧排查记录)

假设 结论
通知未发出/未落库 排除:三表均有行,/api/v1/notifications 返回该条
organization_id 为 null 被组织域过滤 排除:直接把三表 organization_id 刷成当前组织后重测,弹层仍空
自动化环境挡了 SSE/WebSocket 基本排除:无 requestfailed、无 WS 报错;且 Home 卡片用同一份 sys_inbox_message 查询,渲染正常
半加载/等待不足 排除:页面水合后等 9s 再开弹层、开后再等 6s,三轮复现一致

关键观测(落点推断)

打开弹层时,弹层未发出任何通知相关网络请求——数据在页面加载时已由
GET /api/v1/data/sys_inbox_message?top=5&sort=-created_at&filter=[["user_id","=","(uid)"]]
取回,Home 待办卡片就是用这份数据渲染成功的;弹层拿到同一份数据后过滤成空。
据此判断落点在 packages/app-shell/src/layout/InboxPopover.tsx 一侧的筛选/渲染层
(是否与 #2765 的 (topic, title) 去重、或按 topic/来源字段的分流有关,请平台侧定位)。

已核对今日合入的 #4073:只动徽章拆解的展示层,与本症状无关。

版本窗口(回归)

  • 现象版本:@objectstack/*@17.0.0-rc.5(cli / runtime / objectql / spec / driver-memory 全 rc.5 精确锁版;npm 上 rc.5 即最新,已确认无更新版本可试);
  • 2026-07-31 在 17.0.0-rc.3 下,同一 app、同一 emit 链路的 e2e 曾断言铃铛弹层文案并通过;
  • 即回归窗口 rc.3 → rc.5(rc.4 未测)。运行时 node 22 / darwin。

我们没做什么

app 侧零绕行、零补丁:业务通知继续走正规 MessagingService emit 扇出,不直写表、不自建弹层。受影响面:该 app 30+ 处业务 Hook 的站内通知在 PC 端失去最主要触达入口,用户只能靠 Home 卡片或主动进消息中心发现新消息。app 侧 issue(steedos-labs/os-project-titanwind-ehr#976,私有仓,含三张对照截图)挂起等本单。

Activity

  1. claude commented on Aug 10, 2026

    @claude
    Contributor

    Triage: pm:queue (objectui local backlog — no domain labels by protocol).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 10, 2026
  3. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    CLAIM (objectui whole-repo seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching a dev agent now.

    本单为外部 app 阻塞项(rc.5 回归),验收后走正常 ready+auto-merge。


    Generated by Claude Code

  4. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4199.

    The dev followed the evidence past both the triage pointer and the filing's own version window, and the relocated diagnosis is airtight:

    • The popover is innocent. Its 「全部」 sub-filter applies no predicate at all, so 「全部」=「暂无通知」 proves the component was handed an EMPTY array — no 消息中心:重复通知的分组/去重/折叠(scheduled digest 每分钟一条把收件箱刷屏) #2765 dedup or topic fan-out can produce that. The emptying gate is one level up: AppHeader.tsx's inbox poller bails on !isApp, and the bell renders on HomeLayout (variant="home"), OrganizationsLayout, and AiChatPage — where the effect returns before issuing any request. Home's To-do card reads the same table through useHomeInbox, which has no such gate: one page, one card rendering the notification, one dead bell. Your own trace confirms it, @baozhoutao — the single observed request was top=5 (that IS useHomeInbox(5)); the bell's own $top:20 + receipt reads are absent because they never fired.
    • Not an rc.3→rc.5 regression: git log -S + blame date the gate to 20255e5411 (2026-05-18) — it arrived WITH the bell feature. The Home bell has never polled. The passing 2026-07-31 e2e almost certainly asserted the popover on an APP page, where the bug does not reproduce (that case is the green control in the new tests, before and after). @baozhoutao: if you can show the bell working ON HOME under rc.3, that is a second, platform-side cause this PR does not address — please say so here with the evidence.
    • Fix: drop isApp from that gate and its dep array — the inbox is user-scoped, not app-scoped (the flag exists for presence avatars/connection dot, which are). Red-first per-variant tests with the app variant as the never-broken control; reverse verification (gate re-inserted → 5 red / control green, as predicted); the empty-state case asserts the READ happened, not just the copy, so it cannot pass vacuously. Full app-shell suite + CI converged 20/20, real changeset.

    Residual: the SAME gate still sits on the approvals + presence/activity polls, so those bell tabs stay dead off-app and the badge now differs Home-vs-app — correctly filed as #4197 with its trade-off (duplicate reads) rather than folded in. Strictly-better intermediate state; #4197 triaged separately. Flipping ready + auto-merge.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions