Skip to content

fix(plugin-audit): the activity list opens newest first, without platform rows, under a plain name - #30230

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-cikdr1
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-cikdr1

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

Fixes #30036

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:首页「查看全部动态」打开的列表默认按时间从新到旧,只列人对业务记录做的改动;元数据、迁移、用户与成员(含登录戳)、签名密钥这些平台行移到同页「全部条目」视图;标题改为「动态」,描述改成白话。
  • 为什么改:经理点进去想看「今天发生了什么」,看到的是乱序的原始表,夹着平台内部行,标题还和 CRM 自己的「活动」撞名。
  • 风险与回滚:只改 sys_activity 的声明(两个列表视图、标签、描述)和译文,不删数据、不改写入;回滚即撤销本 PR。
  • 先看:packages/plugins/plugin-audit/src/objects/sys-activity.object.ts(默认视图的过滤条件与排除名单)、sys-activity-default-view.integration.test.ts。
  • 两分钟验证:pnpm dev:crm 登录后新建一个客户,点首页「查看全部动态」,应只见刚才的客户行且最新在上;切到「全部条目」能看到登录戳、签名密钥与迁移行。
  • 需要你做的:无,席位落地。

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.feeds is 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 on sys_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_activity with no view named, and the Console then opens the first declared view (ObjectView.tsx: requested view, then the reader's pinned view, then an isDefault view, then views[0]). So the doors needed no change and no ui/** file is touched. The view sorts timestamp descending and filters:
    • actor_id is 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_name not 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, because check:platform-object-tenancy-census loads 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.
  • Label 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 own lifecycle.retention.maxAge).
  • Translations in the zh-CN / ja-JP / es-ES bundles (hand-written leaves; check-i18n-bundles --write regenerated en and 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:crm boots, 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 is changed_fields, and parent-field-query-guard.ts refuses 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 in all, not recent.

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 all view on the same page, and the compliance ledger sys_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 create are all on the ledger).

Acceptance

  1. The page opened from these doors shows newest first by default.

recent is first and sorts timestamp desc. Browser: three accounts created about a second apart list as 远航贸易, 星河科技, 华东物流有限公司 (newest first). Test: `recent` serves only what people did to records, newest first.

  1. Rows about platform internals (metadata history and drafts, migrations, a user's login stamps) are not in that default page. The rows stay available to an administrator on the audit surface.

Browser as admin: recent lists 3 rows; all lists 8, including the sign-in stamp, the JWKS mint, the migration and the two platform-setting rows. Drafts are sys_metadata rows and fall under the same exclusion. Audit surface: see above.

  1. The description a reader sees says what the page is, in plain words.

Browser header: 「最近大家对记录做了什么,按时间从新到旧:谁新建、修改或删除了什么。条目保留 14 天。」

  1. The page's title does not collide with an app's own 活动. The wording is the implementer's to propose on the PR.

Proposed above. Browser header: 「动态 / 最近动态」, with the CRM's own 「活动」 entry visible in the sidebar beside it.

  1. A browser check as an admin and as a member.

Admin: as above. Member, after merging origin/main with #30108's PR #30222 (member_default now reads sys_activity): a user the admin created and gave the CRM's sales_rep position. Home shows the activity card and its 查看全部动态 link; the link opens recent: 3 rows, 创建了 客户 "远航贸易" / "星河科技" / "华东物流有限公司", newest first. ?view=all shows 5: the same three plus 创建了 成员 "ID (member)" and 创建了 用户 "Mia Member", the platform rows recent leaves 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

  • New packages/plugins/plugin-audit/src/sys-activity-default-view.integration.test.ts (9 cases): the declaration (first view is recent, both views sort timestamp desc, the excluded list equals the definitions' names, the identity objects measured writing under a person's id are in it, all carries no filter, the old description and label are gone), and on a real engine + driver-sqlite-wasm + AuditPlugin: the real CRUD mirror leaves actor_id null 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's parseFilterAST, serve exactly the person's business rows newest first; all still lists the excluded kinds (sign-in stamp, metadata, signing key, member added, migration).
  • Ablation, one rule at a time with scripts/ablation-replace.mjs --delete (run at 61bb8996; neither rule line has changed since): removing the actor_id rule → 1 failed / 7 passed; removing the object_name rule → 1 failed / 7 passed; both restores verified blob == HEAD.
  • pnpm --filter @objectstack/plugin-audit test at 2110d932 (after merging origin/main): Test Files 71 passed (71), Tests 1137 passed (1137); pnpm --filter @objectstack/plugin-audit typecheck → exit 0.

Acceptance notes

  • recent is 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 in recent. The excluded list covers every platform row the card named plus the two measured on a fresh boot.
  • System-written rows on business objects (a seed, an automation run as the system) are out of recent by the actor rule, as they are out of Home's card ("real human actions only").
  • Screenshots: I have no image-hosting path through the relay, so the six screenshots (admin Home, admin recent, admin all, member Home, member recent, member all) 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/objectstack at head 2110d932 (after merging origin/main); each run with its exit code recorded before any pipe; --ran reconciliation: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN, every exit 0. On an earlier head check:platform-object-tenancy-census went red: the object file imported @objectstack/metadata-core's root entry, which that source loader cannot evaluate; fixed in 4cd5356dc. eslint --no-inline-config on the two changed source files: exit 0 (the repo-wide pnpm lint is CI's).

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-stale-fill :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-audit, touching 15 documentable anchor(s).

37 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 797f9257c988fbf93cc08cf672b37edf484d75e5.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 797f9257c988fbf93cc08cf672b37edf484d75e5 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from e6913dbc1191e89ab355e2e9a4ad2d4076e9bf14 — the merge of head 2110d932ef9e88a9755ddb81326ee931a8435bd4 into base 797f9257c988fbf93cc08cf672b37edf484d75e5, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 797f9257c988fbf93cc08cf672b37edf484d75e5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

席位核对 · ACCEPT · head 2110d932 · 2026-11-05T03:51Z

Head-sha: 2110d93

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.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 03:52
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 03:53
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 171b6dd Nov 5, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-cikdr1 branch November 5, 2026 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

查看全部动态 opens an unsorted system audit list: no time order, metadata and login rows mixed in, and a developer sentence as its description

1 participant