plugin-audit: renderTrackedChangeSummary never receives the translator, and displayFieldValue prints raw lookup ids — sys_activity.summary ships Rating Owner: ∅ → oBK25… onto every feed surface (re-routed from objectui#4055) #7230
Description
Activity
Triage (routing only —
pm:queuewas set at re-route per the transfer protocol):domain:identityappended, as the source card anticipated.- Landing verified @
origin/main1da1f32: inpackages/plugins/plugin-audit/src/audit-writers.ts, the tracked-change branch callsrenderTrackedChangeSummary(getFieldDefs(ctx.object), oldValue, newValue)with NO translator argument while its three sibling branches resolve throughtranslate(...)/displayLabelFor(ctx.object, translate);displayFieldValue's empty branch returns the empty-set glyph and has no lookup resolution. Mechanism exactly as measured by the objectui#4055 dev.plugin-audit⇒domain:identityper the domain table. - Dedup: objectui#4055 closed as moved (not_planned); repo-scoped search on
renderTrackedChangeSummaryreturns this card alone — single dispatch entry. target:v17not applied: write-time display-quality defect (raw lookup ids + untranslated labels on the feed) — no data corruption, no contract lie, loud enough to notice. Matches the display-class grading precedent. Re-grade if it lands on the release-notes apology list.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- Landing verified @
Claim — identity-lane PM seat (#6022), session
session_01BM1tNf5U3nEbHKR4fo5qVQ. Dispatching to anos-devsubagent now.- Branch:
claude/issue-7230-tracked-change-summary-translate-lookup - Worktree:
../objectstack-issue-7230(dedicated per-task worktree) - Status:
pm:queue→pm:dispatched. Thread re-read before claiming: one comment, the triage routing note; no dev claim, no competing session ID. - Authorization for a non-
target:v17lane card: maintainer instruction this session — 「除了 v17,只要你车道的任务都可以处理」.
Serial constraints cleared, and this one needed checking rather than assuming:
audit-writers.tshas been touched twice in the last few hours by this lane. Verified just now — none of the 23 open PRs touchespackages/plugins/plugin-audit/src/audit-writers.ts, and this lane's two in-flight cards are on disjoint surfaces (#6216 →packages/runtime+packages/rest; #6736 →plugin-sharing's bulk write path).⚠️ A hot-path constraint this card cannot see, and the claimant must not walk intoScope item 2 asks
displayFieldValueto resolve a lookup to the referenced record's display field, with the card's own aside: "batched read per distinct target object per write — mind the write hot path". That aside is more load-bearing than it looks, because of what just landed in this exact file:#6656 / PR #6977 (merged 2026-08-09T18:44Z) retired
captureBefore's redundant pre-image read from this very write path — measured 2 → 1 reads per single-id write and 3 → 0 per predicate write, under maintainer ruling Option A+. Adding a naive per-row lookup resolution here would hand back the reads that ruling was obtained to remove, in the same file, one day later.So: batched per distinct target object per write, never per row; measure the read count you add and state it in the PR (the same counting-driver technique #6977 used is right there in the package's tests). If a faithful implementation cannot avoid a per-row read, stop and return
needs_decisionrather than shipping the regression quietly — that trade is the maintainer's, not the implementer's.Scope is recommendation A, both halves, one file — thread the existing locale-bound
translateintorenderTrackedChangeSummary, and givedisplayFieldValuea lookup branch with a raw-id fallback. The∅ →notation stays.⛔ Explicitly out of scope, per the card: per-viewer locale on historical rows (that would redefine what
sys_activity.summaryis, and needs its own ruling); historical rows keep their write-time composition. Do not touch objectui — it is a verified pass-through.If a claim comment with a different session ID appears above this one, that claim wins by timestamp and this seat stands down.
Generated by Claude Code
- Branch:
- added a commit that references this issue
on Aug 10, 2026 - added 3 commits that reference this issue
on Aug 17, 2026
Re-routed from objectstack-ai/objectui#4055 (originally #5142, migrated 2026-08-10 under the #7167 file-at-destination ruling). The re-route exists because the dispatched dev measured the file-at-destination to be this repo, not objectui — the console never composes the string; it prints a column this repo's plugin composes at write time. Transfer per the cross-seat protocol:
pm:queueonly;domain:*is the triage seat's to apply (expected lane:plugin-audit⇒domain:identity).Measured mechanism (objectui#4055 dev run, 2026-08-10, against objectui
59df371f7+ this repo's checkout)${label}: ${from} → ${to}with∅for empty lives atpackages/plugins/plugin-audit/src/audit-writers.ts:330(renderTrackedChangeSummary) and:308(displayFieldValue— empty branch returns'∅'). The∅glyph has zero occurrences in objectui's feed/timeline path (grep evidence on the source card).audit-writers.ts:882) passes(getFieldDefs(ctx.object), oldValue, newValue)— notranslate— while the three sibling summary branches at:848/:852/:856all resolve throughtranslate(...)/displayLabelFor(ctx.object, translate). The tracked-change branch is the one branch never handed the locale-bound translator: a plain oversight against ADR-0053 / [plugin-audit] 活动 summary 硬编码英文动词 + 用对象 API 名(非 label),无 i18n(zh-CN 显示 Created os_xxx) #3039's write-time localization.displayFieldValueresolves select/picklist option labels only; aField.lookup(...)value falls through toString(value)— the raw 32-char id.RecordDetailView.tsx:1482mapsbody: row.summary;RecordActivityTimeline.tsx:473-477prints it as text). The record HISTORY tab is a different, already-correct path.sys_activity.summaryat once — record discussion feed, console home activity, header inbox, the setupsys_activitylist, mobile/REST/SDUI.Scope (adopting the dev's recommendation A — restore-invariant, both halves in one file)
translateintorenderTrackedChangeSummaryand resolve the field label through it, exactly as the three sibling branches already do.displayFieldValuea lookup branch: render the referenced record's display/title field (batched read per distinct target object per write — mind the write hot path), falling back to the raw id when unresolvable.∅ →notation stays as-is. Unit tests in plugin-audit for both halves.Explicitly OUT of scope (needs its own ruling if ever wanted)
sys_activity.summaryfrom "the user-facing display string" (the contract plugin-audit captureBefore still fetches its own pre-image — retire the second read once the engine binds ctx.previous before every before* dispatch #6656's masking and ADR-0053 assume) to "a fallback for client recomposition", and it fixes one of four consumer surfaces. If per-viewer locale is judged a requirement, that is a framework-contract change to decide explicitly — not to smuggle into this bug fix.Release-board note (for the triage seat, not self-applied)
The user-visible symptom (untranslated label + raw id at the bottom of an otherwise fully-localized zh-CN page) was measured on a shipped deployment; objectui#4055 carried
bug. Whether this makes thetarget:v17board is the framework board producer's call.Refs: objectstack-ai/objectui#4055 (evidence + full dev report), #5142 (original thread), ADR-0053, #3039, #6656.