Skip to content

fix(metadata): publish/discard package drafts in the draft's own org scope (#3115) - #3156

Merged
os-zhuang merged 2 commits into
mainfrom
fix/publish-drafts-org-scope
Jul 18, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
fix/publish-drafts-org-scope

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Problem — #3115

Saving a new object (with fields) in a package via Studio, then clicking Publish, returns no_draft even though the draft was just saved:

{ "success": false, "publishedCount": 0, "failedCount": 1,
  "failed": [{ "type": "object", "name": "…",
    "error": "[no_draft] No pending draft exists for object/… — nothing to publish.",
    "code": "no_draft" }] }

Root cause — a cross-layer org-scope asymmetry

Step Endpoint Org scope
Save Draft REST PUT /meta/:type/:name?mode=draft&package=… (rest-server.ts) never threads activeOrganizationId → draft written env-wide (organization_id = NULL)
Publish POST /packages/:id/publish-drafts (http-dispatcher.ts) resolves session.activeOrganizationId → non-null once a default org is stamped on the session

PR #1852 taught listDrafts to surface env-wide drafts to a non-null active org via $or [{org}, {org IS NULL}] — so the Publish CTA appears — but the publish path was never updated to match: promoteDraft still looks the draft up with a strict organization_id = <org> equality and 404s (no_draft) on the env-wide row it can never match. discardPackageDrafts had the identical latent gap (delete under the request org silently no-ops on env-wide rows).

Same "orphaned draft" bug as #1852, one layer deeper: the pending-changes list was fixed, the publish/discard action wasn't.

Fix

  • SysMetadataRepository.listDrafts() now projects each draft's own organizationId.
  • publishPackageDrafts / discardPackageDrafts promote / delete each listed draft in the scope it actually lives in (env-wide stays env-wide, per-org stays per-org) instead of forcing the caller's active org. Seed-body capture and the ADR-0067 revert-plan pre-state read are scoped the same way.

Package-owned metadata is env-level, so keeping it env-wide on publish is the correct provenance — the bug was purely that publish couldn't reach the env-wide draft.

Tests

  • protocol-publish-drafts-org-scope.test.ts (new) — reproduces the exact [no_draft] … nothing to publish. error against the real listDrafts+promoteDraft interaction (faithful stub engine honouring $or / IS NULL), then asserts publish succeeds env-wide; plus a no-regression case for a genuinely org-scoped draft.
  • sys-metadata-repository-list-drafts.test.ts — asserts the new organizationId projection.
  • Full metadata-protocol (37) + objectql (905) suites green.

Verification

Traced end-to-end: objectui Studio calls metadataClient.save(type, name, body, { mode: 'draft' }) → env-wide PUT /meta?mode=draft; publish via usePublishAllDrafts → POST /packages/:id/publish-drafts. The reported error string is only reachable with an env-wide draft + non-null publish org, which this fix resolves.

🤖 Generated with Claude Code

…scope (#3115)

Studio "Save Draft" goes through REST `PUT /meta/:type/:name?mode=draft`,
which never threads the session's `activeOrganizationId` into `saveMetaItem`
— so the draft row is written env-wide (`organization_id = NULL`). "Publish"
goes through `POST /packages/:id/publish-drafts`, which DOES resolve the
active org (non-null once a default org is stamped on the session).

PR #1852 taught `listDrafts` to surface those env-wide drafts to a non-null
active org via `$or [{org}, {org IS NULL}]` — so the Publish CTA appears —
but the publish path (`promoteDraft`) still looked the draft up with a STRICT
`organization_id = <org>` equality and 404'd (`[no_draft] No pending draft
exists …`) on the env-wide row it could never match. `discardPackageDrafts`
had the identical latent gap (delete under the request org silently no-ops on
env-wide rows).

Fix: `listDrafts` now projects each draft's own `organizationId`, and
`publishPackageDrafts` / `discardPackageDrafts` promote / delete each draft in
THAT scope (env-wide stays env-wide, per-org stays per-org) — so the
pending-changes list and the publish/discard paths agree. Seed-body capture
and the ADR-0067 revert-plan pre-state read are scoped the same way.

Regression tests reproduce the exact `no_draft` error (env-wide draft +
non-null publish org) and assert publish now succeeds env-wide, plus a
no-regression case for a genuinely org-scoped draft.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Canceled Canceled Jul 18, 2026 1:26am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/metadata-protocol, @objectstack/objectql.

13 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol, @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/plugins/index.mdx (via @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql)
  • content/docs/releases/v9.mdx (via @objectstack/objectql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…e fix (#3115)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Jul 18, 2026
@os-zhuang
os-zhuang merged commit b8a21ad into main Jul 18, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the fix/publish-drafts-org-scope branch July 18, 2026 02:22

This branch was successfully deployed

1 active deployment
Preview — b27dcd26 Deployed Jul 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant