Skip to content

draft-publish-lifecycle: the metadata audit trail records only save — publish, rollback and the 409 conflict denial never write a row #7748

Description

@huangyiirene

Symptom

Staging, publishing and rolling back a metadata item all answer 200, and the audit trail records only one of the three.

  • After 3 publishes and 2 rollbacks (all 200, all sent with X-Actor), GET /api/v1/meta/<type>/<name>/audit returns Counter({'save': 5}) — no publish rows, no rollback rows.
  • The 409 METADATA_CONFLICT denial is likewise absent.

Reproduced twice, on two actors. Everything else in the lifecycle passes: draft staging, the publish flip, OCC (409 with a passing control), package-wide publish-drafts atomicity, the #4463 draft→active gate, the Studio unpublished badge and Changes counter, /history carrying both publish events, and rollback flipping the served body. So the operations happen — they are simply not written down.

Root cause (source-confirmed)

packages/metadata-protocol/src/protocol.ts writes an allowed-outcome audit row at exactly two sites:

  • line 10220 — operation: 'save', outcome: 'allowed' (in the save path, commented "ADR-0010 — success audit (best-effort)")
  • line 12848 — operation: 'delete', outcome: 'allowed'

publishMetaItem and rollbackMetaItem reach recordMetadataAuditonly through assertLockAllowsWrite() (called at lines 10783 and 12414). That helper records on the deny path and returns early on allow:

const refusal = evaluateLockForWrite(state.lock);
if (!refusal) return null; // ← allowed: returns before any audit write
…
await this.recordMetadataAudit({ …, outcome: 'denied', … });

So a refused publish is audited and a successful one is not — the inverse of what an audit trail is for. The same asymmetry explains the missing 409: the conflict is refused outside the lock helper, and nothing on that route writes a row either.

Stale-premise check: re-verified on objectstackorigin/main (00e9196). Both allowed-outcome sites and both assertLockAllowsWrite call sites are unchanged in substance; only line numbers moved from the run's build (save 9833 → 10220, delete 12429 → 12848).

Reproduction

  1. Boot the showcase with writable runtime packages.
  2. Stage a draft (PUT …?mode=draft), publish it, roll it back — repeat to 3 publishes and 2 rollbacks, sending X-Actor on every call. All answer 200.
  3. GET /api/v1/meta/<type>/<name>/audit → only save rows (Counter({'save': 5})).
  4. Force a 409 METADATA_CONFLICT (stale If-Match), then re-read the audit — the denial is absent too.

Scope

The actor stamped on the rows that are written is a second, independent defect with a different located file — filed separately as #7749 so it survives this fix.

Source

Extracted from the QA run #7695 (framework 92f26f7, console 09987b680).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions