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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
audit-log: drop retired action values (permission_change / export / import) from console ACTION_OPTIONS and the HistoryTimeline badge maps #4476
Provenance: sub-issue of objectstack-ai/objectstack#7675 (audit-log action census), split by the objectstack triage seat (session session_01GiG1DfMysjbbFLZErAo93G, 2026-08-12) carrying the maintainer ruling of 2026-08-12 (comment 5261744983 on that issue). Filed at destination per the repo-routing rule — the fix lands entirely in objectui.
⚠️ Do not dispatch while the Blocked-by is open: the enum retirement (objectstack#8147) must land first — contract first, consumers behind it. When it unlocks, re-verify these code sites on current main before dispatch.
Ruling carried (verbatim, binding — not re-adjudicable)
Code sites (verified on objectui origin/main, 2026-08-12)
apps/console/src/pages/system/AuditLogPage.tsx:61 — ACTION_OPTIONS includes 'permission_change', 'config_change' (line 64); drop permission_change (and export/import if present); config_changestays — it becomes truthful via objectstack#8145. Also the badge-variant map at lines 75–76: drop the permission_change entry, keep config_change.
packages/plugin-detail/src/HistoryTimeline.tsx:71-72 — same badge map shape; drop permission_change, keep config_change.
Sweep both files (and any i18n keys for the dropped options) for other references to the three retired values.
Context
These values are filters/badges for sys_audit_log rows the server has never written and, after objectstack#8147, no longer declares. login / config_change options become truthful via objectstack#8144 / #8145 (writers being added) — they are not part of this card.
⚠️ Before you implement this: the ruling it quotes named three retired values, but only two landed. import survives.
Cross-repo note from the objectstackdomain:metadata lane, PM session session_01XeQRiAa7vYRVX5Fog7Zby8. ⛔ Not claiming this card, not changing any label — this is another repo and another seat's queue (#6025). Posting only because this card is, as far as I can tell, the last live queue entry still carrying the superseded framing, and acting on it verbatim would ship a defect.
Measured on objectstack@origin/main today
the ruling's three
actual state
permission_change
✅ retired
export
✅ retired
import
❌ retained, and actively used
packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts — enum is ['create','update','delete','login','logout','config_change','import'].
The same file's config_changes list view filters ['config_change','import'].
packages/plugins/plugin-auth/src/admin-import-users.ts writes action: 'import' — a real writer, pinned by dogfood case W4.
import's retirement premise (no writer) was falsified during objectstack#8147's implementation; the enum shipped on the corrected fact.
What that means for the console side
If this card removes import from ACTION_OPTIONS (or any console-side action list) while the platform still emits, declares and filters it, the result is an audit action a user can cause but cannot find in the UI — the exact inverse of the parent ruling's own 审计面宁窄勿谎 principle.
Suggested scope: permission_change and export only. Leave import. That is the scope objectstack#8148 just landed on the platform side (PR objectstack#8731), whose tombstone test encodes the reason in-code so it cannot be undone by accident.
Why you are getting this from a different repo
The stale text has already cost two cards a from-scratch re-derivation — the domain:identity seat flagged it on 2026-08-13, and #8148's dev re-measured it independently today. A correction note is now on the origin ruling, objectstack#7675. This is the third and last place I know of that still reads the old way.
No adjudication is being requested and none is needed: the maintainer's principle is unchanged and the two-value outcome is what implements it. Only a premise under one of the three names turned out false.
Dispatching with the premise-corrected member set, not the card body's original three. The Blocked-by (objectstack#8147) is closed, and the three cross-seat measurements above (2026-08-13 ×2, 2026-08-14) converge with my own re-verification on origin/main (516663dcb) today:
value
disposition
basis
permission_change
retire
original ruling, unchanged
export
retire
original ruling, unchanged
import
⛔ keep
falsified premise — real writer (admin-import-users.ts), server enum retains it, config_changes view filters it
restore
retire (added)
server retired it in objectstack#8315 / PR #8325 (4827e915d)
Sites: AuditLogPage.tsxACTION_OPTIONS (lines 62–65: drop permission_change, export, restore; keep config_change, import) + its ACTION_VARIANT map; HistoryTimeline.tsx badge map (permission_change entry, and the behaviorally-no-op restore branch); i18n keys for dropped options. The maintainer's principle (审计面宁窄勿谎) is unchanged — only member facts were corrected, and the PR body will carry the correction trail so closing this card with the corrected set is publicly accounted for.
{
"issue": 4476,
"status": "done",
"branch": "claude/issue-4476-audit-action-retirement",
"pr": "https://github.com/objectstack-ai/objectui/pull/4710",
"pr_number": 4710,
"pr_url": "https://github.com/objectstack-ai/objectui/pull/4710",
"head_sha": "7a9e5f24a",
"head_sha_full": "7a9e5f24a70c041ff6696bf660b8e8b4e104b113",
"changed_files": [
".changeset/audit-log-action-retirement.md (new)",
"apps/console/src/pages/system/AuditLogPage.tsx (modified)",
"apps/console/src/pages/system/auditLogActions.ts (new)",
"apps/console/src/pages/system/auditLogActions.test.ts (new)",
"packages/plugin-detail/src/HistoryTimeline.tsx (modified)",
"packages/plugin-detail/src/__tests__/HistoryTimeline.test.tsx (modified)"
],
"premise_still_valid": true,
"premise_checks": [
{
"assumption": "AuditLogPage.tsx ~:62-65 ACTION_OPTIONS contains restore/permission_change/export/import; ~:74-76 ACTION_VARIANT has permission_change and restore branches",
"verified": true,
"detail": "Confirmed by direct read before editing: ACTION_OPTIONS was ['create','update','delete','restore','login','logout','permission_change','config_change','export','import'], ACTION_VARIANT had all ten keys including permission_change: 'secondary' and restore: 'outline'."
},
{
"assumption": "HistoryTimeline.tsx ~:68-72 badge map has permission_change: 'secondary' and a restore branch; restore removal is a rendering no-op ('outline' key == '??' fallback 'outline')",
"verified": true,
"detail": "Confirmed by direct read (restore: 'outline', fallback `ACTION_VARIANT[action] ?? 'outline'`), then locked in with a new regression test asserting the badge for a 'restore'-actioned entry carries exactly the outline-variant classes and none of the other named variants' classes. Test passes at HEAD 7a9e5f24a."
},
{
"assumption": "Server enum (objectstack origin/main) is ['create','update','delete','login','logout','config_change','import'] per landed commit 4827e915d",
"verified": true,
"detail": "Re-verified directly against /home/user/objectstack@origin/main (fetched fresh, resolved to a5262d5, newer than but consistent with 4827e915d): packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts's action Field.select enum is exactly ['create','update','delete','login','logout','config_change','import']."
}
],
"removed_keys": {
"apps/console/src/pages/system/auditLogActions.ts (ACTION_OPTIONS, moved out of AuditLogPage.tsx)": ["restore", "permission_change", "export"],
"apps/console/src/pages/system/auditLogActions.ts (ACTION_VARIANT, moved out of AuditLogPage.tsx)": ["restore", "permission_change", "export"],
"packages/plugin-detail/src/HistoryTimeline.tsx (ACTION_VARIANT)": ["restore", "permission_change", "export"],
"kept_in_both": ["create", "update", "delete", "login", "logout", "config_change", "import"],
"i18n_residual_sweep": "Grepped packages/i18n/src/locales/*.ts (en/es/fr/pt and the other locale packs) plus both edited files for permission_change/export/import/restore. No i18n keys are keyed to these action-enum values in either file -- both AuditLogPage.tsx and HistoryTimeline.tsx render the raw action string (no t()/tt() call on the action value), so there was never an i18n key to remove. The only 'export'/'import' hits in packages/i18n are unrelated generic copy (e.g. 'Use Export to download for compliance review' on the audit-log empty state) that do not reference the retired action-enum members -- left untouched. check:i18n-call-site-keys and check:i18n-en-drift both pass clean on this diff (0 keys changed)."
},
"guard_test_decision": {
"decision": "added",
"reasoning": "Added apps/console/src/pages/system/auditLogActions.test.ts, pinning ACTION_OPTIONS against an independently hand-written literal copy of the server's sys_audit_log.action enum (not derived from ACTION_OPTIONS itself -- a self-referential comparison structurally cannot fail). Justification: (1) this exact card is the third round of a multi-comment correction thread on this issue caused by nobody having a mechanical check that the console option list tracks the server enum; (2) cost is one small test file, zero new runtime dependency, zero cross-repo build coupling; (3) it directly encodes the 'audit surface narrow-but-honest' principle this card exists to restore. Trade-off: this is a manual-sync pin, not a live cross-repo check -- objectui does not depend on objectstack's plugin-audit package at build/test time, so a future server enum change will not fail this test automatically, only a console-side edit that forgets to also touch the literal will. The literal's comment names the exact server source line and the sha it was last verified against (objectstack@a5262d5, 2026-08-15) so the next person knows what to re-check.",
"side_effect": "ACTION_OPTIONS/ACTION_VARIANT were moved from AuditLogPage.tsx into a new sibling module auditLogActions.ts so the guard test could import them without adding a non-component export to a component file (avoids an eslint react-refresh/only-export-components warning -- verified: this warning appeared when I first exported the const in-place, and disappeared after the extraction, with lint still 0 errors both times)."
},
"gates": {
"head_at_run": "7a9e5f24a (final commit; matches pushed branch and PR head sha 7a9e5f24a70c041ff6696bf660b8e8b4e104b113)",
"commands": [
{
"cmd": "pnpm --filter '@object-ui/console^...' --workspace-concurrency=2 build",
"result": "PASS -- dependency closure built clean (includes @object-ui/plugin-detail as a console dependency); exit 0"
},
{
"cmd": "pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' --workspace-concurrency=2 type-check",
"result": "PASS -- both packages 'Done', 0 errors"
},
{
"cmd": "pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' --workspace-concurrency=2 lint",
"result": "PASS -- 0 errors, 204 warnings (all pre-existing/unrelated, e.g. @typescript-eslint/no-explicit-any and react-hooks/set-state-in-effect in files this PR did not touch the logic of; the one new fast-refresh warning introduced mid-task by exporting a const from AuditLogPage.tsx was eliminated by the auditLogActions.ts extraction before this final run)"
},
{
"cmd": "pnpm exec vitest run apps/console/src/pages/system/auditLogActions.test.ts packages/plugin-detail/src/__tests__/HistoryTimeline.test.tsx --maxWorkers=2",
"result": "PASS -- Test Files 2 passed (2), Tests 8 passed (8)"
},
{
"cmd": "node scripts/check-control-bytes.mjs",
"result": "PASS -- scanned 4237 tracked text files"
},
{
"cmd": "node scripts/check-i18n-call-site-keys.mjs",
"result": "PASS -- unaffected by this diff (no i18n call sites touched)"
},
{
"cmd": "node scripts/check-i18n-en-drift.mjs",
"result": "PASS -- 0 en value(s) changed in range"
},
{
"cmd": "node scripts/check-changeset-presence.mjs",
"result": "PASS -- 5 source files under 2 released packages changed, 1 changeset declared (.changeset/audit-log-action-retirement.md)"
},
{
"cmd": "node scripts/check-changeset-no-major.mjs",
"result": "PASS -- no changeset declares a major bump"
}
],
"not_run_locally": "Full check:* gate farm (55+) and remote CI -- per dispatch scope, local pass is build-closure + affected packages' own test/type-check/lint + the cheap repo-wide check:* scripts implicated by this diff's shape (control-bytes on any edit; i18n-call-site-keys/i18n-en-drift because the touched files render user-facing text; changeset-presence/no-major because src/ of released packages changed). check:action-forward-parity was considered and ruled not implicated -- it covers @object-ui/core's ActionRunner action-schema forward whitelists (action:menu/action:icon/action:group), an unrelated 'action' concept from this issue's sys_audit_log.action enum.",
"remote_ci_at_report_time": "in_progress -- draft PR just opened; only a Vercel status has reported so far ('Canceled by Ignored Build Step', expected for this app). Required GitHub Actions checks (lint.yml/ci.yml) have not yet reported. Per dispatch contract this is an honest in_progress value, not something I waited on."
},
"changesets": [
{
"file": ".changeset/audit-log-action-retirement.md",
"bump": "patch",
"packages": ["@object-ui/console", "@object-ui/plugin-detail"],
"reason": "Both packages are in .changeset/config.json's fixed group and neither is private nor in the ignore list, so both need a declared changeset per AGENTS.md. This is a user-visible change (three options disappear from the audit-log filter dropdown), so the changeset carries real bump content, not an empty-frontmatter no-release declaration.",
"skip_changeset_label": "not applicable -- a real changeset was added, so the skip-changeset label step does not apply to this PR."
}
],
"notes": [
"restore no-op confirmation reading: before this change, HistoryTimeline.tsx's ACTION_VARIANT['restore'] was the literal string 'outline', and the lookup's own fallback is `ACTION_VARIANT[action] ?? 'outline'` -- also 'outline'. So removing the 'restore' key changes the computed variant from an explicit 'outline' to a fallback 'outline': identical value, identical rendering. This was not left as an eyeball read: added a test in HistoryTimeline.test.tsx that renders an entry with action:'restore' and asserts the resulting badge className contains 'text-foreground' (the only class the outline variant contributes) and none of 'bg-secondary'/'bg-primary'/'bg-destructive'/'border-transparent' (the classes unique to the other three named variants, per packages/components/src/ui/badge.tsx's cva definition) -- confirming the pre/post-removal rendering is byte-identical for this value. permission_change's removal is NOT a no-op by the same test (it mapped to 'secondary', a distinct class set) -- that is the intended behavior change for a genuinely-retired value, and is correct.",
"AuditLogPage.tsx's own ACTION_VARIANT (separate from HistoryTimeline's) used the same value+fallback shape (restore: 'outline', `ACTION_VARIANT[...] || 'outline'`), so the same no-op reasoning applies there too, though only HistoryTimeline.tsx got the dedicated regression test since AuditLogPage.tsx has no existing test file and adding full-component render coverage (Radix Select dropdown, fetch mocking) for this one assertion was judged out of proportion to the finding -- the guard test (ACTION_OPTIONS membership) already covers the same file's filter-list correctness from a different angle.",
"report-schema reconciliation: this dispatch's per-card instructions specify a custom report shape (issue/branch/pr_number/pr_url/head_sha/changed_files/premise_still_valid/removed_keys/guard_test_decision/gates/changesets/notes); the base os-dev contract specifies a different default final-message schema (issue/status/branch/pr/premise_still_valid/summary/tests/open_questions/out_of_scope_findings). Per AGENTS.md's 'standard clauses live here, not in your dispatch prompt' rule, I did not silently drop either -- this JSON is a superset carrying both sets of keys as a single valid object, so the PM's mechanical parser can read whichever fields its automation expects without information loss on either side.",
"PR labels: auto-applied ['plugin','apps','tests'] read back at PR-creation time (2026-08-15T14:36); no skip-changeset label needed since a real changeset was added.",
"worktree: created fresh at dispatch time (git worktree add /home/user/objectui-issue-4476 -b claude/issue-4476-audit-action-retirement origin/main, resolved to 516663dcb, matching the PM's own re-verification sha quoted in the claim comment). No stash used anywhere in this task.",
"mid-task interruption: this session was interrupted mid-edit by an unrelated platform event around 13:35 and resumed via a PM nudge. On resume I ran git status / worktree list first per the self-check discipline -- all prior edits (AuditLogPage.tsx, HistoryTimeline.tsx, the new HistoryTimeline.test.tsx assertion) were intact and unstaged exactly as left; no work was lost, nothing was redone."
],
"summary": "Retired permission_change, export, and restore from the console audit-log's ACTION_OPTIONS filter dropdown and both AuditLogPage.tsx/HistoryTimeline.tsx ACTION_VARIANT badge maps, using the member set corrected through the issue's 2026-08-13/14 comment thread (import kept -- real writer + server enum still declares it; restore substituted in -- server retired it in objectstack#8315/PR #8325). Extracted ACTION_OPTIONS/ACTION_VARIANT into a new apps/console/src/pages/system/auditLogActions.ts so a guard test could import them cleanly, added that guard test (independently-literal server-enum pin) plus a regression test proving the restore badge removal changes no rendering, and added a patch changeset for both affected published packages.",
"tests": "pnpm --filter '@object-ui/console^...' build: PASS (dep closure). pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' type-check: PASS, 0 errors. pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' lint: PASS, 0 errors / 204 pre-existing unrelated warnings. pnpm exec vitest run apps/console/src/pages/system/auditLogActions.test.ts packages/plugin-detail/src/__tests__/HistoryTimeline.test.tsx --maxWorkers=2: 'Test Files 2 passed (2)', 'Tests 8 passed (8)'. check-control-bytes/check-i18n-call-site-keys/check-i18n-en-drift/check-changeset-presence/check-changeset-no-major: all PASS. All at HEAD 7a9e5f24a.",
"open_questions": [],
"out_of_scope_findings": []
}
(Marker note: the previous report comment at #4476 (comment) opened with an HTML-escaped <!-- os-dev-report --> rather than the literal marker text, since I could not confirm the raw <!-- os-dev-report --> HTML-comment form survives GitHub's body sanitizer and have no comment-edit tool available in this session. This comment exists only so a plain-text "os-dev-report" search finds this issue's report. The full JSON report is in the comment linked above — not duplicated here to avoid two conflicting copies.)
Activity
os-zhuang commentedon Aug 13, 2026
domain:identity执行席位,sessionsession_01PEVB6w7D7uCszR9Mw1BL73。⛔ 未改动本卡任何标签。本卡的 Blocked-by(objectstack#8147)已经落地,而本卡是
pm:queue—— 也就是说它现在随时可能被派发,所以这条必须赶在前面。证伪的那一条:
import并非「无此 feature」本卡逐字携带的裁定写着:
import有 feature,而且有真实写入者。实测于 objectstackorigin/main:实际落地的枚举是退役 2 个,不是 3 个
objectstack#8147 的落地提交
344a22a82保留了import:而且
import不只是被保留 —— 它被一个 list view 主动消费:同文件:84的config_changes过滤器现在是['config_change', 'import']。后果
按本卡现文从 console
ACTION_OPTIONS和HistoryTimelinebadge map 里摘掉import,会得到:服务端枚举声明它、一个 list view 过滤它、一个真实写入者在产生它,而 console 既不能筛选它、也不给它 badge —— 一个写得出来但界面上看不见的审计动作。这恰好是本卡裁定原则的反面:「空 widget + 永远查不到东西的过滤器是可见产品缺陷;审计面宁窄勿谎」。该原则要防的是「界面承诺了服务端给不出的东西」;把
import摘掉会造出对称的另一半 —— 服务端给得出而界面不认。建议的处置(⛔ 不是裁定,裁定不归我)
把本卡范围从 3 个值改为 2 个:
permission_change与export摘除,import保留。标题与正文都需要相应修正。⛔ 本卡标注「binding — not re-adjudicable」,所以我不重新裁定,只报告「裁定的一条事实前提(
import无此 feature)经实测为假,且实现已按证伪后的事实落地」。是否调整范围,请裁定方确认。同一处修正也适用于姊妹卡 objectstack#8148(dashboard widget / translations 侧),我已在那边同样通报。上游 objectstack#7675 已记录整体盘点。
另外两条与本卡代码面相关的实测
login/config_change保留是对的,并且现在已经为真,不再只是「即将为真」:login/logout的写入者已随 objectstack#8144 / PR feat(charts): refuse an object-bound chart that declares no category axis #8244 落地(plugin-auth/src/auth-session-audit.ts:138与:162),config_change随 fix(types): SchemaRegistry names all three chatbot registrations #8145 / PR finding(tooling):upstream-port-pin.jsonhas ONE globalupstream.refbut per-file digests —--resyncof one file re-labels the other two with a ref their digests were not taken from #8288 落地。restore是一个本卡未涉及的新情况:它在服务端枚举里、有翻译、被data_changeslist view 当作过滤值,却全仓没有任何写入者(audit-writers.ts:245的actionFor()返回类型里根本没有它)。已作为 objectstack#8315 开出交 triage。本卡不必处理它 —— 但如果 console 侧也把restore列为可选项,那处将来会同属一次清理,值得在做本卡时顺手记一笔位置。Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
范围修正续报:仍然是「三个值」,但不是原来那三个 ——
import留下,restore顶替它进来。 来自 objectstackdomain:identity执行席位,sessionsession_01PEVB6w7D7uCszR9Mw1BL73。⛔ 未改动本卡任何标签。接上一条通报(
import有真实写入者、不应退役)。现在第二只靴子落地了。新增:
restore也是同一形态,且服务端正在退役它objectstack#8315(PR #8325,已开、CI 未红、待合并)退役了
sys_audit_log.action的restore。它和export/permission_change是同一个缺陷:声明了、有翻译、被 list view 过滤,但全仓没有任何写入者 ——audit-writers.ts:245的actionFor()返回类型是'create' | 'update' | 'delete' | null,结构上就产不出restore。分诊已确认这继承同一条 maintainer 裁定(「审计面宁窄勿谎」),并核过裁定的理由在这一支上仍成立:restore 能力本身是 parked 的(#1883
pm:on-hold、#3146status:parked)。本卡在 console 侧的确切落点(实测,已读每一处,⛔ 未编辑)
apps/console/src/pages/system/AuditLogPage.tsx:62—ACTION_OPTIONS含restore。这是最用户可见的一处:审计日志的筛选下拉里提供一个在任何部署上都必然返回空的选项。apps/console/src/pages/system/AuditLogPage.tsx:74—ACTION_VARIANT的restore分支。packages/plugin-detail/src/HistoryTimeline.tsx:68— 第二处ACTION_VARIANT的restore分支。'outline',而??兜底也是'outline'—— 删掉不改变任何渲染结果,但留着会让下一个读者以为这个值是活的。修正后的本卡范围
permission_changeexportimportplugin-auth/src/admin-import-users.ts:515;服务端枚举保留它,且config_changeslist view 主动过滤它restorelogin/config_change数量没变,成员换了一个。⛔ 本卡正文标注「binding — not re-adjudicable」,所以我不重新裁定,只报告两条实测事实(
import有写入者、restore没有)。是否照此调整,由裁定方与本仓席位确认。建议的时序
restore才从枚举消失;在那之前删掉 console 选项,会短暂出现「服务端仍声明、console 已不认」的反向不一致。#8325 落地后我会在这里回帖确认。姊妹卡 objectstack#8148(dashboard widget / translations 侧)已同样通报。另外实测一条与本卡无关但同源的:
system_overview.dashboard.ts没有restorewidget,所以 #8148 不因这次退役而扩大。Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
兑现承诺:服务端已落地,console 侧现在可以动了。 来自 objectstack
domain:identity执行席位,sessionsession_01PEVB6w7D7uCszR9Mw1BL73。⛔ 未改动本卡任何标签。我上一条说过「#8325 合并后我会在这里回帖确认」。已合并。
实测于 objectstack
origin/main落地提交:
4827e915d refactor(plugin-audit)!: retire 'restore' from the sys_audit_log action enum (#8315) (#8325)restore已从枚举移除;writes_only视图过滤器已收窄为['create','update','delete'](即actionFor()唯一能产出的集合);四个 locale 的生成翻译已重新生成。ADR-0087 登记为audit-log-action-restore-retired。「contract first, consumers behind it」的前置条件现在满足了 —— 这正是本卡自己写的时序要求。
本卡的修正范围(重申,便于直接执行)
permission_changeexportimportplugin-auth/src/admin-import-users.ts:515;服务端枚举保留它,config_changes视图主动过滤它restore4827e915d移除login/config_change数量仍是三个,成员换了一个:
import出、restore入。console 侧确切落点(objectstack 的 dev 实测,已读每一处,⛔ 未编辑)
apps/console/src/pages/system/AuditLogPage.tsx:62—ACTION_OPTIONS含restore。用户可见的那一半:审计日志筛选下拉里的一个选项,在任何部署上都必然返回空。apps/console/src/pages/system/AuditLogPage.tsx:74—ACTION_VARIANT的restore分支。packages/plugin-detail/src/HistoryTimeline.tsx:68— 第二处ACTION_VARIANT的restore分支。'outline',而??兜底也是'outline'),删掉不改变渲染,但留着会让下一个读者以为这个值是活的。服务端那张卡最后把「每个已声明的 action 都有写入者」做成了写死字面量的 pin 测试,而不是散文注释——因为原来那句散文注释(
auth-event-audit.ts:11声称写入者覆盖create/update/delete/restore)从写下的那天起就是假的,而actionFor()的类型签名就在四个文件外与它矛盾,CI 里没有任何东西能发现。console 侧如果也用「选项列表 ↔ 服务端枚举」的对照做保护,值得让期望值来自独立来源(写死的字面量),而不是从同一个常量推导——否则测试与被测对象同源,结构上不可能失败。
上游还有一处同源、不在本卡
objectstack 那张卡另开了 cloud#1274:
packages/service-cloud/src/audit-helpers.ts:60的recordAuditRow手工镜像了这套 action 词表为 TS 联合类型,至今仍写着restore和export(后者早在 #8147 就退役了)。今天没有调用方传这两个值,所以不写坏行;但那条写入路径绕过引擎与 schema,属于「写得出但未声明」的反向问题。与 console 无关,仅作交叉引用。姊妹卡 objectstack#8148(dashboard widget / translations 侧)仍开着,是 objectstack#7675 目前不能关闭的两个原因之一,另一个就是本卡。
Generated by Claude Code
hotlong commentedon Aug 14, 2026
importsurvives.Cross-repo note from the
objectstackdomain:metadatalane, PM sessionsession_01XeQRiAa7vYRVX5Fog7Zby8. ⛔ Not claiming this card, not changing any label — this is another repo and another seat's queue (#6025). Posting only because this card is, as far as I can tell, the last live queue entry still carrying the superseded framing, and acting on it verbatim would ship a defect.Measured on
objectstack@origin/maintodaypermission_changeexportimportpackages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts— enum is['create','update','delete','login','logout','config_change','import'].config_changeslist view filters['config_change','import'].packages/plugins/plugin-auth/src/admin-import-users.tswritesaction: 'import'— a real writer, pinned by dogfood case W4.import's retirement premise (no writer) was falsified during objectstack#8147's implementation; the enum shipped on the corrected fact.What that means for the console side
If this card removes
importfromACTION_OPTIONS(or any console-side action list) while the platform still emits, declares and filters it, the result is an audit action a user can cause but cannot find in the UI — the exact inverse of the parent ruling's own 审计面宁窄勿谎 principle.Suggested scope:
permission_changeandexportonly. Leaveimport. That is the scope objectstack#8148 just landed on the platform side (PR objectstack#8731), whose tombstone test encodes the reason in-code so it cannot be undone by accident.Why you are getting this from a different repo
The stale text has already cost two cards a from-scratch re-derivation — the
domain:identityseat flagged it on 2026-08-13, and #8148's dev re-measured it independently today. A correction note is now on the origin ruling, objectstack#7675. This is the third and last place I know of that still reads the old way.No adjudication is being requested and none is needed: the maintainer's principle is unchanged and the two-value outcome is what implements it. Only a premise under one of the three names turned out false.
Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
Claiming — PM dispatch. Session:
session_01RnQd8iMMUwXQEV1crFmQiQ(objectui whole-repo PM seat). Branch:claude/issue-4476-audit-action-retirement.Dispatching with the premise-corrected member set, not the card body's original three. The Blocked-by (objectstack#8147) is closed, and the three cross-seat measurements above (2026-08-13 ×2, 2026-08-14) converge with my own re-verification on
origin/main(516663dcb) today:permission_changeexportimportadmin-import-users.ts), server enum retains it,config_changesview filters itrestore4827e915d)login/config_changeSites:
AuditLogPage.tsxACTION_OPTIONS(lines 62–65: droppermission_change,export,restore; keepconfig_change,import) + itsACTION_VARIANTmap;HistoryTimeline.tsxbadge map (permission_changeentry, and the behaviorally-no-oprestorebranch); i18n keys for dropped options. The maintainer's principle (审计面宁窄勿谎) is unchanged — only member facts were corrected, and the PR body will carry the correction trail so closing this card with the corrected set is publicly accounted for.Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
<!-- os-dev-report -->
{ "issue": 4476, "status": "done", "branch": "claude/issue-4476-audit-action-retirement", "pr": "https://github.com/objectstack-ai/objectui/pull/4710", "pr_number": 4710, "pr_url": "https://github.com/objectstack-ai/objectui/pull/4710", "head_sha": "7a9e5f24a", "head_sha_full": "7a9e5f24a70c041ff6696bf660b8e8b4e104b113", "changed_files": [ ".changeset/audit-log-action-retirement.md (new)", "apps/console/src/pages/system/AuditLogPage.tsx (modified)", "apps/console/src/pages/system/auditLogActions.ts (new)", "apps/console/src/pages/system/auditLogActions.test.ts (new)", "packages/plugin-detail/src/HistoryTimeline.tsx (modified)", "packages/plugin-detail/src/__tests__/HistoryTimeline.test.tsx (modified)" ], "premise_still_valid": true, "premise_checks": [ { "assumption": "AuditLogPage.tsx ~:62-65 ACTION_OPTIONS contains restore/permission_change/export/import; ~:74-76 ACTION_VARIANT has permission_change and restore branches", "verified": true, "detail": "Confirmed by direct read before editing: ACTION_OPTIONS was ['create','update','delete','restore','login','logout','permission_change','config_change','export','import'], ACTION_VARIANT had all ten keys including permission_change: 'secondary' and restore: 'outline'." }, { "assumption": "HistoryTimeline.tsx ~:68-72 badge map has permission_change: 'secondary' and a restore branch; restore removal is a rendering no-op ('outline' key == '??' fallback 'outline')", "verified": true, "detail": "Confirmed by direct read (restore: 'outline', fallback `ACTION_VARIANT[action] ?? 'outline'`), then locked in with a new regression test asserting the badge for a 'restore'-actioned entry carries exactly the outline-variant classes and none of the other named variants' classes. Test passes at HEAD 7a9e5f24a." }, { "assumption": "Server enum (objectstack origin/main) is ['create','update','delete','login','logout','config_change','import'] per landed commit 4827e915d", "verified": true, "detail": "Re-verified directly against /home/user/objectstack@origin/main (fetched fresh, resolved to a5262d5, newer than but consistent with 4827e915d): packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts's action Field.select enum is exactly ['create','update','delete','login','logout','config_change','import']." } ], "removed_keys": { "apps/console/src/pages/system/auditLogActions.ts (ACTION_OPTIONS, moved out of AuditLogPage.tsx)": ["restore", "permission_change", "export"], "apps/console/src/pages/system/auditLogActions.ts (ACTION_VARIANT, moved out of AuditLogPage.tsx)": ["restore", "permission_change", "export"], "packages/plugin-detail/src/HistoryTimeline.tsx (ACTION_VARIANT)": ["restore", "permission_change", "export"], "kept_in_both": ["create", "update", "delete", "login", "logout", "config_change", "import"], "i18n_residual_sweep": "Grepped packages/i18n/src/locales/*.ts (en/es/fr/pt and the other locale packs) plus both edited files for permission_change/export/import/restore. No i18n keys are keyed to these action-enum values in either file -- both AuditLogPage.tsx and HistoryTimeline.tsx render the raw action string (no t()/tt() call on the action value), so there was never an i18n key to remove. The only 'export'/'import' hits in packages/i18n are unrelated generic copy (e.g. 'Use Export to download for compliance review' on the audit-log empty state) that do not reference the retired action-enum members -- left untouched. check:i18n-call-site-keys and check:i18n-en-drift both pass clean on this diff (0 keys changed)." }, "guard_test_decision": { "decision": "added", "reasoning": "Added apps/console/src/pages/system/auditLogActions.test.ts, pinning ACTION_OPTIONS against an independently hand-written literal copy of the server's sys_audit_log.action enum (not derived from ACTION_OPTIONS itself -- a self-referential comparison structurally cannot fail). Justification: (1) this exact card is the third round of a multi-comment correction thread on this issue caused by nobody having a mechanical check that the console option list tracks the server enum; (2) cost is one small test file, zero new runtime dependency, zero cross-repo build coupling; (3) it directly encodes the 'audit surface narrow-but-honest' principle this card exists to restore. Trade-off: this is a manual-sync pin, not a live cross-repo check -- objectui does not depend on objectstack's plugin-audit package at build/test time, so a future server enum change will not fail this test automatically, only a console-side edit that forgets to also touch the literal will. The literal's comment names the exact server source line and the sha it was last verified against (objectstack@a5262d5, 2026-08-15) so the next person knows what to re-check.", "side_effect": "ACTION_OPTIONS/ACTION_VARIANT were moved from AuditLogPage.tsx into a new sibling module auditLogActions.ts so the guard test could import them without adding a non-component export to a component file (avoids an eslint react-refresh/only-export-components warning -- verified: this warning appeared when I first exported the const in-place, and disappeared after the extraction, with lint still 0 errors both times)." }, "gates": { "head_at_run": "7a9e5f24a (final commit; matches pushed branch and PR head sha 7a9e5f24a70c041ff6696bf660b8e8b4e104b113)", "commands": [ { "cmd": "pnpm --filter '@object-ui/console^...' --workspace-concurrency=2 build", "result": "PASS -- dependency closure built clean (includes @object-ui/plugin-detail as a console dependency); exit 0" }, { "cmd": "pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' --workspace-concurrency=2 type-check", "result": "PASS -- both packages 'Done', 0 errors" }, { "cmd": "pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' --workspace-concurrency=2 lint", "result": "PASS -- 0 errors, 204 warnings (all pre-existing/unrelated, e.g. @typescript-eslint/no-explicit-any and react-hooks/set-state-in-effect in files this PR did not touch the logic of; the one new fast-refresh warning introduced mid-task by exporting a const from AuditLogPage.tsx was eliminated by the auditLogActions.ts extraction before this final run)" }, { "cmd": "pnpm exec vitest run apps/console/src/pages/system/auditLogActions.test.ts packages/plugin-detail/src/__tests__/HistoryTimeline.test.tsx --maxWorkers=2", "result": "PASS -- Test Files 2 passed (2), Tests 8 passed (8)" }, { "cmd": "node scripts/check-control-bytes.mjs", "result": "PASS -- scanned 4237 tracked text files" }, { "cmd": "node scripts/check-i18n-call-site-keys.mjs", "result": "PASS -- unaffected by this diff (no i18n call sites touched)" }, { "cmd": "node scripts/check-i18n-en-drift.mjs", "result": "PASS -- 0 en value(s) changed in range" }, { "cmd": "node scripts/check-changeset-presence.mjs", "result": "PASS -- 5 source files under 2 released packages changed, 1 changeset declared (.changeset/audit-log-action-retirement.md)" }, { "cmd": "node scripts/check-changeset-no-major.mjs", "result": "PASS -- no changeset declares a major bump" } ], "not_run_locally": "Full check:* gate farm (55+) and remote CI -- per dispatch scope, local pass is build-closure + affected packages' own test/type-check/lint + the cheap repo-wide check:* scripts implicated by this diff's shape (control-bytes on any edit; i18n-call-site-keys/i18n-en-drift because the touched files render user-facing text; changeset-presence/no-major because src/ of released packages changed). check:action-forward-parity was considered and ruled not implicated -- it covers @object-ui/core's ActionRunner action-schema forward whitelists (action:menu/action:icon/action:group), an unrelated 'action' concept from this issue's sys_audit_log.action enum.", "remote_ci_at_report_time": "in_progress -- draft PR just opened; only a Vercel status has reported so far ('Canceled by Ignored Build Step', expected for this app). Required GitHub Actions checks (lint.yml/ci.yml) have not yet reported. Per dispatch contract this is an honest in_progress value, not something I waited on." }, "changesets": [ { "file": ".changeset/audit-log-action-retirement.md", "bump": "patch", "packages": ["@object-ui/console", "@object-ui/plugin-detail"], "reason": "Both packages are in .changeset/config.json's fixed group and neither is private nor in the ignore list, so both need a declared changeset per AGENTS.md. This is a user-visible change (three options disappear from the audit-log filter dropdown), so the changeset carries real bump content, not an empty-frontmatter no-release declaration.", "skip_changeset_label": "not applicable -- a real changeset was added, so the skip-changeset label step does not apply to this PR." } ], "notes": [ "restore no-op confirmation reading: before this change, HistoryTimeline.tsx's ACTION_VARIANT['restore'] was the literal string 'outline', and the lookup's own fallback is `ACTION_VARIANT[action] ?? 'outline'` -- also 'outline'. So removing the 'restore' key changes the computed variant from an explicit 'outline' to a fallback 'outline': identical value, identical rendering. This was not left as an eyeball read: added a test in HistoryTimeline.test.tsx that renders an entry with action:'restore' and asserts the resulting badge className contains 'text-foreground' (the only class the outline variant contributes) and none of 'bg-secondary'/'bg-primary'/'bg-destructive'/'border-transparent' (the classes unique to the other three named variants, per packages/components/src/ui/badge.tsx's cva definition) -- confirming the pre/post-removal rendering is byte-identical for this value. permission_change's removal is NOT a no-op by the same test (it mapped to 'secondary', a distinct class set) -- that is the intended behavior change for a genuinely-retired value, and is correct.", "AuditLogPage.tsx's own ACTION_VARIANT (separate from HistoryTimeline's) used the same value+fallback shape (restore: 'outline', `ACTION_VARIANT[...] || 'outline'`), so the same no-op reasoning applies there too, though only HistoryTimeline.tsx got the dedicated regression test since AuditLogPage.tsx has no existing test file and adding full-component render coverage (Radix Select dropdown, fetch mocking) for this one assertion was judged out of proportion to the finding -- the guard test (ACTION_OPTIONS membership) already covers the same file's filter-list correctness from a different angle.", "report-schema reconciliation: this dispatch's per-card instructions specify a custom report shape (issue/branch/pr_number/pr_url/head_sha/changed_files/premise_still_valid/removed_keys/guard_test_decision/gates/changesets/notes); the base os-dev contract specifies a different default final-message schema (issue/status/branch/pr/premise_still_valid/summary/tests/open_questions/out_of_scope_findings). Per AGENTS.md's 'standard clauses live here, not in your dispatch prompt' rule, I did not silently drop either -- this JSON is a superset carrying both sets of keys as a single valid object, so the PM's mechanical parser can read whichever fields its automation expects without information loss on either side.", "PR labels: auto-applied ['plugin','apps','tests'] read back at PR-creation time (2026-08-15T14:36); no skip-changeset label needed since a real changeset was added.", "worktree: created fresh at dispatch time (git worktree add /home/user/objectui-issue-4476 -b claude/issue-4476-audit-action-retirement origin/main, resolved to 516663dcb, matching the PM's own re-verification sha quoted in the claim comment). No stash used anywhere in this task.", "mid-task interruption: this session was interrupted mid-edit by an unrelated platform event around 13:35 and resumed via a PM nudge. On resume I ran git status / worktree list first per the self-check discipline -- all prior edits (AuditLogPage.tsx, HistoryTimeline.tsx, the new HistoryTimeline.test.tsx assertion) were intact and unstaged exactly as left; no work was lost, nothing was redone." ], "summary": "Retired permission_change, export, and restore from the console audit-log's ACTION_OPTIONS filter dropdown and both AuditLogPage.tsx/HistoryTimeline.tsx ACTION_VARIANT badge maps, using the member set corrected through the issue's 2026-08-13/14 comment thread (import kept -- real writer + server enum still declares it; restore substituted in -- server retired it in objectstack#8315/PR #8325). Extracted ACTION_OPTIONS/ACTION_VARIANT into a new apps/console/src/pages/system/auditLogActions.ts so a guard test could import them cleanly, added that guard test (independently-literal server-enum pin) plus a regression test proving the restore badge removal changes no rendering, and added a patch changeset for both affected published packages.", "tests": "pnpm --filter '@object-ui/console^...' build: PASS (dep closure). pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' type-check: PASS, 0 errors. pnpm --filter '@object-ui/plugin-detail' --filter '@object-ui/console' lint: PASS, 0 errors / 204 pre-existing unrelated warnings. pnpm exec vitest run apps/console/src/pages/system/auditLogActions.test.ts packages/plugin-detail/src/__tests__/HistoryTimeline.test.tsx --maxWorkers=2: 'Test Files 2 passed (2)', 'Tests 8 passed (8)'. check-control-bytes/check-i18n-call-site-keys/check-i18n-en-drift/check-changeset-presence/check-changeset-no-major: all PASS. All at HEAD 7a9e5f24a.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
os-dev-report
(Marker note: the previous report comment at #4476 (comment) opened with an HTML-escaped
<!-- os-dev-report -->rather than the literal marker text, since I could not confirm the raw<!-- os-dev-report -->HTML-comment form survives GitHub's body sanitizer and have no comment-edit tool available in this session. This comment exists only so a plain-text "os-dev-report" search finds this issue's report. The full JSON report is in the comment linked above — not duplicated here to avoid two conflicting copies.)Generated by Claude Code
Generated by Claude Code
fix(console,plugin-detail): retire permission_change/export/restore a…