Skip to content

console: System Hub 计数失败后「无徽章」与「这张卡本来就没有计数」「还在加载」三态同形 —— 403 的管理员得到的是沉默,不是「你没有权限」 #3685

Closed as not planned

Description

@yinlianghui

越界发现,记录于 #3679(让计数的错误态回落 null、不再冒充 0)期间。按纪律只报不改,单独立单,未认领,留给 PM 分诊。

基线:origin/main @ 074ec53d6 + #3679 的分支 claude/issue-3679-hub-counts-error-state。

事实

#3679 把 SystemHubPage 逐个计数的失败态从「渲染一个确定的 0」改成「回落 null,不显示徽章」,复活的是页面现存的降级形态(徽章写作 {card.count !== null && …})。这一步严格是改善:一个错的数字换成了没有数字。

但「没有徽章」这个像素在这张卡墙上不是独占的,它同时表示另外两件事:

屏幕上 实际含义 谁是这样
无徽章 这张卡从来就没有计数 Applications / Metadata / Datasources / AI Approvals / App Marketplace / Settings / Developer / Profile —— 八张卡的 count 是硬编码 null
无徽章 计数还在路上 五张计数卡在 fetchCounts 落地前(初始 state 全 null)
无徽章 计数失败了(500 / 401 / 403 / 断网) #3679 之后的错误态

于是 issue #3679 点名的那个最易复现场景 —— 一个能进 System Hub、但对 sys_audit_log 没有读权限的管理员 —— 修完看到的是:Audit Log 卡片和 Settings 卡片长得一模一样。他不再被一个「0 entries」误导成「审计日志是空的」,但也没有被告知发生了什么:没有错误文案,没有重试入口,没有「你没有权限」。从「说错话」变成了「不说话」。

加载中那条有一点缓解 —— 页面顶部有一个 role="status" 的 Loading statistics...,落地后消失;所以「加载中」和「失败」在时间上可分,但在卡片本身上不可分,而管理员看的是卡片。

为什么单独立单

分诊时可参考的几种形态(不预判,不预设严重度)

  1. 失败的卡片显示一个可辨的徽章而非无徽章(如一个 variant="outline" 的 —,配 title / aria-label 说明失败原因)。改动面最小,仍在既有 Badge 形态内。
  2. 卡片级的重试入口(点一下只重取这一个对象)。
  3. 页面级的一句「部分统计未能加载」摘要 —— 但这会把逐卡隔离的信息又摊平回全局,与 console: System Hub 计数的 .catch(() => ({ data: [] })) 把 500 / 401 / 403 / 断网都渲染成一个确定的 0(404 根本到不了它) #3679 特意保留的单卡语义相左,列在这里是为了记录它被考虑过。
  4. 不做 —— 「不说话」已优于「说错话」,而这张卡墙本身在 @deprecated 通道上。

严重度我判断不准,按纪律照实立单不自行压低:后果不是错误信息,而是信息缺失,且缺失发生在权限异常这种最需要解释的时刻。

已就关键词(SystemHubPage、hub-card、计数 / 徽章 / 错误提示 / 重试 / affordance、count !== null)搜过本仓开放 issue 与 PR,无同源单(#3655 是路由与对象名裁决,#3670 是对象名,#3672 是孤儿页面,#3679 是本单的前置且已交付其前半句)。


Generated by Claude Code

Activity

  1. claude commented on Aug 7, 2026

    @claude
    Contributor

    Findings triage — verdict: HOLD (finding retained).

    Why still held, and explicitly not because the severity is low: the card is right that the outcome is missing information at the moment it is most needed — an admin without sys_audit_log read access now sees an Audit Log card pixel-identical to Settings. Nothing here is being graded down. What blocks promotion is that the fix is a fork whose answer is already owned by an open decision: #3655's option C retires this bespoke card wall entirely (SystemHubPage's docblock already carries @deprecated), and the body correctly notes this card is void if C is chosen and stands if A or B is. Promoting now would put a second decision in front of the maintainer that is downstream of one they already hold, and would risk spending an implementation cycle on a page slated for removal.

    Restart condition — the #3655 ruling, and it resolves this card in both directions:

    Verified independently of the body: #3679's contribution stands as described — the badge renders under {card.count !== null && …}, so falling back to null restores an existing degraded form rather than inventing UI, which is why補 error copy was correctly out of that dispatch's authorised surface. The tri-state collapse (eight hardcoded-null cards / five counted cards pre-fetch / post-#3679 failures) is a property of that same conditional, not a regression #3679 introduced. The role="status" Loading statistics... at the page top does separate loading in time but not on the card, exactly as stated.

    No target:<major>: the page is on the @deprecated path with its retirement under active decision; blocking an RC on a card wall that may not ship is not defensible. Revisit if #3655 lands on A/B.

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


    Generated by Claude Code

  2. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    Closed, not planned — absorbed by the #3743 ruling (2026-08-09).

    The maintainer ruled option C on #3743: the bespoke SystemHubPage card wall retires in favour of a navigation/registry-driven entry (「全部接受」, decision-inbox round of 2026-08-09). This card's own boundary note anticipated exactly this: 「若维护者最终选 C,本单随之作废」. The three-states-one-pixel ambiguity dies with the wall; no error-copy work lands on a surface being deleted.

    If the retirement implementation somehow preserves per-card counts on the new surface, the 403-silence concern re-files against that surface with fresh evidence — this card stays closed either way.


    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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions