fix(rest): the dispatcher's /packages domain is the one implementation of the package read and delete routes (#14503) - #16628
Conversation
…ackageRoutes (#14503) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…t; pins follow the one route Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…ckage-routes-single-implementation
…lot-lookups pin follows Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
… baseline after the ledger row deletion Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…ckage-routes-single-implementation # Conflicts: # content/docs/permissions/system-context.mdx
… 54 names the write-only gate - changeset: the scoped mount is served by the dispatcher only where the @objectstack/hono catch-all is mounted; the uninstall's tenancy width narrows from allTenants to the caller's organization; GET /packages no longer merges durable sys_packages rows — each stated as deliberately removed, never silently dropped - content/docs/permissions/system-context.mdx row 54: the REST package gate guards POST /packages/publish (manage_metadata) only; the read cohort is row 55's Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
The REST registrar mounts POST /packages/publish only (#14503); the read cohort is the dispatcher domain's to enforce and is named in plain words, since the census gate reads a path#symbol in row prose as an anchor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
📓 Docs Drift CheckThis PR changes 2 package(s): 34 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 29 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c833a3ea5f1ca62e306498786e11764e0d8f8ba5 && git checkout c833a3ea5f1ca62e306498786e11764e0d8f8ba5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bc0ac1d1f5775659e72ad35452c463443a71f1be ba6b8311ed1957b1a3d398a27095b82ed4615d39 && git checkout -B drift-repro bc0ac1d1f5775659e72ad35452c463443a71f1be && git merge --no-ff ba6b8311ed1957b1a3d398a27095b82ed4615d39
node scripts/docs-audit/affected-docs.mjs --json bc0ac1d1f5775659e72ad35452c463443a71f1be
|
- kernel/contracts/metadata-service.mdx: the /api/v1/packages family is the dispatcher domain's; the REST registrar contributes POST /packages/publish (the table had it at the bare POST /packages, which is the install route); the list is the installed set, not registry + database; the read answers the bare row and the dispatcher's 404 wording; the uninstall is organization-scoped - permissions/permission-sets.mdx: the uninstall's revocation report sits under data.persisted.cleanups on the surviving envelope Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
… 91/91 after the three package rows left (#14503) population 94 -> 91 and reachable 94 -> 91, derived from the tree with deriveProbeFileCensus(), not back-solved: each removed row carried family: 'packages', so the family key reached it; the blind spot stays 0 and the 19 families are unchanged (the publish row keeps the family). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…es the tracker id to a comment check:doc-authoring forbids an issue id inside a runtime string literal; the adjacent comment carries it for the reader who can resolve it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
Fixes #14503
Summary
GET /api/v1/packages,GET /api/v1/packages/:idandDELETE /api/v1/packages/:idnow have one implementation: the runtime dispatcher's/packagesdomain (packages/runtime/src/domains/packages.ts).@objectstack/rest'sregisterPackageRoutesno longer mounts its own copies of those three routes; it mountsPOST /api/v1/packages/publishand nothing else. This executes the maintainer's ruling on #14503 (2026-09-02, verbatim 「同意」, option A), in the ruled order: measure → verify compositions → delete with the survivor pinned.The SHADOWS sentence the card is about, the opposite "mounts FIRST in the production stack" claim in the same file, the
#7563docblock's "independent of composition order" claim, and the two module headers calling the?version=refusal "dormant behind a Hono collapse" are all taken with the deletion.Step 1 — the census, re-taken on today's tree (
ce8caba914, the branch base)The prior census (comment
5544498087) was measured before PR #15859; every verdict below is a fresh reading with its control on the same corpus.objectui@ pina472b07)content/docssource: 'registry' | 'database' | 'both'readerssource: 'database'on metadata-loader items, 5 in the producer itself, 2 SDK comments) — control.writable= 17.writable= 26?version=senders to a packages doorScopedEnvironmentClient.packages.get(id, version?)(packages/client/src/index.ts) — control?overwrite== 2?keepData== 1 (the Console's DELETE sends the dispatcher's parameter)The SDK sender is already carded: #12034 (open,
pm:awaiting-maintainer) holds the maintainer's 2026-08-26 ruling thatpackages.getbinds the bare row; its remaining half is exactly the scoped binding this PR now makes bindable. Nothing is ported into the dispatcher: a version-scoped durable lookup was a behaviour only the REST copy had, and porting it would change the dispatcher's data source — materially bigger than what was ruled.Step 2 — compositions, and the fork
Every in-tree composition that mounts the REST package registrar also mounts the dispatcher:
packages/cli/src/commands/serve.ts(createRestApiPlugin:4019,createDispatcherPlugin:4039),packages/verify/src/harness.ts(:594/:595),packages/plugins/plugin-dev/src/dev-plugin.ts(:856/:873). None turnsenableProjectScopingon (mergeBootConfigforces it false on the standalone boot).The deciding reading — driven through the real
@objectstack/honocatch-all on the built runtime dist, atce8caba914(kernel: session-backed identity, ObjectQL engine with a realSchemaRegistryholdingcom.acme.crmand the permission-set tables the shared authz resolver reads):⇒ A scoped
/packagesURL reaches the dispatcher's/packagesdomain through the catch-all (the dispatcher's spelling, single quotes, no trailing period). The fork the prior census reported has dissolved on today's tree; the three REST routes can be removed without any composition losing/packages.Boundary — corrected by the at-tier review (fork report
5571983259), and the reason this PR is held in draft. The composition that loses scoped/packagesunder the deletion is reachable from the open-core CLI, not only from the cloud repo:serve.tsskips the standalone boot whenshouldBootWithLibrary()is false (plugin-detection.ts— any host config, orOS_MODE=off) and then forwardsapi.enableProjectScopingverbatim to the REST plugin and the dispatcher plugin onHonoServerPlugin. Withplugin-hono-serverbut withoutcreateHonoApp, the dispatcher's explicit mounts register/packages*at the unscoped prefix only and the transport's sole route into the dispatcher (setFallbackHandler) serves declarativeapis:endpoints, so on that composition the REST mirror was the only door for scoped package reads and deletes — and after this PR there is none. My earlier boundary statement attributed that region to "a repo not in this session"; that attribution was wrong and is withdrawn here. Every consumer population reachable from this repo is zero for the scoped mount (measured with controls in the fork report); the residue is now stated in the changeset. The ruling's step 2 makes this the maintainer's call (options A′ / B′ / C′ in the fork report) — ⛔ no scopedmountPackagesRoutevariant is added here.Step 3 — the deletion, and what pins the survivor
packages/rest/src/package-routes.ts: the three service-gated routes, their mount gate, theprotocoloption (only they read it), the read cohort of the gate, the registry projection helper and the compile-time pins for theprotocolseam are removed; the docblocks say what happened and why.direct-mount-composition.tsandrest-api-plugin.tsstop threadingprotocol.packages/rest/src/rest-route-ledger.ts: the three direct-mount rows are gone; the runtime route ledger carries the family.packages/rest/src/rest-server.ts:/discovery'sroutes.packagesis now derived from the publish mount (POST {base}/packages/publish→{base}/packages) instead of from the registrar's ownGET {base}/packagescopy. Measured consequence worth knowing: on a stockobjectstack serveboot that copy was never mounted (thepackageservice registers after the REST plugin starts), so REST's discovery omittedroutes.packageswhile the dispatcher served the family; it is advertised on every boot now.packages/runtime/src/domains/packages-single-door.test.ts(new) pins the surviving door — 404 wordingPackage 'ID' not foundwithRESOURCE_NOT_FOUND, the retired spelling asserted absent, the bare-row envelope{ data, meta, success }with nopackagewrapper and nosource, the list rows withoutsource— unscoped and environment-scoped, driven throughdispatch()with the catch-all-derived subpath (the fix(runtime): the dispatcher's scope strip matches/environments/, the prefix its own hint parser reads #15859 pattern; the hono package's suite aliases runtime to a mock, so the real-adapter drive above is the measurement and this file is the durable pin).packages/runtime/src/domains/packages.ts: the stale "which shadows it only when apackageservice is registered" sentence and the two "sibling REST door" references are corrected; behaviour untouched (main's fix(runtime): key package lifecycle state by project, not by environment alone #16572 merged cleanly beside it).packages/rest/src/query-multiplicity.tsheader: the "dormant" claim replaced with the measured fact (plugin-hono-server'sreadQuerykeeps arrays since 两个IHttpServer适配器对「重复的查询参数」给出不同形状:Hono 折叠成第一个值,node:http 给数组 #6878 / PR test(http-conformance): 「重复查询参数」的跨适配器分歧落锁为已知事实 (#6878 路线 1) #6941, pinned by the http-conformance suite).POST /packages/publish— same resolver, same gate, same three wire answers).scripts/check-undeclared-dep-imports.mjs: the type-only ledger row forpackage-routes.ts→@objectstack/metadata-protocolis deleted (the import went with the delete route; the gate reds on a stale row).content/docs/permissions/system-context.mdxandscripts/doc-authoring-prose-id.baseline.json: line-rot re-anchoring and the shrink-only baseline regeneration, both by the gates' own--fix/--census-ledgerremedies.Changeset
@objectstack/restminor (.changeset/rest-package-routes-single-implementation.md), per the ruling. It states FROM → TO forGET /packages/:id(data.package→data), records thesourcestamp and the?version=read + multiplicity refusal as deliberately removed, the 404 wording change, the DELETE semantics, and the discovery change.Verification
Every build/test ran through
scripts/pm/os-verify-lock.sh; VERDICT lines read, never a bare$?. Heads are named per reading because the branch grew across the run (final head60f7d46b49; the two commits after7647624a00touch only a docs page and a baseline JSON).pnpm --workspace-concurrency=2 --filter '@objectstack/hono^...' build, treece8caba914):build-exit=0, 97 "Build success" lines.@objectstack/rest, full suite at7647624a00(after mergingorigin/mainf2f6684cd5):Test Files 179 passed (179) · Tests 3008 passed (3008), exit 0.@objectstack/runtime, full suite at7647624a00:Test Files 239 passed (239) · Tests 3330 passed (3330), exit 0.74b480158b(18 rest files + the new runtime pin):18 passed / 360 testsand1 passed / 10 tests.@objectstack/clientledger consumers at60f7d46b49, against the rebuilt rest dist (rest-route-ledger-coverage,client-url-conformance,route-ledger-response-schema,packages-write-envelope,return-type-precision):5 passed / 14 tests.pnpm --filter @objectstack/rest typecheck,pnpm --filter @objectstack/runtime typecheck= tsc +check:test-typecheck) at60f7d46b49: both exit 0. (A first attempt was NOT MEASURED — TS7016 against apackages/spec/distthat had lost its declarations mid-run; rebuilt withpnpm --filter @objectstack/spec build,check-dts-emitted: 34/34, then measured.)pnpm --filter @objectstack/spec build && check:generated→ "All 15 generated artifacts are up to date".60f7d46b49(committed state; trap-restored; both blobs hash-equal to HEAD afterwards,git diff HEADempty): mutating the dispatcher's 404 spelling at its 6 sites turned the new runtime pin RED (4 failed / 6 passed— exactly the four wording cases, scoped and unscoped); resurrecting aGET /packagesroute in the registrar turned the REST absence pins RED (12 failed / 18 passedacrosspackage-publish-mount,direct-mount-introspection,rest-route-ledger.conformance). Both modules resolve fromsrcthrough relative imports, so no dist sits on the path and no build leg was owed.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(no paths); 79 families atf904e2d62e, 106 at60f7d46b49once the docs page joined the diff. Reconciliation--ran:106 derived, 106 run, 0 UNRUN. Green, except four NOT MEASURED (reported as such, not as passes):check:dual-build-cjs-loads(exit 3, PREREQUISITE NOT MET — needs a fullpnpm build),check:type-check-debt(exit 3, PREREQUISITE NOT MET — needs the full built closure),check:pm-dispatch-gates(timed out at the 170 s and 280 s per-command budgets),check:skill-examples(exit 1 — refuses without@objectstack/client-reactbuilt; unrelated to this diff). Three gates were red on the raw diff and are green after their own remedies, committed here:check:system-context-census(line rot,--fix),check:doc-authoring(prose-id baseline over-pinned the deleted ledger row,--census-ledgershrink),check:undeclared-dep-imports(staletype-onlyrow deleted with the import; its--self-testand real run both pass). The dispatch-namedcheck:nul-bytes: green.pnpm exec eslint --no-inline-config --format jsonover every.ts/.mjsthis diff adds or modifies — ① population: the repo'seslint . --no-inline-configwithignores: [...NEVER_LINTED](eslint.config.mjs); ②--format jsoncount: 44 files (the changed set after the merge, a superset of this branch's own 27), 0 errors, 0 warnings, exit 0, at60f7d46b49; ③ invariance: the config enables noparserOptions.projectand no typed@typescript-eslintrules (eslint.config.mjs:328), so nothing in this diff can move an untouched file's verdict.turbo ls --affectedlists 19 packages; only rest, runtime and the client ledger consumers were run here. The others (cli, client-react, cloud-connection, dogfood, downstream-contract, four examples, hono, http-conformance, organizations, plugin-auth, plugin-dev, service-sms, verify) consume rest/runtime and are left to CI — the behavioural change is confined to the REST registrar and its discovery projection, and the hono adapter's own suite aliases runtime to a mock.in_progressat the time of this body — headba6b8311ed(round 4: the census note's tracker id moved from the runtime string to an adjacent comment;pnpm check:doc-authoringexit 0 measured on this exact tree before the push) started 31 check runs (11success, 18 in progress, 2 skipped, read 80 s after the push). Onc1344677dceverything else was green —Dogfood Regression Gatepassed (the authz-conformancediscover()reaches 1 of 17 route registrars in rest-server.ts — the ratchet's route-completeness guarantee is false for 66 of 85 mounted routes #13260 census fix confirmed by CI) — andLint & Repo Gateswas red only oncheck:doc-authoringfor that id-in-string.check:doc-authoring 0; that reading was taken on the docs tree (edcb5808e7) before the census note existed and was carried forward unchecked — an ordering mistake on my side, not a CI/local divergence. Earlier history:40c7679eecred on Dogfood 3/3 (the census);edcb5808e7cancelled by the next push; the initial draft head60f7d46b49had 0 check runs.Round 2 — contract-review follow-ups (all owed under any ruling outcome)
origin/main(8341ed21ce) merged; the one conflict,content/docs/permissions/system-context.mdx, resolved by taking main's side (symbolicfile#functionanchors) and re-runningpnpm gen:system-context-censuson the merged tree (no further re-anchoring needed; gate green).rest-server.tsauto-merged — main's change there is a comment hunk near the/metaread door, far from the discovery hunks. ⛔ No hand-edited anchors, no rebase, no force-push.plugin-hono-server-without-createHonoAppcomposition above); the uninstall's tenancy width narrows from the REST copy'sdeletePackage({ packageId, allTenants: true })(package-wide,protocol.deletePackagefinds zerosys_metadatarows the data plane finds 3 of — uninstall leaves orphaned rows (persistence half of #7557) #7705 case 4) to the dispatcher's{ packageId, organizationId?, keepData? }resolved for the caller's organization;GET /packages(not only/:id) no longer merges durablesys_packagesrows, so a published-but-not-installed artifact is neither listed nor gettable on the surviving door. Level unchanged:@objectstack/restminor.POST /packages/publishgate (manage_metadata); the read cohort is cited to the dispatcher domain's own gate by symbol (the census gate holds numeric row references to declared keys, so no row number is used).Round 3 — docs-drift (bot comment on this PR, 34 hand-written pages + 4 release-owned)
Re-derived on this tree with
node scripts/docs-audit/affected-docs.mjs --json 8341ed21ce(38 rows, anchors read per row). Fixed only what this diff falsified:content/docs/kernel/contracts/metadata-service.mdx— the REST endpoints table said the REST layer "mounts package routes under/api/v1/packages" and listedGET /api/v1/packagesas "List all packages (registry + database)". The family is the dispatcher domain's now, the list is the installed set (published-but-not-installed artifacts are not merged in), the read answers the bare row with the dispatcher's 404 wording, the uninstall is organization-scoped. While editing that table, itsPOST /api/v1/packages"Publish" row was corrected toPOST /api/v1/packages/publish— pre-existing (the bare POST is the install route), not something this diff falsified, fixed because the row is the one route this PR keeps.content/docs/permissions/permission-sets.mdx— "the uninstall response reports the revocation undercleanups" described the removed REST envelope; on the surviving door it isdata.persisted.cleanups.Judged accurate and left alone (listed because they name a symbol or literal this diff touched, not because they went false):
api/metadata-api.mdx(itsGET /packages"List installed packages",GET /packages/:id,DELETE /packages/:id,PATCH …/enable|disablesections describe the surviving door; itsPOST /packages/publishresponse is shown flat — pre-existing, untouched);api/client-sdk.mdx(theclient.packages.*snippet is the dispatcher door; itsVALIDATION_ERRORrow is the code's general meaning);api/environment-routing.mdx(env.packages.list()on the scoped client — the residue is a maintainer decision, stated in the changeset, not a docs edit);api/error-catalog.mdx,api/error-handling-client.mdx,api/error-handling-server.mdx,automation/webhooks.mdx,data-modeling/drivers.mdx,protocol/kernel/error-handling.mdx,protocol/objectql/types.mdx,ui/forms.mdx(anchored by theRESOURCE_NOT_FOUND/VALIDATION_ERRORliterals — vocabulary pages);automation/flows.mdx,protocol/objectui/actions.mdx,ui/actions.mdx(anchored byerrorMessage, a field name of theirs unrelated to the removed projection list);concepts/metadata-lifecycle.mdx,deployment/environment-variables.mdx,kernel/services-checklist.mdx(getMetaItems— the protocol verb is unchanged; the checklist's "packagesis answered kernel-side by the/packagesdispatcher domain" is now exactly true);automation/approvals.mdx,deployment/cli.mdx,permissions/capabilities.mdx,permissions/access-recipes.mdx,ui/audience-based-interfaces.mdx(manage_metadata— the write cohort is unchanged; none states the REST package read gate);permissions/authentication.mdx,plugins/packages.mdx(createRestApiPlugin— signature unchanged; the service-package blurb describes the service, not the route);data-modeling/formulas.mdx,deployment/publish-and-preview.mdx(/api/v1/cloud/packagesis a different surface),deployment/self-hosting.mdx,getting-started/examples.mdx(GET /api/v1/packageslists the installed set — true),permissions/record-view-auditing.mdx,plugins/adding-a-metadata-type.mdx,protocol/kernel/http-protocol.mdx(the discovery example's"packages": "/api/v1/packages"is what is advertised),ui/apps.mdx(anendpoint: '/api/v1/packages'example — served);permissions/system-context.mdx(row 54 already corrected in round 2).Release-owned, not touched (
content/docs/releases/):implementation-status.mdx(anchored byregisterDiscoveryEndpoints; its discovery sentence is generic and still true),v15.mdx(PATCH /packages/:idhistory),v16.mdx(manage_metadata),v17.mdx(the #7780allTenantsnote is about the protocol verb, still true). None reads false on this diff; nothing for a docs-only PR.The two declared blind spots, swept by hand: no hand-written page documents the
?version=repeated-parameter refusal on the package routes (query-multiplicity.tsyielded no anchor; the onlyrepeated query parammention isclient-sdk.mdx's generalVALIDATION_ERRORrow, which the/metaand/dataread points still honour); the package client methods appear only inclient-sdk.mdx(unchanged behaviour),metadata-api.mdx(packages.install) andenvironment-routing.mdx(above).Docs gates on the tree:
check:doc-anchors,check:docs-single-h1,check:doc-authoring,check-doc-route-spelling --advisory,check:docs-transcript-drift,check:docs-redirects,check:system-context-census,check:nul-bytes— exits in the report.Round 3b — the #13260 authz-probe blind-spot census (CI red on
40c7679eec, Dogfood Regression Gate 3/3)packages/qa/dogfood/test/authz-probe-blind-spot.census.tspinspopulation/reachable/blindSpotper probe file; the three ledger rows this PR removes moved thepackages/rest/src/rest-route-ledger.tsrow. The census has no generator, so all three numbers were re-derived from the tree with the module's ownderiveProbeFileCensus()(a tsx script over every row, diffed against the recorded table) — ⛔ not back-solved from the assertion: derivedpopulation 91, reachable 91, controlsroute: '91 /family: '91 /RestRouteLedgerEntry2.reachablemoved withpopulation(94 → 91 on both), because each removed row carriedfamily: 'packages'and was reached by that family's key; the blind spot stays 0, the 19 families are unchanged (the publish row keeps the family), andBLIND_SPOT_TOTAL_STATIC/_RUNTIME(75 / 80, summed over the route surfaces, not the ledger rows) are untouched. After the edit every one of the 13 rows derivessame. The row's note and the header's "94 audited rows" record the re-measurement. The aggregator's second red (dogfood-3-of-3 MISSING) is this shard's missing attestation, not a second cause.Acceptance notes
direct-mount-composition.tsandrest-api-plugin.ts(theprotocoloption only the removed routes read);rest-route-ledger.ts(the three rows);rest-server.ts(theroutes.packagesprojection was keyed on the removed list route);scripts/check-undeclared-dep-imports.mjs(stale row);content/docs/permissions/system-context.mdxandscripts/doc-authoring-prose-id.baseline.json(gate remedies); 8 REST pin files deleted, 13 re-pointed, 1 runtime pin added; the changeset.packages/rest/src/rest-server.ts(two measurement narratives onGET /api/v1/packages),packages/runtime/src/package-door-error-parity.test.ts(header says the registrar "mounts FIRST in the production stack").client.packagesdeclares envelopes two mounted surfaces do not emit:getdiverges between them, andinstall/enable/disabledeclare{ package }where the only serving surface sends the bare row #12034,pm:awaiting-maintainer) —packages/client/src/index.tsstill says only the REST registrar serves the scoped mount and declaresScopedEnvironmentClient.packages.getas{ package: InstalledPackage }with aversion?argument; both describe the removed door. That card holds the maintainer's 2026-08-26 ruling to bind the bare row; this PR is the producer-side convergence it was waiting on.packages/spec/distwas rewritten without declaration files (dist mtime inside thecheck:*batch window; not attributed to a specific gate), which reds any later typecheck in the same tree with TS7016 until the package is rebuilt. Recovered by rebuilding spec; recorded so the next tree does not read the TS7016 as its own.#12034 remains open— this PR does not close it.维护者速读(草稿)
改了什么:
/api/v1/packages的读与删三条路由只剩一套实现(runtime dispatcher 的/packagesdomain);@objectstack/rest只再挂POST /packages/publish。REST 那一套自带的source戳与?version=读取随之删除,并在 changeset 里记为有意去掉。REST/discovery改由 publish 挂载点推导routes.packages。为什么改:同一网址两套实现且行为已分歧(404 措辞、
data.package外壳、source戳),注释宣称的「遮蔽」在标准启动上从未发生(服务注册晚于 REST 挂载判定,三条路由根本没挂)。2026-09-02 裁决 A:删一套,不修优先级。风险与代价(含回滚):曾经真的走到 REST 那一套的部署(
package服务先于 REST 启动注册的宿主)会看到GET /packages/:id的响应从data.package变成data、404 措辞变化、?version=不再被读取;in-tree 的所有组合均不受影响(标准启动本来就是 dispatcher 在答)。SDK 的 scopedpackages.get(id, version?)声明的{ package }类型随之失真,归 #12034 那张已裁未合的卡。回滚 = revert 本 PR(纯删除 + 注释,无数据迁移)。席位意见:(留空)
你要做的:确认 changeset 的 minor 定级与「有意去掉」两项;决定 #12034 的 SDK 收口是否随本 PR 排队。
Generated by Claude Code