refactor(plugin-audit)!: retire export / permission_change from the sys_audit_log action enum (#8147) - #8200
Conversation
…_audit_log action enum (#8147) Retires the two action values with no writer anywhere in the repo, per the maintainer ruling of 2026-08-12 on #7675. Narrows the auth_events and config_changes list-view filters, regenerates the translation bundles, and registers the retirement under ADR-0087 as `audit-log-action-enum-retired`. `import` is deliberately NOT retired: plugin-auth's admin user-import writes a real run-level row with that action, pinned by dogfood case W4. Escalated on the issue for a maintainer ruling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PEVB6w7D7uCszR9Mw1BL73
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
Part of #8147
Part of, notFixes— deliberately. This lands 2 of the 3 values the2026-08-12 ruling named. The third (
import) is referred back to the maintainerbecause its stated premise is measurably false; see "The escalation" below.
Merging this must not close #8147.
What landed
exportandpermission_changeare retired from thesys_audit_log.actionenum. Neither has ever been written. The repo has exactly two
sys_audit_logwriters:packages/plugins/plugin-audit/src/audit-writers.ts(generic hook writer)actionFor()maps afterInsert/afterUpdate/afterDelete tocreate/update/deleteand nothing elsepackages/plugins/plugin-auth/src/admin-import-users.tsaction: 'import', run-level,record_id: nullNeither emits
exportorpermission_change, so the shipped list viewsfiltering on them and the dashboard tiles counting them were permanently empty —
the visible product defect the ruling names (空 widget + 永远查不到东西的过滤器).
export+permission_changeremovedauth_eventslist view filter: narrowed to['login', 'logout']config_changeslist view filter:exportdropped,config_changeandimportkeptplugin-audittranslation bundles regenerated (4 locales, viacheck-i18n-bundles --write— not hand-edited)audit-log-action-enum-retiredon D3 step 17, withspec-changes.jsonand the upgrade guide regeneratedsys-audit-log-retired-actions.test.tsservice-analyticsrouting fixture reseeded offpermission_changeWhy a semantic entry and not a D2 conversion
sys_audit_logis a platform-owned append-only object whose every field isreadonly: true. Nobody authors an audit row and nobody authors this enum, sothere is no source for the chain to rewrite — the same disposition
BatchOptions.validateOnlyand the notification cursor already take in thismajor. As an enum-VALUE retirement nothing lands in
RETIRED_KEYS_BY_MAJORandthe four surface ratchets are byte-identical by construction (no def and no
authorable key changed) — verified, they did not move.
The escalation:
importis NOT retiredThe ruling named
importon the premise 无此 feature. That premise is false:plugin-auth/src/admin-import-users.tswrites a real run-level audit row onevery admin user-import run, and its docblock records the design decision
(corrected rationale, superseding an earlier stale claim) that this row is a
deliberate complement to the per-row rows, not a duplicate.
packages/qa/dogfood/test/admin-identity-audit-trail.dogfood.test.tsasserts that row exists, with
record_id: nullandmetadata.event === 'user.import_run'.Retiring it would make the enum deny a value the platform writes — and do it
silently:
validateRecordskipsreadonlyfields on insert, every fieldhere is readonly, so nothing rejects the write and nothing goes red. The result
would be written-but-not-declared, which for an audit surface is strictly worse
than the declared-but-never-written defect this card exists to fix, because the
row exists and the contract denies it.
config_changesis also the only shippedview that lists those rows.
Ablation (predicted before measured)
Restoring both values to the enum, everything else held:
importcases stay greencheck:i18n(plugin-audit)DRIFTED (4)check:spec-changes/check:upgrade-guide/check:authorable-surfaceservice-analyticsrouting testdelete, never validates against the object schemaZero divergence. The negative arms matter as much as the red one: the ADR-0087
registration is not verified against reality by any gate, and the
service-analytics fixture is not a detector of this retirement. The only
mechanical detectors are the new pin test and the i18n bundle ratchet — which is
precisely why the pin test is in this PR, with its expectations written as
literals so that expectation and reality do not derive from the same source.
Gates
check:nul-bytes,check-adr-0087-registration,check-changeset-no-major,check-empty-changeset,check:migration-registry,check:spec-changes,check:upgrade-guide,check:authorable-surface,check:api-surface,check:liveness,check:i18n,check:adr-anchors,check:merge-driver,check:cross-package-test-inputs,check:test-source-alias,check:docs-audit-scope,check:engine-double-contract,check:query-options-erasure— all PASS.check:type-check-debtPASS from therepo root after a full build closure (no entry above its ceiling; the surplus
notes are pre-existing and in packages this PR does not touch — ledger not
lowered).
plugin-audit207 tests pass,service-analytics1655 pass,plugin-audit+spectypecheck pass.Downstream, deliberately untouched
packages/platform-objectswidget + translations + tile test → audit-log (D): remove thepermission_changewidget fromsystem_overview.dashboard.tsand align the dashboard's audit tiles with the post-retirement action set #8148ACTION_OPTIONS/HistoryTimeline→ objectui#4476PR's finding, both need correcting to two — dropping
importfrom theconsole badge map would leave real, existing rows rendering without a label.
Generated by Claude Code
Generated by Claude Code