Repository navigation
fix(plugin-audit): the activity list opens newest first, without platform rows, under a plain name - #30230
Conversation
…abel Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
… rows, under a plain name Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
…ctivity view Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
…ity view Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
… alone Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 37 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e6913dbc1191e89ab355e2e9a4ad2d4076e9bf14 && git checkout e6913dbc1191e89ab355e2e9a4ad2d4076e9bf14
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 797f9257c988fbf93cc08cf672b37edf484d75e5 2110d932ef9e88a9755ddb81326ee931a8435bd4 && git checkout -B drift-repro 797f9257c988fbf93cc08cf672b37edf484d75e5 && git merge --no-ff 2110d932ef9e88a9755ddb81326ee931a8435bd4
node scripts/docs-audit/affected-docs.mjs --json 797f9257c988fbf93cc08cf672b37edf484d75e5
|
…t activity view Claude-Session: https://claude.ai/code/session_01Utp6CXc6hMgykEtjFYQXjy Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb 席位核对 · ACCEPT · head
|
| Item | Reading |
|---|---|
| Shape | Draft, base main, relay pr_create. Body (v2, written by the seat per os-dev.md rule 3): attribution, signature, Fixes #30036, Clause-②: / Metadata-protocol: at line start, ## 维护者速读. |
| Acceptance | sys_activity declares recent first (actor set, platform objects excluded, newest first) and all (every readable row, newest first); label Activity Feed / 动态; the description says what the page is. Home's 查看全部动态 and the bell's tab land on recent (no ui change: the bare object route opens the first view). |
| Member re-check (after #30222) | Admin: recent 3 business rows newest first, all 8. Member (sales_rep): 查看全部动态 shown, lands on recent, 3 rows, no platform rows; all 5. The re-check found the admin-written "created member / user" rows leaking into recent; the exclusion now covers the whole identity domain, derived from @objectstack/platform-objects/identity (pinned, 9/9). Screens in the project files. |
| Gates | Dev at 2110d932: plugin-audit 1137/1137, typecheck 0, derived gates 60/60, --ran 0 not measured. CI: 33 success, 0 failure; the body-guard reruns after the body edit are pending and gate the queue. |
| Declarations | Clause-②: no: list views, label and description on an existing system object; ACTIVITY_FEED_EXCLUDED_OBJECTS is not re-exported from the package entry. Metadata-protocol: none; changeset patch. |
| Docs | Drift list read by the seat (references/data/feed.mdx, frontend/plugins/plugin-detail.mdx, references/api/record-page.mdx, permissions/system-context.mdx, references/ui/component.mdx, ui/components/record.mdx): none states sys_activity's label, description or default view, so none is made false. |
| Landing | Not governed (0 of 7 paths), 400 changed lines (< 3000). Seat lands: pr_ready + automerge_enable. |
Requested by Bill · project thread
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb
Fixes #30036
Clause-②: no
Metadata-protocol: none
维护者速读
Proposed title wording (acceptance 4)
The object label becomes Activity Feed (zh-CN 动态, ja-JP アクティビティフィード, es-ES Feed de actividad), with the default view titled Recent activity (zh-CN 最近动态) and the second view All entries (zh-CN 全部条目). In the CRM this reads 「动态 / 最近动态」 beside the app's own 「活动」 sidebar entry, and matches the Home link's own word 「查看全部动态」. One overlap to weigh: the Studio capability toggle
enable.feedsis also rendered 动态 in zh-CN; it labels a switch on the object form, not a page, so I kept 动态 here. If you prefer to keep them apart, 动态流 is the alternative.What changes
packages/plugins/plugin-audit/src/objects/sys-activity.object.ts(the platform half the triage routed here) declares two list views onsys_activity, plus a new label and description:recent, declared first. Both doors (Home's 查看全部动态,HomePage.tsx, and the bell's Activity tab,InboxPopover.tsx) open/apps/APP/sys_activitywith no view named, and the Console then opens the first declared view (ObjectView.tsx: requested view, then the reader's pinned view, then anisDefaultview, thenviews[0]). So the doors needed no change and noui/**file is touched. The view sortstimestampdescending and filters:actor_idis not null: a person acted. A system write leaves the actor null (ADR-0118 D1): the migration ledger, platform settings written at boot, seeds.object_namenot in: the metadata store (sys_metadata,sys_metadata_history,sys_metadata_commit,sys_metadata_audit), the data-migration ledger (sys_migration,sys_migration_journal), and every object the identity domain declares, derived from the exports of@objectstack/platform-objects/identity(sys_user,sys_member,sys_organization,sys_invitation,sys_session,sys_jwks, teams, business units, OAuth and SCIM objects, …). The metadata-store names are spelled out, becausecheck:platform-object-tenancy-censusloads every object file from source and cannot evaluate@objectstack/metadata-core's root entry (its only export of those definitions); the new test holds them equal to the definitions' own names, so a rename reds there.all: every row the reader may read, newest first, no filter.Activity Feed, description "What people did to records recently, newest first: who created, changed or deleted what. Entries are kept for 14 days." (the 14 days is the object's ownlifecycle.retention.maxAge).check-i18n-bundles --writeregeneratedenand dropped the source-hash entries for the re-translated leaves).Why the identity domain is excluded by object rather than by actor (measured on fresh
pnpm dev:crmboots, see Verification): the sign-in stamp (Updated User "Dev Admin": Last Login At and Last Login IP), the first signing-key mint (Created 签名密钥 (JWKS)) and an administrator adding a person (Created User "Mia Member",Created Member "ID (member)") are all written under a person's own id, not as the system, and the added person can read the last two. The only column that tells a sign-in stamp from a profile edit ischanged_fields, andparent-field-query-guard.tsrefuses a filter on that column for any reader withheld a field anywhere, so a default view naming it would 403 for members. Consequence: an administrator's edits to users, members and organizations are inall, notrecent.Rows stay where they are (acceptance 2, second sentence): nothing is deleted and nothing changes in what is written. An administrator reaches every excluded row in two places: the
allview on the same page, and the compliance ledgersys_audit_log(Setup's audit log), which holds its own row for each CRUD-mirrored write (measured:sys_migration create,sys_user update,sys_platform_setting create,sys_platform_setting_audit createare all on the ledger).Acceptance
recentis first and sortstimestampdesc. Browser: three accounts created about a second apart list as 远航贸易, 星河科技, 华东物流有限公司 (newest first). Test:`recent` serves only what people did to records, newest first.Browser as admin:
recentlists 3 rows;alllists 8, including the sign-in stamp, the JWKS mint, the migration and the two platform-setting rows. Drafts aresys_metadatarows and fall under the same exclusion. Audit surface: see above.Browser header: 「最近大家对记录做了什么,按时间从新到旧:谁新建、修改或删除了什么。条目保留 14 天。」
Proposed above. Browser header: 「动态 / 最近动态」, with the CRM's own 「活动」 entry visible in the sidebar beside it.
Admin: as above. Member, after merging
origin/mainwith #30108's PR #30222 (member_defaultnow readssys_activity): a user the admin created and gave the CRM'ssales_repposition. Home shows the activity card and its 查看全部动态 link; the link opensrecent: 3 rows, 创建了 客户 "远航贸易" / "星河科技" / "华东物流有限公司", newest first.?view=allshows 5: the same three plus 创建了 成员 "ID (member)" and 创建了 用户 "Mia Member", the platform rowsrecentleaves out. The plain API read of the object (no view) returns the same 5 in no time order, which is the defect the default view closes.Tests
packages/plugins/plugin-audit/src/sys-activity-default-view.integration.test.ts(9 cases): the declaration (first view isrecent, both views sorttimestampdesc, the excluded list equals the definitions' names, the identity objects measured writing under a person's id are in it,allcarries no filter, the old description and label are gone), and on a real engine +driver-sqlite-wasm+AuditPlugin: the real CRUD mirror leavesactor_idnull for a system write and the person for a person's;recent's own rules, lowered the way the Console lowers a view filter and parsed by the server'sparseFilterAST, serve exactly the person's business rows newest first;allstill lists the excluded kinds (sign-in stamp, metadata, signing key, member added, migration).scripts/ablation-replace.mjs --delete(run at61bb8996; neither rule line has changed since): removing theactor_idrule → 1 failed / 7 passed; removing theobject_namerule → 1 failed / 7 passed; both restores verifiedblob == HEAD.pnpm --filter @objectstack/plugin-audit testat2110d932(after mergingorigin/main): Test Files 71 passed (71), Tests 1137 passed (1137);pnpm --filter @objectstack/plugin-audit typecheck→ exit 0.Acceptance notes
recentis the same cut as Home's activity card only as far as a query column can say it. Home narrows to the objects the reader's apps list (a client-side roster); a server-declared view cannot know that, so a person's edit of another platform object (a permission set, a setting) still appears inrecent. The excluded list covers every platform row the card named plus the two measured on a fresh boot.recentby the actor rule, as they are out of Home's card ("real human actions only").recent, adminall, member Home, memberrecent, memberall) are held with the seat; the readings above are the page text captured from the same runs.Verification
Browser, admin (fresh pnpm dev:crm --fresh, Console via Vite, zh-CN)
Home → 查看全部动态 →
/apps/com.example.crm/sys_activity. Header 「动态 / 最近动态 · 3 · 只读 · 审计日志」. Rows: 创建了 客户 "远航贸易" / "星河科技" / "华东物流有限公司", each by Dev Admin.?view=all: header 「动态 / 全部条目 · 8」. Rows add: 创建了 签名密钥 (JWKS) (Dev Admin), 更新了 用户 "Dev Admin":最近登录时间和最近登录 IP (Dev Admin), 创建了 数据迁移 "pinyin-search-companion" (—), 创建了 Platform Setting Audit Entry (—), 创建了 Platform Setting "api_key_policy_started_at" (—).Browser, member (sales_rep position, after #30222)
Home: 1 「查看全部动态」 link →
/apps/com.example.crm/sys_activity. Header 「动态 / 最近动态 · 3」. Rows: 创建了 客户 "远航贸易" / "星河科技" / "华东物流有限公司", each by Dev Admin, newest first.?view=all: header 「动态 / 全部条目 · 5」; adds 创建了 成员 "ID (member)" and 创建了 用户 "Mia Member" (both by Dev Admin).Before #30222 (head
4cd5356dc):GET /api/v1/data/sys_activity→PERMISSION_DENIED; no Home link; direct URL 「无权访问」.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat head2110d932(after mergingorigin/main); each run with its exit code recorded before any pipe;--ranreconciliation: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN, every exit 0. On an earlier headcheck:platform-object-tenancy-censuswent red: the object file imported@objectstack/metadata-core's root entry, which that source loader cannot evaluate; fixed in4cd5356dc.eslint --no-inline-configon the two changed source files: exit 0 (the repo-widepnpm lintis CI's).Generated by Claude Code