Repository navigation
fix(plugin-audit): render the activity summary in the reader's language - #26258
Conversation
… (red on main) Claude-Session: https://claude.ai/code/session_01UBPXQdapfjkjYdQck9dtTa Co-authored-by: Claude <noreply@anthropic.com>
…t in the reader's language (WIP: write-time locale pins to migrate) Claude-Session: https://claude.ai/code/session_01UBPXQdapfjkjYdQck9dtTa Co-authored-by: Claude <noreply@anthropic.com>
…e write locale to mention titles Claude-Session: https://claude.ai/code/session_01UBPXQdapfjkjYdQck9dtTa Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBPXQdapfjkjYdQck9dtTa Co-authored-by: Claude <noreply@anthropic.com>
…ext page; type the new tests' engine options Claude-Session: https://claude.ai/code/session_01UBPXQdapfjkjYdQck9dtTa Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 21 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 7e7197baf5095f8865c6b14952ecdc21fa92f81c && git checkout 7e7197baf5095f8865c6b14952ecdc21fa92f81c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 81bba6c3f5be5b6cbabf76511383940349f23b15 fa1d3912d6abbbe1b77583276439c88048666bf3 && git checkout -B drift-repro 81bba6c3f5be5b6cbabf76511383940349f23b15 && git merge --no-ff fa1d3912d6abbbe1b77583276439c88048666bf3
node scripts/docs-audit/affected-docs.mjs --json 81bba6c3f5be5b6cbabf76511383940349f23b15
|
席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, Fixes #26167, a patch changeset for @objectstack/plugin-audit, Clause-②: no, Metadata-protocol: none (measured, as the claim asked), mergeable clean. |
| Files | 14 files, +1294/−269. All but one are in packages/plugins/plugin-audit/**. The exception is content/docs/permissions/system-context.mdx row 45, the new read-site anchor that check-system-context-census requires, as the amendment covers. No ui/ file changes, so the serial rule with #26055 holds. |
| Fix | Hypothesis A held: the server renders on read. The writer stores the summary as a code (template key, object name, field keys, rendered values plus the matched option) in metadata.summary_message. It also keeps the English-fallback sentence in summary, composed through the same composeActivitySummary. A sys_activity engine middleware re-composes summary in the reader's ExecutionContext.locale, negotiated by resolveBundleLocale and rendered with II18nService.t(), the same sequence plugin-approvals uses. A miss, a throw, or a read with no locale keeps the stored sentence. For a non-system caller the code is stripped from the served metadata, and the middleware runs inside the field redaction, so the composed text is judged by the same sources. |
| Rows written before the change | Served as written, with no migration and no backfill. That is the claim's default, and the report states it. |
| Evidence | Red first on main: 4 failed and 3 passed, and the ablation reproduces the same. 102/102 derived gates exit 0 and --ran reconciles with 0 NOT-MEASURED. On this head, 38 check runs succeeded and 10 were expected skips, with 0 failed. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay. |
Generated by Claude Code
Fixes #26167
Clause-②: no (no exported type, schema field or spec key changes; the code rides in the existing
sys_activity.metadataJSON, andsummarykeeps its type and meaning)Metadata-protocol: none
Maintainer ruling on the card: 「同意,立卡」. The activity summary is stored as its message key plus params and rendered in the reader's language, as the approvals precedents do (#25627, the
admin_rescuenote; #24977). The English sentence stays insummaryas the fallback text.What changed
The code is all in
packages/plugins/plugin-audit. One docs row changes too:content/docs/permissions/system-context.mdxrow 45 now cites the new read site, becausecheck-system-context-censusrequires every elevation read to have an anchor. Nopackages/spec/**file and noui/**file is touched, so the serial rule with PR #26055 holds.New
activity-summary-message.ts. It holds one composer,composeActivitySummary, which the writer and the reader both use. The code is one of three shapes:activityCreated/activityDeleted/activityUpdatedwith{object, label}activityUpdatedFieldswith{object, label, fields, more}fieldChangeswith{object, changes: [{field, from, to}]}, where each side is{text, option?}The object param is the object name. Object, field and option labels are resolved per reader from that reader's pack (
objects.NAME.label,…fields.FIELD.label,…options.VALUE), with the authored label as the fallback.The writer (
audit-writers.ts). Each CRUD mirror row stores the English sentence insummaryand the code inmetadata.summary_message. The write no longer asks for a locale for the summary. Milestone summaries (the plugin-audit:activityMilestonessummary templates still interpolate raw lookup ids —{owner_id}rendersoBK25…after #7230 #7290 path) store no code and keep their authored text.The reader (
installActivitySummaryLocale, registered inaudit-plugin.tsafterinstallActivityFieldRedaction). It is an engine middleware onsys_activityfind/findOne.ctx.context.localeagainst the held packs withresolveBundleLocale, so a barezhreads the zh-CN pack.summary.summaryonly, it addsmetadataand removes it again afterwards.metadatafor every caller that is not the system, because its params are field values.Hypothesis A held: rendering happens on read, on the server. The Discussion feed fetches its rows with
dataSource.find('sys_activity', …)(ui/packages/plugin-detail/src/renderers/record-activity.tsx) and showsrow.summary(recordActivityFeed.ts). The authenticated fetch sendsAccept-Languageon every API call (ui/packages/auth/src/createAuthenticatedFetch.ts), and the REST door resolves it into the caller context'slocale. So the server overlay changes what the feed shows, with no UI change, and Hypothesis B was not needed.Rows written before this change
They stay as written, for every reader. There is no migration and no backfill, and no row is dropped. A row with no
summary_messagein itsmetadatais served its storedsummaryunchanged. This is pinned by the last case of the integration test, which uses a row in the earlier{ old, new }metadata shape.Tests
The red test came first, in commit cb6b6af:
src/activity-summary-reader-locale.integration.test.ts. It runs on a real kernel with ObjectQL, SQLite, a security double and AuditPlugin. It writes as the system with the workspace locale left aten, then reads as a zh-CN caller.main(fbb7599) it fails 4 and passes 3:Expected: "创建了 合同 "NDA"",Received: "Created Contract "NDA"", and the lock reportedVERDICT command-exit 1.创建了 合同 "NDA",阶段: 草稿 → 已签署and更新了 合同 "NDA":备注. The system, an en-US reader and a reader with no locale get the English sentences. A barezhis negotiated to zh-CN, and fr-FR keeps the stored text. The code is served only to the system. Afields: ['summary']projection still composes. A reader who is not servednotegets no summary for that row. A row written before the change keeps its text.The unit tests for the composer and the overlay are in
src/activity-summary-message.test.ts.Six existing suites pinned the summary being localized at write time. They now assert the stored English sentence plus the text a reader gets through the same overlay, using
src/__tests__/summary-as-read.ts. The workspace-locale and host-locale suites now pin the remaining write-locale consumer, which is the @mention notification title (Ada 提到了你). One new case asserts that an audited write asks for no locale at all.Local results:
pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2: Test Files 62 passed (62), Tests 1013 passed (1013), exit 0.pnpm --filter @objectstack/plugin-audit typecheck(which includescheck:test-typecheck): exit 0.packages/plugins/plugin-audit: exit 0.installActivitySummaryLocale(engine as any, getI18n);inaudit-plugin.ts, usingscripts/ablation-replace.mjs. The integration test then failed 4 and passed 3, the same as onmain. The restore matched the HEAD blob andgit diff HEADwas empty.--commandson this diff derives 102 families. All 102 ran and exit 0, and--ranreconciles as 102 derived, 102 run, 0 NOT-MEASURED. Five of them were re-run before they went green:check-system-context-censusfound the new read site with no anchor on the page. The docs row above fixes it.check:query-options-erasurefound one new erased options bag in the tests. The new tests now pass typed options.check-plugin-teardown-shape --self-testfailed on a shallow clone and passed after unshallowing.check:i18nandcheck:dual-build-cjs-loadsfailed for missing builds and passed after building.Acceptance notes
summaryissearchable, and its stored text is now the English sentence wherever the workspace locale is set. So in a Chinese workspace, full-text search oversummarymatches the English words for rows written after this change. This follows the ruling that the English sentence is the fallback text. The same goes for an export of the raw column.AuditPluginOptions.getLocale, otherwise the localization settings. Only its doc comment changed.pnpm demo:zhConsole check. That repo is outside this session. The read path was measured from the code above and is pinned end to end by the integration test.Generated by Claude Code