Repository navigation
fix(plugin-audit): the activity list opens newest first, without platform rows, under a plain name - #30230
fix(plugin-audit): the activity list opens newest first, without platform rows, under a plain name#30230objectstack-fleet[bot] wants to merge 9 commits into
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>
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),sys_userandsys_jwks. Names come fromSystemObjectNameand theplatform-objectsdefinitions; the three 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 the list 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
sys_userandsys_jwksare excluded by object rather than by actor (measured on a freshpnpm dev:crm, see Verification): the sign-in stamp (Updated User "Dev Admin": Last Login At and Last Login IP) and the first signing-key mint (Created 签名密钥 (JWKS)) are both written under the signing-in person's own id, not as the system. 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 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 (a
member_defaultuser created by the admin): the server refuses the read (GET /data/sys_activityanswers403 PERMISSION_DENIED), so Home renders no activity card and no 查看全部动态 link, and the direct URL shows 「动态 / 最近动态」, the new description and 「无权访问 · 你没有查看这些记录的权限」. That is today's grant, not this change:member_defaultdoes not grantsys_activityread. Adjacent: #30108 (seat 2) grants it. Once it lands, a member's rows are narrowed by the parent-record read gate and then byrecent's filter; that filter names onlyactor_id,object_nameandtimestamp, none of them a columnparent-field-query-guard.tsguards, so it is admitted for every reader. I will mergeorigin/mainagain after #30108 lands and repeat the member check.Tests
packages/plugins/plugin-audit/src/sys-activity-default-view.integration.test.ts(8 cases): the declaration (first view isrecent, both views sorttimestampdesc, the excluded list equals the definitions' names,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.scripts/ablation-replace.mjs --delete: removing theactor_idrule → 1 failed / 7 passed; removing theobject_namerule → 1 failed / 7 passed; both restores verifiedblob == HEAD.pnpm --filter @objectstack/plugin-audit test→ Test Files 71 passed (71), Tests 1136 passed (1136);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, member direct URL) 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
Home: 0 「查看全部动态」 links. Direct URL: 「动态 / 最近动态」, description shown, 「无权访问 — 你没有查看这些记录的权限。如需访问,请联系管理员。」 API:
GET /api/v1/data/sys_activity→PERMISSION_DENIED.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat head4cd5356dc; each run with its exit code recorded before any pipe;--ranreconciliation: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN, every exit 0. On the first passcheck:platform-object-tenancy-censuswent red: the object file imported@objectstack/metadata-core's root entry, which that source loader cannot evaluate (import.metaoutside a module); fixed in4cd5356dcand green on the final run.check-plugin-teardown-shape --self-testexited 3 on the shallow worktree until its fixture commit was fetched, then 0.eslint --no-inline-configon the two changed source files: exit 0 (the repo-widepnpm lintis CI's).Generated by Claude Code