Skip to content

[console] Four remaining inbox/activity entries still hardcode /apps/setup/..., so a business user without setup access has no working "all notifications" or "all activity" link #4074

Description

@claude

Transferred from objectstack-ai/objectstack#7266 under the file-at-destination ruling (objectstack-ai/objectstack#7165, rule 1): the entire fix face is this repo's packages/app-shell. API issue transfer is unavailable to this credential (objectstack-ai/objectstack#7167 precedent), so the card is recreated with provenance and the source is closed as moved. Bare cross-repo issue refs rewritten to full form; content otherwise verbatim.


Found while implementing objectstack-ai/objectstack#7231 (PR #4071). Filed rather than fixed there — objectstack-ai/objectstack#7231's scope is the approvals entry only.

The finding

objectstack-ai/objectstack#7231 fixed one producer that hardcoded the setup app in a navigation target. Four siblings with the same shape were left in place, all reached by the same non-admin users:

  • packages/app-shell/src/console/home/HomePage.tsx:464 — onOpenNotification={(n) = > navigate(n.actionUrl || '/apps/setup/sys_inbox_message?view=mine')} (the fallback branch, taken whenever a notification carries no action_url)
  • packages/app-shell/src/console/home/HomePage.tsx:472 — HomeActivity onViewAll → /apps/setup/sys_activity
  • packages/app-shell/src/layout/InboxPopover.tsx:129 — goToAllNotifications → /apps/setup/sys_inbox_message?view=mine
  • packages/app-shell/src/layout/InboxPopover.tsx:137 — goToAllActivity → /apps/setup/sys_activity

The last two are the notable pair: the same component's goToApprovals (line 119) and handleNotificationClick (line 142) already resolve currentAppName ?? params.appName and prefix with it. The two "see all" drills sitting between them do not — they were written as deliberate exceptions, and the comment above goToAllNotifications says why: "Route through the setup app's sys_inbox_message list view — the canonical full-page inbox (ADR-0030 L5), outside per-app sidebars."

Why it is still worth a look

That comment argues the OBJECT is app-independent, which is true, and then picks setup as the app to render it in, which does not follow — sys_inbox_message and sys_activity are framework-owned objects reachable at /apps/{any app}/{object} exactly like system/approvals was. A business user with no access to the setup app therefore has a dead "see all" on both the bell popover and Home, while the approvals row directly above it now works.

I did not verify the failure end-to-end in a browser against a non-admin session, so the user-visible symptom (shell "App not available" guard vs. something softer) is inferred from the routing, not measured.

Deliberately excluded from this report

/apps/setup/system/marketplace (HomePage.tsx:395, HomeAppsStrip onBrowseMarketplace) and /apps/setup/system/apps are admin-scoped surfaces where setup is a defensible target rather than an oversight. They are listed here only so a fix does not sweep them in without a decision.

Suggested shape

If it is taken up: the same resolution objectstack-ai/objectstack#7231 introduced in HomePage (last-open app re-checked against the live active-app list, then first active app, setup last) applied to all four, or — better, if the four are agreed to be one surface — a single shared helper the popover and Home both call, which the objectstack-ai/objectstack#7231 PR deliberately did not create because it was out of scope there.


Generated by Claude Code

