fix(rest,client)!: reconcile the two REST↔client mismatches the audit ledgered (#3610, #3611) - #3621
Merged
Merged
Conversation
… ledgered (#3610, #3611) #3610: the REST package registrar claimed the bare POST /packages for marketplace publish ({manifest, metadata}) while the dispatcher packages domain gives the same verb+path install semantics — and REST registers first in the production stack (first-match-wins), so every client.packages.install call landed on the publish handler and 400'd. Marketplace publish moves to POST /api/v1/packages/publish (zero direct callers found repo- and objectui-wide; the dispatcher's two-segment POST /packages/:id/publish is a different shape, no clash). The remaining REST GET/GET/DELETE shadows are compatible and stay: the dispatcher already double-writes on install (protocol.installPackage) and fully uninstalls on DELETE (protocol.deletePackage). #3611: meta.getView spoke the ?type= query dialect only the dispatcher /ui domain understands; REST mounts only the path form /ui/view/:object/:type, so the query form 404'd wherever REST serves (e.g. project-scoped bases). The client now sends the path form both surfaces accept, with a URL-pinning test — no test locked this URL before, which is exactly how the split survived. REST route ledger: both mismatch rows resolved (packages publish row is server-only publisher tooling; ui row flips to sdk). Zero mismatches remain. Closes #3610 Closes #3611 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 18 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…h moved to /packages/publish The doc described the pre-#3610 collision state: bare POST /packages as marketplace publish. That path is install (409 duplicate guard, overwrite opt-in); publish now lives at POST /packages/publish. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5
os-zhuang
marked this pull request as ready for review
July 27, 2026 10:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3610, closes #3611 — the two
mismatchrows the #3587 REST route ledger carried (PR #3609). Same reconcile-in-one-PR shape as #3592 did for the dispatcher's four.#3610 —
POST /api/v1/packagespublish-vs-install collision (real bug)Evidence assembled before changing anything:
service-packageis the marketplace plugin inserve.ts's plugin table; itspublish({manifest, metadata})is marketplace-registry publishing — a different concept from install.protocol.installPackage(in-memory registry and durablesys_packages), DELETE routes throughprotocol.deletePackage(full uninstall, Package uninstall does not clean up data-plane permission rows by package_id (ADR-0086 D3 wiring gap) #2747).serve.tsorder + first-match-wins routers), so when thepackageservice resolves,client.packages.install's POST landed on the publish handler →400 Missing required fields: manifest, metadata.Fix: marketplace publish moves to
POST /api/v1/packages/publish; the barePOST /packagesnow has exactly one owner (dispatcher install). The dispatcher's ownPOST /packages/:id/publish(ADR-0033 draft publish) is two segments — different shape, no clash, and package ids are dotted so a barepublishid is not plausible. The remaining RESTGET /packages,GET /packages/:id,DELETE /packages/:idshadows are compatible and deliberately stay (list additionally merges published-but-not-installed packages from the database; scoped bases depend on them).#3611 — UI view dialect split
meta.getViewspoke the?type=query dialect only the dispatcher/uidomain understands; the REST surface mounts only the path form/ui/view/:object/:type, so the query form 404'd wherever REST is the serving surface (e.g. project-scoped bases). The client now sends the path form both surfaces accept, with a URL-pinning test — no test locked this URL before, which is exactly how the split survived.Ledger
rest-route-ledger.ts: the packages row becomes aserver-onlyPOST /packages/publishrow (publisher tooling, not app-SDK surface); the ui row flipsmismatch→sdk(meta.getView). Zero mismatches remain in the REST ledger; the direct-mount conformance guard enforces the route move mechanically (the old bare-POST row would now fail both directions).Verification
@objectstack/rest: 394/394 (conformance guard green on the moved route).@objectstack/client: 130/130 (newgetViewURL-pinning test) + baretsc -pclean.meta.getViewexists (flipped row).fix!, route relocation with zero known callers, launch-window convention) + client patch.🤖 Generated with Claude Code
https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5
Generated by Claude Code