Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

console inbox and home: the Activity feed lists platform events (JWKS keys, data migrations, owner backfills, every login) to business users — the feed reads the app's business objects only #12530

Closed

Description

@objectstack-fleet

Filing gate ① (a product defect, measured on main), filed on the maintainer's word (chat, 2026-10-15, session session_019YFzJp4o8Ck4NsT1toC4UP, seat account os-bill): the seat ran a functional audit of the HotCRM console on objectui main 720f6d4 (report, private to the maintainer: https://claude.ai/artifact/XPrn5eZMsQaKUgasMJEMmR, finding F5, figure 13), and the maintainer answered 「同意开卡,使用 pm-dispatch 创建卡片」. Quota note: the audit's cards exceed the filing gate's three-per-fire quota on that word, which approved the report's card list as a set.

Parent: objectstack-ai/objectstack#25945. Blocks nothing; nothing blocks it.

Reader who acts: the domain:ui lane's next dispatch (seat post #9800), one dev round in @object-ui/app-shell (hooks/sharedUserFeeds.ts, layout/ActivityFeed.tsx, the console home's feed), size S–M.

Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T14:14Z): "activity feed shows system events JWKS key data migration sys objects inbox notifications filter business objects only" → 0. None is this card.

What happens

The bell's Activity feed tab and the console home's Activity feed card show, to the HotCRM admin a minute after a fresh seed: 「Dev Admin Created JWKS Key "QdKJl6LTc2jHmOx3…"」 (JWKS Key), 「Created Data Migration "adr-0104-value-shapes"」 and 「"adr-0104-file-references"」 (Data Migration), 「Owner: ∅ → Dev Admin · Knowledge Article」 twice, then the user's own 「Updated User "Dev Admin"」 on every login (the last_login_at write). A sales user reading this feed sees platform plumbing named after ADRs and key ids; the business events (a lead created, an opportunity's stage changed) are below them or off the page of twenty.

