Skip to content

sys_audit_log.action declares restore with no writer anywhere — actionFor() structurally cannot return it, while a comment and a shipped list view both assert it is covered #8315

Description

@os-zhuang

Found by the domain:identity execution seat while measuring whether #7675 ("4 of 10 declared sys_audit_log actions have no writer anywhere") can close. Filed unassigned and unlabeled for triage — I am an execution seat and do not grade findings or route domain:*.

Same defect class as #7675, on a value #7675's own survey did not name. Measured on origin/main, not inferred.

What

packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts:125 declares:

action: Field.select(
  ['create', 'update', 'delete', 'restore', 'login', 'logout', 'config_change', 'import'],

Nothing writes restore. The record-level audit writer structurally cannot produce it — packages/plugins/plugin-audit/src/audit-writers.ts:245:

function actionFor(event: string): 'create' | 'update' | 'delete' | null {

restore is not in the return type, and audit-writers.ts:1105-1106 early-returns on null. A tree-wide grep for any form of action: 'restore' across packages/**/*.ts excluding tests returns no producer — the only restore hits are unrelated (metadata-protocol uses a different 'restored' literal on its own revert report; sys-attachment.object.ts:138 explicitly declares false rather than "claim a restore capability the runtime does not provide").

⚠️ Two declarations assert the opposite, which is what makes this more than tidiness

1. A comment states the coverage that does not existpackages/plugins/plugin-audit/src/auth-event-audit.ts:11:

create/update/delete/restore and nothing else — four of the declared…

It names restore as one of the four covered actions. actionFor()'s own signature contradicts it. This is the pattern #8011 was about: a declaration near a mechanism, with nothing enforcing it. A declaration-reading audit stops at this comment and scores restore as covered.

2. A shipped list view offers it as a filter valuesys-audit-log.object.ts:55:

filter: [{ field: 'action', operator: 'in', value: ['create', 'update', 'delete', 'restore'] }],

The data_changes view advertises a value that can never match. Harmless to the query, but it is the same "empty widget / permanently-unmatchable filter" shape the maintainer ruled on for #7675: 空 widget + 永远查不到东西的过滤器是可见产品缺陷;审计面宁窄勿谎. Four generated translation files also carry the option label (plugin-audit/src/translations/*.objects.generated.ts:27).

Status of #7675's named values, for context

Every value #7675 actually named is now resolved, which is why this one surfaced:

action state
login ✅ writer at auth-session-audit.ts:138 (#8144 → PR #8244)
logout ✅ writer at auth-session-audit.ts:162 (#8144 → PR #8244)
config_change ✅ writer landed (#8145 → PR #8288)
export ✅ retired from the enum (#8147 → PR #8200)
permission_change ✅ retired from the enum (#8147 → PR #8200)
import real writer at plugin-auth/src/admin-import-users.ts:515
restore no writer — this card

Not adjudicated here

⛔ I have deliberately not decided between the two obvious shapes (give restore a writer, or retire it from the enum/view/translations per the 宁窄勿谎 principle). That turns on whether a restore/undelete capability is intended to exist — #1883 (pm:on-hold, M2 permission lifecycle undelete/purge) suggests the capability itself is unbuilt, which would make retirement the consistent answer, but that is a product call and not an execution seat's to make. Recorded plainly per the file-plainly rule.

Whichever way it goes, the false comment at auth-event-audit.ts:11 needs correcting either way — it is wrong today regardless of the disposition.

Related: #7675 (parent survey), #8147 (the retirement that this measurement re-checks), #1883, #8011 (same "unenforced declaration" class), #8203 (a select option enum is unenforceable once the field is readonly — the mechanism that lets this persist undetected).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions