Skip to content

The draft→active promotion door cannot state its write's package, so #9612's package-closure narrowing never fires for publishMetaItem #10063

Description

@os-elon

Filed unassigned from #9612 (PR #10058), which implements package-closure narrowing at the runtime publish gate. Recording the half that card's fence could not reach.

Blocked-by: #9612

What is true after #9612

The gate narrows objects to the written item's package closure when the caller can state a packageId. Two write doors reach it:

doorstates a package?narrows?
saveMetaItem (direct active save)yes — request.packageIdyes
publishPackageDrafts (batch package publish)yes — the batch names the packageyes
publishMetaItem (single-item draft→active)nono

The third row is the one worth a card. Studio's designer saves ?mode=draft and POSTs /publish on every edit, so the promotion door is the hot one for the surface the gate exists to protect — and it is the one that keeps receiving the whole tenant.

Why it could not simply be wired

The package binding is genuinely stored: sys_metadata.package_id, and SysMetadataRepository.listDrafts already projects it. But the gate reads the draft through repo.get(ref, { state: 'draft' }), whose rowToItem builds a MetadataItem{ ref, body, hash, parentHash, authoredBy, authoredAt, message, seq }. There is no package id on that shape, so draftForGate.packageId is undefined on every path.

That is the trap worth recording: reading it there would have compiled, typed cleanly, and produced narrowing that never fires while looking exactly like narrowing that does. #10058 deliberately does not do it, and says so in a comment at the call site.

MetadataItem is a packages/spec contract, so widening it — or adding a package-aware read seam beside it — is a decision outside #9612's fence.

Options, for triage rather than as a recommendation

  1. Add the owning package to MetadataItem (spec change; every repository implementation has to answer for it).
  2. Give publishMetaItem an optional packageId in its request, as promoteDraftForPublish already has — pushes the question to the caller, which is where saveMetaItem already answers it.
  3. Resolve it inside promoteDraftForPublish from the draft row it is about to promote, without widening the returned item shape.

Route 2 is the smallest and matches the existing saveMetaItem shape; route 3 costs a read on a path that is already reading. Not a dev's pick — option 1 changes a contract.

Re-check command

grep -n "rowToItem(ref: Pick" packages/metadata-protocol/src/sys-metadata-repository.ts
grep -n "packageId" packages/metadata-protocol/src/protocol.ts | grep -i promotedraftforpublish

Unassigned, observation-adjacent but concrete, awaiting first-touch grading.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions