You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
plugin-audit: an activity summary is rendered once, at write time, in the workspace locale — store it as a key and render it in the reader's language #26167
Filing class: ③ maintainer-directed — the maintainer, replying to this suggestion in the hotclm regression thread on 2026-10-26: 「同意,立卡」. Reach: public door, measured on 18.1.0 with objectstack-ai/hotclm main (pnpm demo:zh, Console in zh-CN). Reader: the plugin-audit lane (audit-writers.ts summary writer) and the record feed renderer in objectui.
Business reason
A record's Discussion feed is read by everyone who works on the record, in their own language. Today its system rows are frozen in whatever language the workspace default was at the moment of each write: an English-default workspace shows Created Contract "…" to a Chinese reader forever, a workspace that switches language keeps every older row in the old one, and two colleagues with different languages see the same row in one of them. Without the change, every multi-language deployment has a half-translated feed that no translation pack can fix after the fact.
Measured
hotclm, pnpm demo:zh (workspace locale left at its default en), Console in zh-CN: the 讨论 tab of a contract shows Created Contract "回归测试 NDA — 已有相对方" and eight Updated Contract "…" rows, although @objectstack/plugin-audit 18.1.0 ships activityCreated: '创建了 {{object}} "{{label}}"'. (The app-side half — the demo should set its workspace locale — is objectstack-ai/hotclm#142.)
Why
audit-writers.ts resolves resolveWriteLocale(tenantId, userId) (ADR-0053 localization.locale, framework#3039) and stores the interpolated sentence in sys_activity.summary; the object label is translated at the same moment.
Proposal
Store the summary as its message key plus params (messages.activityCreated, {object: <object name>, label}), keep the English sentence as the fallback text, and render it per reader — the shape plugin-approvals already took for the admin_rescue timeline note (#25627, "store … as a code and render it in the reader's language") and for flow/step labels (#24977). Existing rows keep their stored text.
Dedupe: objectstack and objectui issues (2,000 most recently updated each, all states) grepped for activity … locale|language|i18n|render, activityCreated, reader … locale — neighbours #24235/#24261 (the write-time locale cache, closed), #25627 and #24977 (reader-locale precedents in approvals), none proposes this for activity summaries.
Filed from the hotclm browser regression thread (2026-10-26).
The maintainer's 「同意,立卡」. Store the audit summary as a message key plus its parameters, and render it in the reader's locale. Rows written before the change keep their frozen text: the PR states whether they stay as-is or get a one-time re-render, and does not silently drop them.
Two lanes: the plugin-audit writer (server) and the record feed renderer (ui/). One PR in this tree.
Claim: PM loop round 8 · card thread
Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01UBPXQdapfjkjYdQck9dtTa
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-xshjzo (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#1
Ruling-ref: the maintainer's 「同意,立卡」, quoted on the card, and triage first grade (2026-10-26T14:55Z). Store the activity summary as its message key plus params, keep the English sentence as the fallback summary, and render in the reader's locale. Rows written before the change keep their text unless the PR gives a measured reason otherwise; they are never dropped silently. One PR.
File surface: packages/plugins/plugin-audit/** plus the read path the measurement picks (server render on read preferred). ui/ only if needed, and ⛔ not the files of open PR #26055 while it is open. ⛔ No packages/spec/** (stop on breach; explain in the report)
Container & model: S, mode:thread, default model and effort (set at thread open)
Clause-②: no — provisional; the dev measures it on the diff (a stored field on sys_activity may be a schema change) and the PR body carries the measured value
Metadata-protocol: none — provisional; the dev measures it on the diff
Responsibility: a record's Discussion feed shows system rows frozen in the write-time workspace locale (audit-writers.ts stores the interpolated sentence)
Thread-read: none
Premise: holds (read 2026-10-26T15:54Z at origin/mainfbb75998ba: card open, unassigned, pm:queue, no claim; audit-writers.ts still interpolates messages.activityCreated at write time)
Serial constraints cleared: PR #26055 (domain:ui, #25959) edits the ui activity renderer and the locales, so this claim stays off those files while it is open; no other open PR touches plugin-audit/src/audit-writers.ts; the dev re-measures before pr_create
{
"issue": 26167,
"status": "done",
"branch": "claude/project-thread-xshjzo",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26258",
"session": "session_01UBPXQdapfjkjYdQck9dtTa",
"premise_still_valid": true,
"summary": "Each CRUD mirror row in sys_activity now stores the English sentence in summary as the fallback, and the message key plus params in metadata.summary_message. The params carry the object name, the record label, the field names, and from/to values with option values. A new sys_activity find/findOne middleware in plugin-audit (installActivitySummaryLocale) negotiates the caller's locale against the held packs, composes the summary with that reader's object, field and option labels, and drops the code for non-system callers. Hypothesis A held: the Discussion feed reads dataSource.find('sys_activity') and shows row.summary, and the authed fetch sends Accept-Language, so no ui file changed and the serial rule with #26055 holds. Rows written before the change have no code and stay as written, with no migration or backfill (pinned by a test). File surface: plugin-audit/** plus one docs row, content/docs/permissions/system-context.mdx row 45, which check-system-context-census requires for the new read site. Please amend the claim's file surface to include it.",
"tests": "Red first, commit cb6b6af2: src/activity-summary-reader-locale.integration.test.ts on main fbb75998 failed 4 and passed 3, with Expected '创建了 合同 \"NDA\"' and Received 'Created Contract \"NDA\"' (lock VERDICT command-exit 1). Green at fa1d3912: pnpm --filter @objectstack/plugin-audit exec vitest run gave Test Files 62 passed (62) and Tests 1013 passed (1013), exit 0. The two files edited after that run were re-run, 8/8 passed, exit 0. pnpm --filter @objectstack/plugin-audit typecheck (which includes check:test-typecheck) exited 0, and eslint on packages/plugins/plugin-audit exited 0. Ablation: ablation-replace.mjs disabled the installActivitySummaryLocale registration in audit-plugin.ts, the mutation was verified on disk (blob ad14d3cc to dc00e279), and the integration test failed 4 and passed 3. The restore matched the HEAD blob and git diff HEAD was empty. No dist step applies, because the suite imports src.",
"gates": "dispatch-gates --commands on the final diff derives 102 families. All 102 ran with exit 0, each code captured before any pipe. --ran reconciles 102 derived, 102 run, 0 NOT-MEASURED, 0 UNRUN. First-pass reds were re-run to green: check-system-context-census exited 1 for a missing anchor, fixed by the docs row. check:query-options-erasure exited 1 because the test surface went from 236 to 237, fixed by typing the new tests' options, and it is back to 236. check-plugin-teardown-shape --self-test exited 3 on a shallow clone and passed after unshallowing. check:i18n exited 3 until its build closure was built, then reported all 10 packages in sync. check:dual-build-cjs-loads exited 3 until a full pnpm build, then exited 0. check:nul-bytes exited 0. CI check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 relay strokes. (1) The pr_create dispatch fw-20261026T172240Z-57c06e: POST /repos/objectstack-ai/objectstack/dispatches, which the relay executed as POST /pulls (draft) and POST /issues/26258/assignees [os-project-manager]. The body read back as 7171 of 7171 bytes, identical. (2) This os-dev-report comment, through post-stamped.mjs: POST /issues/26167/comments. git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"Acceptance note, not filed: summary is searchable, and its stored text is now English, so full-text search over summary in a zh-CN workspace matches English words for new rows. This follows the ruling that English is the fallback text. carrier: none (承接者:无)",
"Not measured: the hotclm pnpm demo:zh Console check. hotcrm/hotclm is outside this run; the read path was measured from code and is pinned end to end by the integration test."
]
}
Claim amendment: PM loop round 8 · card thread (supersedes 6420565917 by widening the file surface by one docs file; everything else stands)
Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01UBPXQdapfjkjYdQck9dtTa
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-xshjzo (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#1
Ruling-ref: the maintainer's 「同意,立卡」, quoted on the card, and triage first grade (2026-10-26T14:55Z). Store the activity summary as its message key plus params, keep the English sentence as the fallback summary, and render in the reader's locale. Rows written before the change keep their text unless the PR gives a measured reason otherwise; they are never dropped silently. One PR.
File surface: packages/plugins/plugin-audit/** plus the read path the measurement picks (server render on read preferred). ui/ only if needed, and ⛔ not the files of open PR #26055 while it is open. Also content/docs/permissions/system-context.mdx: row 45 gains the new read-site anchor that check-system-context-census requires (the seat agreed 2026-10-26T17:26Z). ⛔ No packages/spec/** (stop on breach; explain in the report)
Container & model: S, mode:thread, default model and effort (set at thread open)
Clause-②: no — provisional; the dev measures it on the diff (a stored field on sys_activity may be a schema change) and the PR body carries the measured value
Metadata-protocol: none — provisional; the dev measures it on the diff
Responsibility: a record's Discussion feed shows system rows frozen in the write-time workspace locale (audit-writers.ts stores the interpolated sentence)
Thread-read: none
Premise: holds (read 2026-10-26T17:26Z at origin/main1eebbc08ba: card open, pm:dispatched, assignee os-project-manager, this seat's claim the newest; the dev reproduced red first on main)
Serial constraints cleared: PR #26055 (domain:ui, #25959) edits the ui activity renderer and the locales, so this claim stays off those files while it is open; no other open PR touches plugin-audit/src/audit-writers.ts; the dev re-measures before pr_create
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade —
enhancement·priority:p2·domain:server·area:i18n·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T14:55Z⛔ Not a claim, ⛔ not a dispatch.
plugin-auditwriter (server) and the record feed renderer (ui/). One PR in this tree.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 8 · card thread
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_01UBPXQdapfjkjYdQck9dtTaAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-xshjzo(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: the maintainer's 「同意,立卡」, quoted on the card, and triage first grade (2026-10-26T14:55Z). Store the activity summary as its message key plus params, keep the English sentence as the fallback
summary, and render in the reader's locale. Rows written before the change keep their text unless the PR gives a measured reason otherwise; they are never dropped silently. One PR.File surface:
packages/plugins/plugin-audit/**plus the read path the measurement picks (server render on read preferred).ui/only if needed, and ⛔ not the files of open PR #26055 while it is open. ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
S,mode:thread, default model and effort (set at thread open)Clause-②: no — provisional; the dev measures it on the diff (a stored field on
sys_activitymay be a schema change) and the PR body carries the measured valueMetadata-protocol: none — provisional; the dev measures it on the diff
Responsibility: a record's Discussion feed shows system rows frozen in the write-time workspace locale (
audit-writers.tsstores the interpolated sentence)Thread-read: none
Premise: holds (read 2026-10-26T15:54Z at
origin/mainfbb75998ba: card open, unassigned,pm:queue, no claim;audit-writers.tsstill interpolatesmessages.activityCreatedat write time)Serial constraints cleared: PR #26055 (
domain:ui, #25959) edits the ui activity renderer and the locales, so this claim stays off those files while it is open; no other open PR touchesplugin-audit/src/audit-writers.ts; the dev re-measures beforepr_createobjectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 26167, "status": "done", "branch": "claude/project-thread-xshjzo", "pr": "https://github.com/objectstack-ai/objectstack/pull/26258", "session": "session_01UBPXQdapfjkjYdQck9dtTa", "premise_still_valid": true, "summary": "Each CRUD mirror row in sys_activity now stores the English sentence in summary as the fallback, and the message key plus params in metadata.summary_message. The params carry the object name, the record label, the field names, and from/to values with option values. A new sys_activity find/findOne middleware in plugin-audit (installActivitySummaryLocale) negotiates the caller's locale against the held packs, composes the summary with that reader's object, field and option labels, and drops the code for non-system callers. Hypothesis A held: the Discussion feed reads dataSource.find('sys_activity') and shows row.summary, and the authed fetch sends Accept-Language, so no ui file changed and the serial rule with #26055 holds. Rows written before the change have no code and stay as written, with no migration or backfill (pinned by a test). File surface: plugin-audit/** plus one docs row, content/docs/permissions/system-context.mdx row 45, which check-system-context-census requires for the new read site. Please amend the claim's file surface to include it.", "tests": "Red first, commit cb6b6af2: src/activity-summary-reader-locale.integration.test.ts on main fbb75998 failed 4 and passed 3, with Expected '创建了 合同 \"NDA\"' and Received 'Created Contract \"NDA\"' (lock VERDICT command-exit 1). Green at fa1d3912: pnpm --filter @objectstack/plugin-audit exec vitest run gave Test Files 62 passed (62) and Tests 1013 passed (1013), exit 0. The two files edited after that run were re-run, 8/8 passed, exit 0. pnpm --filter @objectstack/plugin-audit typecheck (which includes check:test-typecheck) exited 0, and eslint on packages/plugins/plugin-audit exited 0. Ablation: ablation-replace.mjs disabled the installActivitySummaryLocale registration in audit-plugin.ts, the mutation was verified on disk (blob ad14d3cc to dc00e279), and the integration test failed 4 and passed 3. The restore matched the HEAD blob and git diff HEAD was empty. No dist step applies, because the suite imports src.", "gates": "dispatch-gates --commands on the final diff derives 102 families. All 102 ran with exit 0, each code captured before any pipe. --ran reconciles 102 derived, 102 run, 0 NOT-MEASURED, 0 UNRUN. First-pass reds were re-run to green: check-system-context-census exited 1 for a missing anchor, fixed by the docs row. check:query-options-erasure exited 1 because the test surface went from 236 to 237, fixed by typing the new tests' options, and it is back to 236. check-plugin-teardown-shape --self-test exited 3 on a shallow clone and passed after unshallowing. check:i18n exited 3 until its build closure was built, then reported all 10 packages in sync. check:dual-build-cjs-loads exited 3 until a full pnpm build, then exited 0. check:nul-bytes exited 0. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 relay strokes. (1) The pr_create dispatch fw-20261026T172240Z-57c06e: POST /repos/objectstack-ai/objectstack/dispatches, which the relay executed as POST /pulls (draft) and POST /issues/26258/assignees [os-project-manager]. The body read back as 7171 of 7171 bytes, identical. (2) This os-dev-report comment, through post-stamped.mjs: POST /issues/26167/comments. git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "Acceptance note, not filed: summary is searchable, and its stored text is now English, so full-text search over summary in a zh-CN workspace matches English words for new rows. This follows the ruling that English is the fallback text. carrier: none (承接者:无)", "Not measured: the hotclm pnpm demo:zh Console check. hotcrm/hotclm is outside this run; the read path was measured from code and is pinned end to end by the integration test." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim amendment: PM loop round 8 · card thread (supersedes 6420565917 by widening the file surface by one docs file; everything else stands)
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_01UBPXQdapfjkjYdQck9dtTaAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-xshjzo(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: the maintainer's 「同意,立卡」, quoted on the card, and triage first grade (2026-10-26T14:55Z). Store the activity summary as its message key plus params, keep the English sentence as the fallback
summary, and render in the reader's locale. Rows written before the change keep their text unless the PR gives a measured reason otherwise; they are never dropped silently. One PR.File surface:
packages/plugins/plugin-audit/**plus the read path the measurement picks (server render on read preferred).ui/only if needed, and ⛔ not the files of open PR #26055 while it is open. Alsocontent/docs/permissions/system-context.mdx: row 45 gains the new read-site anchor thatcheck-system-context-censusrequires (the seat agreed 2026-10-26T17:26Z). ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
S,mode:thread, default model and effort (set at thread open)Clause-②: no — provisional; the dev measures it on the diff (a stored field on
sys_activitymay be a schema change) and the PR body carries the measured valueMetadata-protocol: none — provisional; the dev measures it on the diff
Responsibility: a record's Discussion feed shows system rows frozen in the write-time workspace locale (
audit-writers.tsstores the interpolated sentence)Thread-read: none
Premise: holds (read 2026-10-26T17:26Z at
origin/main1eebbc08ba: card open,pm:dispatched, assigneeos-project-manager, this seat's claim the newest; the dev reproduced red first onmain)Serial constraints cleared: PR #26055 (
domain:ui, #25959) edits the ui activity renderer and the locales, so this claim stays off those files while it is open; no other open PR touchesplugin-audit/src/audit-writers.ts; the dev re-measures beforepr_createobjectstack-fleet commentedon Oct 26, 2026
Landed
PR #26258 merged at 2026-10-26T18:52Z by the merge queue as
c49688601donmain(Fixes #26167; ACCEPT6423361244).git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26258-*'origin/mainby contentgit cat-file -e origin/main:packages/plugins/plugin-audit/src/activity-summary-message.tsgit merge-base --is-ancestor c49688601d origin/mainState: this card closed by
Fixes(completed);pm:dispatchedremoved.Generated by Claude Code