Repository navigation
fix(metadata-protocol): a failed seed in a package publish counts the rows earlier seeds wrote - #27802
Conversation
… seeds wrote The seed loader records, on the error a load throws, the rows it had written (finished datasets plus the dataset that threw) and the object of the dataset that threw. applySeedBodies' failure receipt reads it, so seedApplied.inserted/updated describe what landed and error names the seed that stopped the load. Publishing and the stop-at-failure behaviour are unchanged. Claude-Session: https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F Co-authored-by: Claude <noreply@anthropic.com>
…ed-receipt-counts
Moves the loader-level pins into seed-loader-seed-files.test.ts so they reuse its pinned engine double, and makes the publish-door test driver honour the caller's limit and type its find options. Claude-Session: https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 2 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 — 10 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 90d52cade4ca636a3786b8535b44b683ba6bb335 && git checkout 90d52cade4ca636a3786b8535b44b683ba6bb335
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2a0b37bb6fe5c4efc1e837d2fb321cf0b6960ba4 1c64cb68fc46c7a06c587c1611b08707d74e4e05 && git checkout -B drift-repro 2a0b37bb6fe5c4efc1e837d2fb321cf0b6960ba4 && git merge --no-ff 1c64cb68fc46c7a06c587c1611b08707d74e4e05
node scripts/docs-audit/affected-docs.mjs --json 2a0b37bb6fe5c4efc1e837d2fb321cf0b6960ba4
|
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F 维护者速读(席位终稿)
席位意见: ACCEPT。按分诊「计数永远描述实际写入」选了真实汇总计数,不扩形状、不改发布与回滚语义。 席位核对ACCEPT · head
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Requested by Sales · project thread
Fixes #27393
Clause-②: no
Metadata-protocol: none
Skills-docs: none — nothing an author writes changes; only the values of an existing receipt become truthful
维护者速读
seedApplied回执的inserted/updated现在等于失败前实际写入的行数,error点名是哪个对象的 seed 让加载停下;回执形状不变。inserted: 0, updated: 0,但前面的 seed 已经写进表里,回执说"什么都没写"是错的数据。packages/metadata-protocol/src/protocol-publish-batch.ts(catch 分支)、packages/metadata-protocol/src/seed-loader.ts(load()的 catch 与notePartialWrites)、packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts。pnpm --filter @objectstack/metadata-protocol build && pnpm --filter @objectstack/objectql exec vitest run src/publish-package-drafts-seed-receipt-counts.test.ts,预期 5 passed。What changes
Semantics chosen and stated: truthful aggregate counts, inside the existing
seedAppliedshape (success/inserted/updated/error/errors). When a seed in the batch fails:inserted/updatedare the rows the loader actually wrote before it stopped — the finished datasets plus any rows the failing dataset itself wrote before it threw;successstaysfalse;errornames the seed that stopped the load by the object its dataset populates:seed apply stopped at the seed for object 'OBJECT': ..., followed by the same sentence as before (a declared refusal quoted, anything else withheld asseed apply failed).Not changed: which seeds are published, that the loader stops at the failing dataset (later seeds are not applied), and that nothing is rolled back. All-or-none and per-seed counts were not chosen: the first is a larger behaviour change, the second needs a spec edit.
Mechanism:
seed-loader.ts:load()now catches a throw and records on the thrown error (a module-privateWeakMap, read through the newseedLoadPartialWrites(err)) what the load had written: the sum over finished dataset results plus the in-flight dataset's counters, and that dataset's object. The error itself is rethrown unchanged, so its declared status and message reach the caller as before.loadDatasetkeeps its insert/update counters in one object the load can read.seedLoadPartialWritesis not re-exported from the package index: the published surface does not move.protocol-publish-batch.ts: theapplySeedBodiescatch reads that record instead of hard-coding zeros. No record means the throw came before the loader wrote anything (the request parse, or the loader import), so 0 is the truthful count there.Tests
packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts(new) — the realpublishPackageDraftsdoor, real ObjectQL engine, realSeedLoaderService, in-memory driver (objectql carries no sqlite driver; harness copied frompublish-package-drafts-response-conformance.test.ts). Every count is checked againstengine.findon the seeded object:success: false,inserted: 2,errornamesseedrepro_nope; outcomepublished,publishedCount: 2(unchanged).updated: 2,inserted: 0.{ success: true, inserted: 3, updated: 0 }, as before.packages/metadata-protocol/src/seed-loader-seed-files.test.ts(3 cases added, reusing that file's pinned engine double) — a throw INSIDE a dataset after one of its rows landed (the file service failing while the row's seed file settles):{ inserted: 3, updated: 0, failedObject: 'pw_doc' }against the store; a throw before any dataset:{ inserted: 0, updated: 0 }; a foreign error carries no reading.Red first: on the unfixed tree, cases 1 and 2 failed with
expected +0 to be 2; 3 passed.Acceptance notes
POST /packages/:id/publish-draftskeeps a fallback seed apply (packages/runtime/src/domains/packages.ts,applyPublishedSeeds, with its own../seed-loader.jscopy) for protocols that do not self-apply seeds. Its catch reportsseedAppliedwithoutinserted/updatedat all. It runs only when the protocol returned noseedApplied;metadata-protocol'spublishPackageDraftsalways returns one when the batch has a seed, so this door is not reached by the stock protocol. Not touched here (outside this card's file surface).applySeedBodiescarry no name, and threading the names through would editprotocol.tsbeyond this card's surface.Verification
Ablation — hard-coded zeros restored (metadata-protocol dist rebuilt, door test)
Ablation — in-flight dataset count dropped (seed-loader, src-resolved)
(taken before the cases moved into
seed-loader-seed-files.test.ts; same assertions)Package tests, typecheck, lint, gates (head 1c64cb6)
@objectstack/metadata-protocolfull suite (after the origin/main merge, at 86b1fe7): Test Files 281 passed | 4 skipped; Tests 21805 passed | 26 skipped. At 1c64cb6:seed-loader-seed-files.test.ts22 passed; objectqlpublish-package-drafts-seed-receipt-counts+protocol-publish-package-drafts+publish-package-drafts-response-conformance38 passed.typecheck: metadata-protocol exit 0, objectql exit 0.--no-inline-configonpackages/metadata-protocolandpackages/objectql: exit 0.dispatch-gates --commands: 59 derived families, all run;--ranreconciliation: 58 run, 1 NOT MEASURED —check:dual-build-cjs-loadsexit 3, PREREQUISITE NOT MET (it needs every package's dist/; left to CI).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F