Readings (objectui origin/main 720f6d4, read 2026-10-15T14:15Z)

  1. packages/app-shell/src/hooks/sharedUserFeeds.ts line 10 (the feed table) and line 685 (ACTIVITY_OBJECT = 'sys_activity'): the recent-activity feed is find('sys_activity', top 20, sort -timestamp) with no filter on object_name; lines 622–645 map rows to items by type.
  2. packages/app-shell/src/layout/ActivityFeed.tsx lines 55–70 and 122: the only filter is isKindEnabled(notificationPreferences, a.type) (the user's per-kind switches); nothing filters by object.
  3. packages/app-shell/src/hooks/useHomeInbox.ts feeds the console home's card from the same reading.
  4. API reading (HotCRM backend, admin): GET /api/v1/data/sys_activity?top=20&sort=-timestamp returns rows with object_name sys_user (every login writes one), sys_jwks_key, sys_data_migration, knowledge_article, then crm_lead; the sys_* rows carry organization_id: null.

What to build

  1. The user-facing feeds (the bell's Activity tab, the console home card) read business activity only: rows whose object_name is an object of an installed app the user can open, which excludes the platform's sys_* objects and system-only writes (an object_name the app roster does not list). The exclusion is derived from the object roster the shell already holds, not a hand-written list of names.
  2. The user's own sys_user login write (last_login_at) is never feed material, whatever the roster says.
  3. The platform events stay where an admin looks for them: the Setup / audit surfaces read the same sys_activity unfiltered, as today. The query's top applies after the filter (a filtered read, not a client-side drop that empties the page).

Acceptance

  • sharedUserFeeds tests: with a roster of two app objects, the activity reading's filter names those objects and the mapped items contain no sys_* row; a sys_user last_login_at update row is excluded even when sys_user is in the roster; the Setup audit reading is unchanged.
  • Browser reading by the landing seat on HotCRM after a fresh seed: the bell's Activity feed lists leads, accounts, opportunities and tasks; no JWKS key, migration or login row.
  • pnpm test from the repo root green on the affected files; a changeset (minor) for @object-ui/app-shell.

Out of scope

  • The Notifications and Approvals tabs; the per-kind preference switches; the writer side (objectstack) of sys_activity.

Attribution: written by the domain:ui seat in session session_019YFzJp4o8Ck4NsT1toC4UP (account os-bill) on the maintainer's words quoted above.

Screenshots (added 2026-10-15T14:40Z, on the maintainer's word 「专用分支存图,卡里嵌 raw 链接」)

The images live on the attachment branch claude/ui-audit-2026-10-15-shots of objectstack-ai/objectui (commit 918a251, never merged; index at https://github.com/objectstack-ai/objectui/blob/918a2510a54bd6875b90eb6996d098c3ec0451dd/docs/design/console-audit-2026-10-15/README.md). Full set and figure numbers: the audit report linked above.

The bell's Activity feed on HotCRM: JWKS Key, Data Migration and Knowledge Article rows ahead of any lead

activity-feed.png

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12530-activity-business-only
    Worktree: objectui-issue-12530
    Domain: domain:ui
    Seat: domain:ui#2 (the card names the lane's next dispatch under seat 3's post #9800, which is vacant)
    File surface: packages/app-shell/src/hooks/sharedUserFeeds.ts (the sys_activity reading's query and row mapping), packages/app-shell/src/layout/ActivityFeed.tsx and packages/app-shell/src/hooks/useHomeInbox.ts only if the filter must be passed through them, the module that holds the shell's object roster only to read it, the tests beside each, and one .changeset/12530-*.md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
    Clause-②: no
    Responsibility: objectui's shared activity feed, which reads sys_activity with no object filter | the per-kind preference switches filter by type only; nothing filters by object | every business user who opens the bell's Activity tab or the console home's Activity card (measured on HotCRM after a fresh seed: JWKS keys, data migrations and logins ahead of any lead)
    Thread-read: none
    Serial constraints cleared: at main 521817ce9, no in-flight claim or open PR touches sharedUserFeeds.ts, ActivityFeed.tsx or useHomeInbox.ts. objectui#12496 (this seat, ResourceEditPage.tsx) is disjoint. This seat's objectui#12439 (sharedUserFeeds.ts's hidden-tab poll stop) landed as 27411ea; this card edits that file's activity reading, not the poll cadence.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 12530,
     "status": "done",
     "branch": "claude/issue-12530-activity-business-only",
     "pr": "https://github.com/objectstack-ai/objectui/pull/12542",
     "session": "session_01XVp19E1MYUTFM3dnkwz8gB",
     "premise_still_valid": true,
     "summary": "useSharedActivityFeed (the one reading behind the bell Activity tab, global:notifications and Home's activity card) now reads sys_activity with $filter {object_name: {$in: roster}} beside $top 20. The roster is derived in activityObjectRoster from filterActiveApps(apps) and every type:'object' nav entry (areas + navigation, every depth), each read through navEntriesShownToMember with the sidebar's useNavMemberGates; no name list and no sys_ prefix is written. The roster string is part of the feed key (A1 measured: a new key resets and refetches). No key until the app type settles; an empty roster is ready+empty with no request. isLoginStampRow drops an updated sys_user row whose metadata.new names only last_login_at/last_login_ip (A2 measured in objectstack e4ec6ec1: AuthManager.stampLastLogin + plugin-audit diff); this is the only post-read cut, because no queryable column separates a sign-in from a profile edit. Setup/audit surfaces (object view, RecordDetailView) are untouched. ActivityFeed.tsx and useHomeInbox.ts needed no change.",
     "tests": "HEAD 84b8f5632. app-shell type-check (tsc --noEmit + tsconfig.test.json) after the '@object-ui/app-shell^...' closure build: exit 0. vitest over 14 feed files (sharedUserFeeds*, sharedInboxFeed*, ActivityFeed*, AppHeader.inboxVariant): Test Files 14 passed (14), Tests 133 passed (133), exit 0. Wider set at 95510b3bf (84b8f5632 only destructures the memo deps), covering app-shell src/hooks/, src/layout/, src/console/home/, views/RecordDetailView*, views/record-preview/ and apps/console record-block-record-reach: Test Files 222 passed (222), Tests 1626 passed (1626), exit 0. Before the AppHeader fixture fix the same set read 9 failed in AppHeader.inboxVariant. Ablation via objectstack scripts/ablation-replace.mjs: the $filter line was deleted (anchor x1 -> x0, marker x0 -> x1) and the new pin read 5 failed | 13 passed (18); restore was proven (blob c9fb21067517 == HEAD, git diff HEAD empty). Pre-tsc battery all exit 0: phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes. Also exit 0: check-changeset-fixed, check-changeset-presence, check:new-line-citations (0 new), check:test-path-roots. Narrowed eslint: population is eslint.config.js files **/*.{ts,tsx} minus ignores, all 7 changed files inside; --format json counts 7 files with 0 errors; the warnings are pre-existing AppHeader test lines, and sharedUserFeeds.ts is clean after 84b8f5632; no parserOptions.project, so not type-aware and untouched verdicts are invariant. check-hand-rolled-comment-mask.test.ts not run (no test reads source). NOT MEASURED: HotCRM browser reading, reason: no backend in this run (seat/maintainer per card).",
     "mcp_calls": "0",
     "api_writes": "2 relay writes (fleet-write dispatch): pr_create POST /repos/objectstack-ai/objectui/pulls (+ POST /issues/12542/assignees), and this os-dev-report comment POST /repos/objectstack-ai/objectui/issues/12530/comments. Plus 3 git pushes of the branch. 0 label writes (objectui: none named).",
     "open_questions": [],
     "out_of_scope_findings": [],
     "deviations": [
      "File surface: layout/__tests__/AppHeader.inboxVariant.test.tsx was edited outside the claim list. It pins the bell's activity reading through AppHeader and went red (9 cases) under the roster rule; only its MetadataProvider stub (one app listing hr_contract) and one grant payload changed.",
      "One existing assertion was reversed by ruling: sharedUserFeeds.activityGate-7476 'no MetadataProvider at all' asserted an unfiltered read and now asserts no read (no roster).",
      "New shared test fixture hooks/__tests__/activityRoster.tsx, outside the named test files."
     ],
     "files_changed": [
      ".changeset/12530-activity-business-only.md",
      "packages/app-shell/src/hooks/sharedUserFeeds.ts",
      "packages/app-shell/src/hooks/__tests__/activityRoster.tsx",
      "packages/app-shell/src/hooks/__tests__/sharedUserFeeds.activityRoster-12530.test.tsx",
      "packages/app-shell/src/hooks/__tests__/sharedUserFeeds.activityGate-7476.test.tsx",
      "packages/app-shell/src/hooks/__tests__/sharedUserFeeds.activityGrant-12081.test.tsx",
      "packages/app-shell/src/hooks/__tests__/sharedUserFeeds.activityType-6730.test.tsx",
      "packages/app-shell/src/layout/__tests__/AppHeader.inboxVariant.test.tsx"
     ],
     "gates": "type-check 0; feed vitest 0 (133/133); wider vitest 0 (1626/1626 at 95510b3bf); ablation red then restored; pre-tsc battery 15x exit 0; changeset-fixed 0; changeset-presence 0; new-line-citations 0; test-path-roots 0; eslint narrowed 0 errors"
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — objectui PR #12542 · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-15T16:31Z

    Reviewed on GitHub at head 84b8f56, against the card's What to build 1–3 and Acceptance, the seat's rulings in the dispatch and the claim (6195718220), not against the dev's report.

    • Shape: the PR is a draft against main. Its body opens Fixes objectstack-ai/objectui#12530, the only closing keyword, followed by Clause-②: no. 8 files.
    • Filter in the query (card item 3, the seat's ruling): useSharedActivityFeed is the one reading behind the bell's Activity tab, global:notifications and Home's activity card. It now reads sys_activity with $filter: { object_name: { $in: roster } } beside $top: 20, so top applies after the filter. The $in shape is the one this package already sends: sharedUserFeeds.ts' own notification read, useApproverDirectory and RecordDetailView.
    • The roster is derived, never written (card item 1):
      • activityObjectRoster walks filterActiveApps(apps) and every type: 'object' navigation entry, in areas and navigation at every depth.
      • Each entry is read through navEntriesShownToMember with the sidebar's useNavMemberGates, so the feed follows the same member gates the sidebar shows (the one predicate objectui#12426 shared).
      • There is no name list and no sys_ prefix rule.
    • Loading and empty states:
      • The roster string is part of the shared feed's key, so a roster change refetches. The dev measured that a new key resets the store to idle.
      • No key is issued until the app metadata type settles, so an unfiltered read never goes out.
      • An empty roster answers ready and empty, with no request.
    • Login stamps (card item 2): isLoginStampRow drops an updated sys_user row whose metadata.new names only last_login_at / last_login_ip. The dev measured the writer as objectstack AuthManager.stampLastLogin plus plugin-audit's diff. This is the one cut made after the read, because no queryable column separates a sign-in from a profile edit. It drops every user's sign-in stamp, not only the reader's own; a sign-in is not business activity for anyone.
    • Setup and audit surfaces (the generic object view, RecordDetailView) read sys_activity unchanged (card item 3).
    • Test changes:
      • sharedUserFeeds.activityGate-7476's "no MetadataProvider at all" case now asserts no read instead of an unfiltered read. That follows the ruling: no roster means no read. A standalone bell embed with no MetadataProvider now shows an empty activity feed.
      • AppHeader.inboxVariant.test.tsx (outside the claim's list) gets an app roster in its MetadataProvider stub, because it pins the bell's activity reading.
    • Pins and ablation: the roster names exactly the gated object entries, the query filter names them, no sys_* row is mapped, a login stamp is dropped even with sys_user in the roster, and a business row shows (CONTROL). Deleting the $filter line fails 5 of 18 pins. Restore was proven by blob hash.
    • Changeset: @object-ui/app-shell: minor, as the card asks.
    • Evidence: the closure build and the app-shell type-check exit 0. The 14 feed files give 133/133. The wider set gives 222 files and 1626 tests passed: hooks/, layout/, console/home/, RecordDetailView*, record-preview/ and the console's record-block reach. The battery exits 0.
    • Acceptance notes (not filed):
      • The roster is the objects an app lists in its navigation. An app object reachable only through a related list or a lookup, never from navigation, drops out of the feed.
      • Because the login-stamp cut happens after the read, a page of 20 can show fewer rows.
      • An admin whose Setup app lists sys_user, sys_organization or sys_team still sees edits to those records, because the roster follows the apps the user opens, as the card asks.
      • The HotCRM browser reading (no JWKS, migration or login row) is the maintainer's check.
    • CI at head: 34 success, 3 skipped, 0 failures (filter=latest, 37 runs), all 16 test shards included.

    Next: pr_ready + automerge_enable.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-15T16:50Z

    • Merged: PR fix(app-shell): the bell's and Home's activity feed reads the app objects only (#12530) #12542 merged through the merge queue as main 1f23d5b. The card closed by the PR's Fixes objectstack-ai/objectui#12530, which is the only closing keyword.
    • Verified by content: on origin/main, all 8 files the PR touches are blob-identical to the accepted head 84b8f56. They are sharedUserFeeds.ts, the roster fixture, the five activity pins, AppHeader.inboxVariant.test.tsx and .changeset/12530-activity-business-only.md.
    • The HotCRM browser reading after a fresh seed (the bell's Activity feed lists leads, accounts, opportunities and tasks; no JWKS key, migration or login row) is the maintainer's check.
    • Label: pm:dispatched is removed in the same stroke as this comment.

    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

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p1

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions