Skip to content

fix(rest): /meta/:type/:name/published consults the active overlay before the code snapshot (#8278) - #8298

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-8278-rest-published-overlay
Aug 13, 2026
Merged

fix(rest): /meta/:type/:name/published consults the active overlay before the code snapshot (#8278)#8298
os-zhuang merged 3 commits into
mainfrom
claude/issue-8278-rest-published-overlay

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #8278

Mirrors the dispatcher-side change that landed in PR #8254 for card #8031 (packages/runtime/src/domains/meta.ts) onto the REST transport — the one that actually serves the cloud runtime, and the one PR #8254 did not reach.

The defect

Both arities of the route resolved exclusively through svc.getPublished(type, name) and never consulted the overlay:

  • GET /api/v1/meta/:type/:name/published
  • GET /api/v1/meta/:type/:section/:name/published

Two publish lifecycles write to two different places, and this route knew only the older one. MetadataManager.publishPackage snapshots a body into the row-local publishedDefinition key of its own in-memory registry; publishPackageDrafts / promoteDraft flips the artifact's sys_metadata row state:'draft' -> 'active'. So an item published at runtime answered 404 here while the ordinary read GET /api/v1/meta/:type/:name served it — a false statement about an item that IS published.

Why the overlay is authoritative

Both authority claims were re-read on origin/main rather than taken from the card:

  • ADR-0027 (E)(5), item 5 verbatim: "Seal -> status:'published', freeze manifestJson, compute checksum, assign semver; flip the draft's sys_metadata rows state:'draft' → 'active'." The flip is the publish.
  • ADR-0033 §2: "AI never publishes — every write lands in the ADR-0027 draft ... All agent metadata mutations route through the draft workspace." So promoting that draft is what "published" means for anything authored at runtime.

The change

packages/rest/src/rest-server.ts, the two published arities only. The overlay is read through getMetaItemLayered — the narrow primitive, whose overlay layer is a strict state:'active' lookup that never reads a draft and is reported separately from the code layer. A null overlay is therefore positively "no runtime-published row" and falls through to the untouched getPublished path, so a code-published item resolves to byte-identical bytes. The broader getMetaItem would not do: it folds the code layer into its own answer, so the route could no longer tell the two stores apart.

No new dependency, no new service member, no packages/spec change: this.resolveProtocol() and getMetaItemLayered are both already used elsewhere in this same file.

Two deliberate divergences from the dispatcher twin

  1. No organizationId. packages/rest carries no resolveActiveOrganizationId and no org plumbing at all — the seam package-routes.ts already names at its deletePackage call ("the dispatcher twin owns that seam"). Inventing org plumbing here to close a 404 would be a new seam smuggled in under a bug fix. Omitting it reads the env-wide (organization_id: null) row, symmetric with what an org-less publishPackageDrafts (request.organizationId ?? null) writes — so this door resolves exactly the publishes this door can produce. Environment scoping still holds: it comes from which protocol resolveProtocol returns, not from the request payload (getMetaItemLayered declares no environmentId member).
  2. A store outage stays a 503. getMetaItemLayered documents a 503 SERVICE_UNAVAILABLE for an overlay read that failed for any reason other than the table not being provisioned yet (that benign case returns normally with a null overlay). Blanket-swallowing that throw — a literal mirror of the dispatcher — would let an availability failure fall through and reach the client as 404 Not found, which is exactly the getMetaItem 的 overlay 读用裸 catch 把「sys_metadata 不可达」吞成「该项不存在」—— GET /meta/:type/:name 在存储故障时回一个无 code 的 400「not found」 #5532 defect this package pins in rest-meta-outage-vs-miss.test.ts. A declared status is re-thrown; anything undeclared still falls through, so this cannot make the code-published path newly fail closed.

What is deliberately NOT changed

Tests

New: packages/rest/src/meta-published-overlay.test.ts — 9 arms driving the real ObjectStackProtocolImplementation and real MetadataManager over a stub engine (both already devDependencies; the engine double opens on assertEngineUpdateDispatch / assertEngineDeleteDispatch, so it cannot accept a call ObjectQL would refuse).

§1 runtime-published item served, published body · §2 draft-only item is not served (the load-bearing arm, mirroring PR #8254's) · §3 code-published item byte-identical to getPublished · §4 anti-vacuity: the two fixtures resolve from different stores · §5 neither store still 404s · §6 the compound arity · §7 outage is 503 not 404 · §8 the 501 arm survives · §9 the ordering consequence.

Reverse verification, direction predicted before running: reverting only rest-server.ts to origin/main turns §1, §4, §6, §7, §9 red and leaves §2, §3, §5, §8 green — observed exactly, 5 failed / 4 passed, with §1/§4/§6 expected 404 to be 200, §7 expected 404 to be 503 (the reverted route really does answer 404 on an outage), §9 expected 501 to be 200. Fix restored from its own commit.

Full package suite post-merge with current main: 109 files, 1809 tests passed; pnpm --filter @objectstack/rest typecheck clean.

Gates re-derived against the final diff including the changeset, via node scripts/pm/dispatch-gates.mjs. The five named at dispatch pass (check:authz-resolver, check:cross-package-test-inputs, check:filter-alias-parity, check:meta-type-normalized, check:route-envelope), plus the .changeset-triggered and convention-triggered families the dispatch list did not name: check:changeset-gate-self-tests, check:objectui-changeset, check-changeset-no-major, check:query-options-erasure, check:nul-bytes, check:engine-double-contract — all pass. check:objectui-pin-fresh fails on a pre-existing condition unrelated to this diff (.objectui-sha untouched here; the container's /home/user/objectui clone does not contain the SHA the gate wants).


Generated by Claude Code

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 13, 2026 3:51am

Request Review

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/rest.

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

  • content/docs/ai/connect-mcp.mdx (via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest)
  • content/docs/permissions/authentication.mdx (via @objectstack/rest)
  • content/docs/permissions/system-context.mdx (via packages/rest)
  • content/docs/plugins/index.mdx (via @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/rest)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/rest)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/rest)

3 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/rest)
  • content/docs/releases/v12.mdx (via @objectstack/rest)
  • content/docs/releases/v17.mdx (via @objectstack/rest)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

@os-zhuang
os-zhuang marked this pull request as ready for review August 13, 2026 04:13
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 2efd2c9 Aug 13, 2026
26 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8278-rest-published-overlay branch August 13, 2026 04:23
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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/rest serves /meta/:type/:name/published from the code snapshot too — #8031's fix does not reach the REST transport

2 participants