Activity

  1. claude commented on Aug 10, 2026

    @claude
    ContributorAuthor

    Triage: pm:queue (whole-repo backlog — no domain labels in this repo).

    • Landing verified @ objectui origin/main bd863fe, all four anchors live by direct read: packages/app-shell/src/console/home/HomePage.tsx:464 (fallback branch) and the HomeActivity onViewAll drill; packages/app-shell/src/layout/InboxPopover.tsx goToAllNotifications / goToAllActivity both navigate to the hardcoded setup-app paths, while goToApprovals and handleNotificationClick directly beside them already resolve currentAppName ?? params.appName. The contrast pair makes this a same-shape completion of the landed [approvals][console] Register an approvals:inbox component-registry key and de-hardcode the Approvals Inbox entry links objectstack#7231 fix, not a new design question — the in-code ADR-0030 comment argues the OBJECT is app-independent, which does not entail rendering it in setup.
    • Queue, not finding: a business user without setup access has a dead "see all" on the bell popover and Home today (symptom inferred from routing, as the card honestly notes — the fix card should verify the guard behavior first).
    • Scope guard carried over: the two admin-scoped marketplace/apps links are deliberately excluded; do not sweep them in without a decision.
    • Dedup: no open objectui shadow (searches for the inbox object and component came back empty; counterprobed the searcher against a known-present term).

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


    Generated by Claude Code

  2. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    Contributor

    Confirmed in a browser against a real non-admin session, closing the one gap this card names about itself ("I did not verify the failure end-to-end in a browser against a non-admin session … inferred from the routing, not measured"). Done as part of objectstack-ai/objectstack#7331, which browser-verifies the objectstack-ai/objectstack#7213 entry convergence.

    Setup

    examples/app-showcase booted from framework 88154bee1, bundled console built from this repo at the pinned 8aad9fd50b16 (packages/console/dist/.objectui-sha matches .objectui-sha). Persona: a user created at runtime holding only business positions (contributor + finance, plus the implicit org_member / everyone). GET /api/v1/meta/app for that session returns showcase_app and account and not setup — so this is the exact audience the card describes.

    Measured

    InboxPopover goToAllNotifications (anchor 3). Opening the bell popover and clicking View all notifications:

    -> http://localhost:3811/_console/apps/setup/sys_inbox_message?view=mine
    

    HomePage HomeActivity onViewAll (anchor 2). On /_console/home, clicking View all activity:

    -> http://localhost:3811/_console/apps/setup/sys_activity
    

    Both anchors reproduce exactly as written. The two remaining anchors were not separately driven: goToAllActivity is the same string as anchor 2 reached from the popover's Activity tab, and onOpenNotification's fallback needs a notification with no action_url.

    The user-visible symptom, since it was the open question

    Not an "App not available" shell guard. The router renders the target inside the current app's shell — the breadcrumb and sidebar stayed on Showcase while the URL said /apps/setup/... — and the page then shows the object's "You don't have access" empty state, because the data read is refused:

    GET /api/v1/data/sys_inbox_message?top=20&sort=-created_at&filter=[…user_id…] -> 403 PERMISSION_DENIED
    GET /api/v1/data/sys_activity?top=200&sort=timestamp                          -> 403 PERMISSION_DENIED
    

    So it is a softer failure than a hard app guard, and correspondingly more confusing: the shell looks fine and only the content is refused.

    One thing a fix here will not fully close

    The 403 above is a second, independent cause, filed separately as objectstack-ai/objectstack#7344: sys_inbox_message is granted by no shipped permission set — member_default, the additive everyone baseline, does not name it. So re-pointing these four anchors at an app the user can open leaves them on the same "You don't have access" state until that grant question is settled. Verified directly: the Account app's own Notifications entry (sys_inbox_message, no setup involved anywhere) renders the identical empty state for this persona.

    Worth knowing before someone measures a fix for this card and reads the unchanged empty state as "the fix did not work". The routing fix is still correct and still needed; it is just not sufficient on its own.

    Contrast — the sibling that already works

    The approvals row directly above these, which objectstack-ai/objectstack#7234 fixed, resolves correctly for the same session in the same run: Home's pending approvals card navigates to /_console/apps/com.example.showcase/system/approvals — a package the user can open — and renders the inbox. That is the shape this card asks for.


    Generated by Claude Code

  3. baozhoutao commented on Aug 10, 2026

    @baozhoutao
    Contributor

    Browser-verified — the inferred symptom is real, and it is a hard app switch

    This card states: "I did not verify the failure end-to-end in a browser against a non-admin session, so the user-visible symptom … is inferred from the routing, not measured." Closing half of that gap with a measurement.

    Environment — objectstack main (88154bee1) with the vendored console rebuilt at the current pin .objectui-sha = 8aad9fd50b16, examples/app-showcase on a fresh seeded DB (objectstack dev --ui --seed-admin), zh-CN, signed in as the dev admin.

    Measured, from inside a business app (Showcase, /_console/apps/com.example.showcase/...):

    Producer Click Resulting location.pathname
    InboxPopover goToAllNotifications bell → 查看全部通知 /_console/apps/**setup**/sys_inbox_message
    HomePage HomeActivity onViewAll Home → 查看全部动态 /_console/apps/**setup**/sys_activity

    Both anchors fire exactly as described, and both leave the app the user was in: the console re-renders with the Setup sidebar and the header app switcher reading 系统设置. So the defect is not only "a dead link for a user without setup access" — for every user it is an unannounced app switch out of the business app, with no way back except the app switcher.

    Same session, same popover, the approvals row directly above resolves to the current app (/_console/apps/com.example.showcase/system/approvals) — the asymmetry this card describes is visible in one screenshot.

    Still not measured: the non-admin side. I had no second account with a usable password on the seeded instance and did not create one, so what a user without setup access sees at the destination (shell "App not available" guard vs. something softer) remains inferred, as originally stated. The two anchors above are now fact rather than inference; the failure mode at the far end is not.

    The other two anchors on the card (HomePage.tsx:464 notification fallback, InboxPopover.tsx:137 goToAllActivity) were not exercised — the seeded notification carried an action_url, so the fallback branch never ran.

  4. self-assigned this
    on Aug 11, 2026
  5. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

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

    • Branch claude/issue-4074-inbox-links-app-resolve, worktree objectui-issue-4074 (S/M container)
    • Direction per the card's suggested shape + the two browser verifications above: ONE shared helper (the objectstack#7231 resolution — last-open app re-checked against the live active-app list → first active app → setup last) applied to all four anchors, replacing the deliberate-exception comments whose ADR-0030 argument (object is app-independent) does not entail rendering in setup. The two admin-scoped marketplace/apps links stay excluded, per the card's scope guard.
    • Known second cause, do not misread: objectstack#7344 (no shipped permission set grants sys_inbox_message) means the destination may STILL show the no-access empty state for business users after this fix — the routing fix is necessary, not sufficient; the pins assert the resolved TARGET, not the far end's data grant.

    Generated by Claude Code

  6. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4242, CI fully green.

    The helper is the ruled anti-drift shape done properly: resolveHostAppSegment LIFTS the objectstack#7231 HomePage resolution (with filterActiveApps absorbing the previously-inline predicate) rather than competing with it, and both surfaces consume it through the same metadata context, so Home and the popover cannot drift. The resolution order contains one measured improvement the dispatch did not ask for and the review endorses: the preferred app passes UNCHECKED only when the catalog names no apps at all — an empty list is "not loaded yet" as often as "no apps", and without that step a user standing inside /apps/crm/... would be demoted to setup during the pre-catalog render, the same defect in a loading race. All four anchors resolve; the deliberate-exception comments are replaced with the corrected reasoning (app-independence is exactly why the object renders under whatever app the user can open); the admin-scoped links are pinned as STAYING setup; the setup-as-last-open tail still resolves setup. Pins assert the navigate() argument only, with the objectstack#7344 second-cause caveat restated in both PR and changeset so nobody misreads the far end's empty state. The TS2871-driven imperative deviation is flagged, typed clean, and each fallback step pinned individually. Reverse verification: exactly the 13 target cases red, every control and helper unit green. 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

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions