Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/package-publish-audit-rows.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
---
'@objectstack/metadata-protocol': patch
'@objectstack/metadata-core': patch
---

fix(metadata-protocol): `publishPackageDrafts` now writes the audit rows a batch publish always owed

Studio's "publish whole app" (`POST /packages/:id/publish-drafts`) promoted every
draft in a package and wrote **no `sys_metadata_audit` rows at all** — neither the
allowed-outcome `publish` rows nor a `denied` row for a refusal. The route calls
`promoteDraftForPublish` directly rather than `publishMetaItem`, so the row added
for the single-item routes never ran for it: a batch that published twenty
artifacts left the compliance trail exactly as empty as a batch nobody ran.

Both outcomes are now recorded, and **where** they are recorded is the fix:

- **allowed** — one `publish` / `allowed` row per promoted item, written in Phase 2
off `promoted[]`, with `source: 'protocol.publishPackageDrafts'` so the trail
distinguishes "publish whole app" from a single-item publish. The row is keyed on
the scope the draft was promoted in, not the request's active org, because
env-wide drafts are promoted env-wide.
- **denied** — one `publish` / `denied` row with `code: 'batch_aborted'` when the
batch rolls back, written from the rollback handler, **outside** the
`engine.transaction()`. Written inside it, the refusal's own row would roll back
with the batch it records — leaving nothing behind about a refused publish, which
is the defect the single-item audit rows exist to close.

The causal reason rides in `note`, which is served by `GET /api/v1/meta/:type/:name/audit`
and therefore carries the client-facing text rather than raw driver output.
Original file line numberDiff line numberDiff line change
Expand Up@@ -123,9 +123,17 @@ export const SysMetadataAuditObject = ObjectSchema.create({
* - on `allowed`: `'ok'`
* - on `denied`: `'not_overridable'` | `'not_creatable'` |
* `'item_locked'` | `'invalid_metadata'` | `'destructive_change'` |
* `'metadata_conflict'`
* `'metadata_conflict'` | `'batch_aborted'`
* - on `forced`: `'lock_override'` (Phase 3)
*
* `batch_aborted` (#8400) is the batch publish's own refusal value:
* `publishPackageDrafts` promotes a whole package inside ONE transaction,
* so its refusal is "the batch rolled back and nothing landed" rather than
* any one item's verdict, and the causal code rides in `note`. It is ONE
* fixed value on purpose — lower-casing whatever `error.code` happened to
* abort the batch would turn this closed set into an open one that grows
* silently with the error catalog.
*
* Deliberately lowercase, and deliberately NOT the `error.code` vocabulary
* (ADR-0112) even though the denial values are spelled the same as the
* codes the protocol throws. This column is persisted audit history: rows
Expand Down
Loading
Loading