Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): declare packageId on publishMetaItem's request type, and correct three comments that say the per-item door names no package - #11005
Conversation
…t type `publishMetaItem` declared `type / name / organizationId / actor / message / _skipSeedApply` and no `packageId`, while `POST /meta/:type/:name/publish ?package=PKG_ID` (#10063) states one on every HTTP-driven promotion that names a package. The value already flowed correctly - the request object is forwarded whole and `canonicalizeMetaRequestType` is a spread - but the binding was invisible to every typed caller and one destructuring refactor away from being dropped in silence. Also corrects three in-tree comments that claimed the per-item door "names no package", which stopped being true at #10063. The `draftForGate` / `rowToItem` warning those comments were really defending is preserved verbatim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
…+ changeset Measured: deleting the cast yields `TS2339: Property 'publishMetaItem' does not exist on type 'RestProtocol'`, not a TS2353 about an unknown key. It is ADR-0076 D9 feature detection for a server-only extension `packages/spec` declares no member for - unchanged by declaring `packageId` on the implementation's request type. Removing it is a `packages/spec` contract decision; #10350 is not addressed by this comment beyond recording the measurement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
…ollow-up The package-dimension asymmetry the corrected comment describes is now tracked as #11003; behaviour here stays untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
📓 Docs Drift CheckThis PR changes 2 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 19 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 2f763b5172ce4dff32b9f02d598251a9bee7b8a9 && git checkout 2f763b5172ce4dff32b9f02d598251a9bee7b8a9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bd003d821787d3d72e68c5297d3d2088a473c5c0 ca53f9aad5a4e989ef284380072c56f2fd362a9d && git checkout -B drift-repro bd003d821787d3d72e68c5297d3d2088a473c5c0 && git merge --no-ff ca53f9aad5a4e989ef284380072c56f2fd362a9d
node scripts/docs-audit/affected-docs.mjs --json bd003d821787d3d72e68c5297d3d2088a473c5c0
|
Uh oh!
There was an error while loading. Please reload this page.
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32551602796 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes#10350
publishMetaItem's declared request type carriedtype / name / organizationId / actor / message / _skipSeedApplyand nopackageId, while since #10063POST /meta/:type/:name/publish?package=PKG_IDstates one on every HTTP-drivenpromotion that names a package. Three in-tree comments still claimed the per-item
door "names no package".
Nothing was broken at runtime, and this does not repair a live bug. The value
already flowed end to end: the request object is forwarded whole, the one
transform in between (
canonicalizeMetaRequestType) is a spread that drops nokey, and
promoteDraftForPublishalready declaredpackageId?: string | null.The defect was on the declared contract — invisible to typed callers, and one
destructuring refactor away from being dropped in silence.
What changed
publishMetaItemdeclarespackageId?: string | null(
packages/metadata-protocol/src/protocol.ts), with the absent-vs-nulldistinction documented: an absent key keeps the historical "match any
package" resolution,
nullpins the lookup to the unbound row, and apresent-and-
undefinedkey coerces tonulldownstream and makes apackage-bound draft unfindable.
promoteDraftForPublishand athird in
resolveDraftOrgScopeForPublishthat the card did not name butcarries the identical stale claim.
protocol-publish-drafts-package-scope.test.ts.The fence inside (3) was honoured
The
draftForGate/rowToItemwarning those comments were really defending —that the package must not be read off
draftForGate— survives verbatim,proved byte-for-byte against
HEAD:Only the "names no package" claim was rewritten. Both surviving matches for that
phrase in the file are now the quoted-and-corrected forms.
environmentIdis deliberately NOT added — this diverges from the cardThe card's⚠️ asked for the declared shape, noting
environmentIdsits in thesame cast-hidden position at the call site. That is true of the call site, but
the conclusion is already ruled on:
environmentIdis out of the protocolrequest shape by the maintainer ruling recorded 2026-08-18 on #9741, quoted in
packages/rest/src/rest-server.ts:Measured independently:
request.environmentIdoccurs nowhere inpackages/metadata-protocol— the class reads its environment off the instance(
private environmentId?: string, set at construction). Adding it would reversethat ruling rather than record it, so the exclusion is documented in-code
instead. Flagged for the maintainer rather than decided quietly.
Why the
(p as any)cast could NOT be removedThe card called the cast the finding — "it is what makes the call compile, so the
contract is currently enforced by nothing". The first half is true; the causal
claim is not. Measured by deleting the cast and running
pnpm --filter @objectstack/rest typecheck:TS2339(member does not exist), notTS2353(unknown key) — the compilernever reaches the argument.
publishMetaItemis an ADR-0076 D9 server-onlyextension:
RestProtocolisDataProtocol & MetadataProtocol, andMetadataProtocol(packages/spec) declares no such member. OnlyPublishMetaItemResponseSchema(#7294) exists there — no request schema, nointerface entry. The cast is feature detection, exactly like the
auditMetaItemtwin a few hundred lines up.
Re-measured after this PR's fix: byte-identical error. Declaring
packageIdon the implementation's request type changes nothing there, because that type
lives in
@objectstack/metadata-protocol, whichpackages/restdeliberatelydoes not depend on. Removing the cast needs
MetadataProtocolto declare themember plus a
PublishMetaItemRequestto hang #9741'sTransportScopedMetaRequesttyping off — a
packages/speccontract decision, which this card fences outexplicitly. Recorded as a comment at the call site so it is not re-filed; the
decision is raised in the dispatch report.
Evidence
Defect control on the type surface (the runtime path is sound, so this is the
only falsifiable control available — both directions shown):
tsc --noEmit -p packages/metadata-protocolorigin/main)TS2353: ... 'packageId' does not exist in type '{ type: string; name: string; organizationId?: ...; _skipSeedApply?: ... }'packageIderrors gone, ledger unmovedThe before-state error prints the declared shape verbatim, independently
reproducing the card's claim about which keys the type carried.
The three new cases are regression PINS, not defect controls — they pass in
the before-state too (7/7 green before the type fix), because the runtime path
was already correct. Stated plainly rather than reported as a green control that
proves nothing.
Ablation — the pins can fail, on the exact mechanism the card names.
Replacing the wholesale forward with a destructuring rebuild that drops
packageId:That is the #8907 inversion reproduced on the per-item door. The third case
(no package stated) correctly stays green — nothing to drop. The subject resolves
from source (
./protocol.js, relative, same package), so the red itself provesthe mutation reached the executed code. Restored and re-verified:
git hash-object==
git rev-parse HEAD:PATH,git diff --exit-code0, ablation marker absentfrom
srcanddist, suite back to 7/7.Gates — union derived from the real change set via
node scripts/pm/dispatch-gates.mjswith no path arguments, after the finalcommit
ca53f9aad5. All 18 path-matched + 5 convention-triggered families run,exits captured before any pipe, all green. Selected verdict lines:
check-engine-double-contract: OK — 377 pinned, 133 in the DEBT ledger, 2 exempt.(no new double — the existingmakeStubEnginewas reused)check:where-matcher—baseline key set verified against 047ac86: no files added.check:query-options-erasure—baseline key set verified against 047ac86: no files added.check-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured in 332.1s, 1908 raw tsc error(s) total, none above its recorded number.check-nul-bytes: OK (scanned 6350 text file(s) ... no raw ASCII control bytes).OK: 13 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.Suites on the final tree:
@objectstack/metadata-protocol1816 passed / 10skipped (134 files);
@objectstack/rest2179 passed (134 files).Out of scope, filed
#11003 —
resolveDraftOrgScopeForPublishprobessys_metadatawithout thepackage_iddimension, so now that the per-item door can state a package, thescope probe and the promote can ask different questions: a package-stating
publish can resolve the wrong org scope and answer
no_draftwhile the nameddraft sits publishable in the other scope. Behaviour deliberately untouched here
(#11003 is not addressed by this PR) — it needs its own fixture and a precedence
ruling between the ADR-0005 overlay order and the ADR-0048 package key. The
corrected comment points at it.
Changeset
.changeset/publish-meta-item-declares-package-id.md,patchfor@objectstack/metadata-protocoland@objectstack/rest. Justified: the declaredrequest type of a published package gains an optional key, which is user-visible
to typed callers — that visibility is the entire point of the card. Additive and
non-breaking, so no ADR-0087 marker is required;
check-adr-0087-registrationand
check-changeset-no-majorboth pass.Generated by Claude Code