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
publishPackageDrafts (behind Studio's "publish whole app", POST /packages/:id/publish-drafts) promotes every draft in a package and writes no sys_metadata_audit rows at all — neither the allowed-outcome publish rows nor the denied row for a refusal.
Found while implementing #7748, which fixed the same gap on the single-item routes (publishMetaItem, rollbackMetaItem, and all four 409 METADATA_CONFLICT sites). Not fixed there, deliberately — see below.
publishPackageDrafts calls promoteDraftForPublish directly (not publishMetaItem), and it does so inside one engine.transaction() — the ADR-0067 D2 "a commit cannot half-land" invariant. That makes the batch case a genuinely different contract from the three sites #7748 touched:
So the correct placement for the batch path is Phase 2 (after the transaction commits), driven off the promoted[] array — a different edit from #7748's, with its own test, rather than something to smuggle into that card.
Repro sketch
Stage two or more drafts in one package.
POST /packages/:id/publish-drafts — answers 200, drafts go active.
GET /api/v1/meta/:type/:name/audit for any published item — no publish row.
(Compare: after #7748, the same item published one-at-a-time does get its row.)
Notes for whoever takes this
The sys_metadata_audit schema already declares publish as an operation option — no schema work needed.
Best-effort semantics (ADR-0010 §3.6) mean a failed audit write is swallowed, so "row missing" and "write failed" are indistinguishable to a caller unless the test observes the attempt separately.
What
publishPackageDrafts(behind Studio's "publish whole app",POST /packages/:id/publish-drafts) promotes every draft in a package and writes nosys_metadata_auditrows at all — neither the allowed-outcomepublishrows nor thedeniedrow for a refusal.Found while implementing #7748, which fixed the same gap on the single-item routes (
publishMetaItem,rollbackMetaItem, and all four 409METADATA_CONFLICTsites). Not fixed there, deliberately — see below.Why it was left out of #7748
publishPackageDraftscallspromoteDraftForPublishdirectly (notpublishMetaItem), and it does so inside oneengine.transaction()— the ADR-0067 D2 "a commit cannot half-land" invariant. That makes the batch case a genuinely different contract from the three sites #7748 touched:save,delete) write after their repository transaction has closed. draft-publish-lifecycle: the metadata audit trail records onlysave— publish, rollback and the 409 conflict denial never write a row #7748's newpublish/rollbackrows were placed to match that.save— publish, rollback and the 409 conflict denial never write a row #7748 exists to close.So the correct placement for the batch path is Phase 2 (after the transaction commits), driven off the
promoted[]array — a different edit from #7748's, with its own test, rather than something to smuggle into that card.Repro sketch
POST /packages/:id/publish-drafts— answers 200, drafts go active.GET /api/v1/meta/:type/:name/auditfor any published item — nopublishrow.(Compare: after #7748, the same item published one-at-a-time does get its row.)
Notes for whoever takes this
sys_metadata_auditschema already declarespublishas anoperationoption — no schema work needed.save— publish, rollback and the 409 conflict denial never write a row #7748 documented: most multi-table fake engines in this repo openinsertwithif (table === 'sys_metadata_audit') return { id: 'audit_skip' };, which makes every audit assertion pass for the wrong reason.packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.tshas a harness that genuinely persists audit rows and separates "attempted" from "landed" — reuse its shape.Backlink: #7748 (single-item routes, fixed).