Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): thread packageId into both resolveDraftOrgScopeForPublish probes - #11156
Conversation
…peForPublish probes (#11003) Maintainer ruling 2026-08-22, option A: the scope probes ask the promote's question. A package-stating per-item publish now resolves the org scope of the draft it named; the package-agnostic probe could match another package's row in the caller's org and answer 404 [no_draft] over a publishable env-wide draft. Accepted cost, on the record: a caller stating a package no longer discovers a no-package draft of the same (type, name). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RfyXxZ2WPjcjhuXpiQQc3y
…less draft row The post-promotion drain is an optimistic-lock delete keyed on the row's checksum; a checksum-less hand-built row makes the drain classify the mismatch as the benign newer-draft race and survive, failing the remedy half of the accepted-cost pin. Stamp hashSpec(body) exactly as the repository's put does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RfyXxZ2WPjcjhuXpiQQc3y
📓 Docs Drift CheckThis PR changes 1 package(s): 10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 7 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 eb8fd3173397b1fc311aa2eafab80eb171e11606 && git checkout eb8fd3173397b1fc311aa2eafab80eb171e11606
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 17bad125cca89674cfd2dca59025b0d31da68968 0e7499df5240d6217589060265b5c4466023f997 && git checkout -B drift-repro 17bad125cca89674cfd2dca59025b0d31da68968 && git merge --no-ff 0e7499df5240d6217589060265b5c4466023f997
node scripts/docs-audit/affected-docs.mjs --json 17bad125cca89674cfd2dca59025b0d31da68968
|
Uh oh!
There was an error while loading. Please reload this page.
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32602077404 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes#11003
The ruling this implements
Maintainer, 2026-08-22, live session (「同意所有」, item 7 = A), recorded by the triage seat on the issue:
Options B (package-first with package-agnostic fallback) and C (detect-and-refuse) were closed by the adjudication; B by name — it reintroduces the two-question resolution #8907 removed, and its failure mode silently publishes another package's draft on a mistyped package name instead of failing loudly.
What changed
packages/metadata-protocol/src/protocol.ts, exactly the surface the card fences — the two probes and their single-item caller:resolveDraftOrgScopeForPublishtakes an optionalpackageId: string | null; when present, bothsys_metadataprobes addpackage_idto theirwhere, so the scope probe asks the identical question the promote'swhereForasks.undefinedspreads nothing — an unstated package keeps the historical package-agnostic probes, matching the promote's own match-any resolution.publishMetaItemthreads the dimension under the same present/absent contractpromoteDraftForPublishuses ('packageId' in request, with the?? nullcoercion), so all three spellings — absent,null, string — agree between probe and promote by construction.Accepted cost (the ruling, not a side effect): a publish stating
?package=no longer discovers a package-less draft of the same(type, name)— it answers404 [no_draft]; the remedy is retrying without?package=. The changeset (minorper the launch-window convention, with an ADR-0087not-required (no-migration-prescription)disposition) words that narrowing and its remedy for release-notes readers.The two pins the adjudication names — both against the real protocol
Both live in
protocol-publish-drafts-package-scope.test.ts, the existing ADR-0048 coexistence family, as a new#11003describe block (5 cases):app.otherholds anorg1draft for(object, shared_ticket);app.demoholds an env-wide (organization_id IS NULL) draft for the same(type, name); publish?package=app.demowith session orgorg1finds app.demo's draft. Failing baseline demonstrated: with the probes reverted toorigin/main, this case rejects with the card's exact[no_draft] No pending draft exists for object/shared_ticket— probe 1, package-agnostic, matched app.other'sorg1row; the package-exact promote then found nothing there.app.demo; app.other'sorg1draft stays pending, undrained, its partition untouched). Same reverted-probe baseline: rejects[no_draft].Guard cases beside them, each labelled in-file with whether it had a pre-fix red:
org1— the ADR-0005 own-org-shadows-env precedence survives the package narrowing. Green pre-fix too, stated so in the test; it bounds the fix, it does not reproduce the defect.code: 'NO_DRAFT',status: 404) over a package-less draft and the row is untouched; retrying without?package=publishes it. No pre-fix red (the package-exact promote already answeredno_drafthere) — it pins the ruling's cost sentence end-to-end.Fixture notes, per the dispatch's PR #11139 warning: the org-scoped
objectdrafts are seeded throughsaveMetaItemwith an explicitpackageId(a package-stating save — not the package-less save path #11139 rebinds), under theOS_METADATA_WRITABLE=objecthatch scoped to the describe (objectisallowOrgOverride: false; without the hatch the #6190 gate answers 403 before the probes matter — the hatch is the deployment shape in which the card's 404 is observable). The one package-less draft row is seeded by directengine.insert, deliberately not throughsaveMetaItem(mode:'draft'), so PR #11139's binding changes cannot move this fixture's meaning; it carrieschecksum: hashSpec(body)because the post-promotion drain is an optimistic-lock delete keyed on it.sys-metadata-repository.tsis untouched here; PR #11139 owns that file and is not addressed here.Verification (at
0e7499df5, sha printed inside the final run)origin/main(mutation proven on disk: fix marker count 0, superseded comment count 1) → the 2 coexistence cases red with[no_draft], all other cases and the rest of the package green (Tests 2 failed | 1862 passed); fix restored (marker count 3, comment count 0) → whole package green.pnpm --filter @objectstack/metadata-protocol testat head0e7499df5:Test Files 135 passed | 2 skipped,Tests 1864 passed | 10 skipped.@objectstack/metadata-protocol, run against the rebuilt dist containing the fix):@objectstack/objectql4034/4034,@objectstack/rest2192/2192.scripts/pm/dispatch-gates.mjsfrom the merge base): all 20 green, each exit code captured pre-pipe; highlights in the gates' own words —check-adr-0087-registrationaccepts theno-migration-prescriptiondisposition;✓ This diff introduces no 'major' bump;check-engine-double-contract: OK;check-nul-bytes: OK;check-type-check-coverage --re-measure: OK — … none above its recorded number(metadata-protocol is a debt-ledger package with notypecheckscript; the ratchet is its type gate).Generated by Claude Code