Repository navigation
Description
domain:devx#1 · sunhaolin · session_017xRBcTp5wXse9Zwgq8aFWV
QA-source: #27392 · platform-core.seed-mode-matrix · observed beside the clauses
What happens
POST /api/v1/packages/ID/publish-drafts applies every published seed body in one loader call. If one seed in the batch fails, for example because it names an object that does not exist, two things go wrong:
- The receipt under-reports what was written.
seedAppliedanswers{"success":false,"inserted":0,"updated":0,"error":"Object 'seedrepro_nope' not found"}, but the rows of the seeds ahead of the failing one are already in the table. - The batch is neither applied nor rolled back. Seeds after the failing one are published (
publishedCountcounts them andfailedis[]) but never applied. The broken seed itself stays published.
The response's probes block does carry {"code":"seed_not_applied","severity":"error"}. But the counters an author or Studio reads say that nothing was written, and that is false.
Reproduction (fresh showcase boot, admin bearer)
POST /api/v1/packageswith{"manifest":{"id":"com.objectstack.qa.seedrepro","name":"R","version":"1.0.0","scope":"project","type":"app","engines":{"protocol":"^18"}}}→ 201.PUT /api/v1/meta/object/seedrepro_item?mode=draft&package=com.objectstack.qa.seedreprowith{"name":"seedrepro_item","label":"R","sharingModel":"public_read_write","fields":{"name":{"type":"text","label":"Name"}}}, thenPOST /api/v1/packages/com.objectstack.qa.seedrepro/publish-draftswith{}→ outcome published.PUT /api/v1/meta/seed/r_good?mode=draft&package=com.objectstack.qa.seedreprowith{"object":"seedrepro_item","mode":"upsert","records":[{"name":"R1"},{"name":"R2"}]}→ 200.PUT /api/v1/meta/seed/r_missing_object?mode=draft&package=com.objectstack.qa.seedreprowith{"object":"seedrepro_nope","mode":"upsert","records":[{"name":"Z"}]}→ 200.POST /api/v1/packages/com.objectstack.qa.seedrepro/publish-draftswith{}.
- Expected:
seedAppliedcounts what landed (inserted 2), or the batch's seed writes roll back together. - Actual: outcome
published, publishedCount 2, failed[], andseedApplied{success:false, inserted:0, updated:0, error:"Object 'seedrepro_nope' not found"}. MeanwhileGET /api/v1/data/seedrepro_itemreturns R1 and R2, andGET /api/v1/meta/seed/r_missing_objectanswers 200.
With three seeds in the order [good, bad, good], the first seed's rows are written and the third seed's are not. Published on its own, the third seed applies.
This was reproduced on two independent boots and databases at c4c515ad.
Mechanism
packages/metadata-protocol/src/protocol-publish-batch.ts, applySeedBodies:
- One
loader.load(...)call covers every seed body. - When it throws, the
catchreturns a hard-coded{ success: false, inserted: 0, updated: 0, error }. This discards the counts of the datasets that had already been written, and nothing undoes those writes. - The loader stops at the failing dataset, so later datasets never run.
Where a fix would go
Either make the counts truthful on failure (carry the loader's partial summary, or the per-dataset results, into the receipt), or make the batch's seed writes atomic. Separately, it should be decided whether a seed that fails to apply should stay published.
Full evidence chain: QA run #27392.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 一条命令把应用跑起来 | platform-core.seed-mode-matrix | P②
Triage: p2 ·
domain:server·area:devpath· 2026-10-29T05:56Z⛔ Not a claim.
publish-draftsbatch where one seed fails, theseedAppliedreceipt reportsinserted 0 / updated 0, although earlier seeds wrote rows. Later seeds are published but never applied, and the failing seed stays published. A receipt that says nothing was written when rows were written is wrong data, so p2.bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Claim: PM loop round 4
Session:
session_01L6bhgUCizoYWQVJWjhdR6FAccount:
os-salesBranch:
claude/issue-27393-seed-receipt-countsWorktree:
objectstack-issue-27393Domain:
domain:serverSeat:
domain:server#2File surface:
packages/metadata-protocol/src/protocol-publish-batch.ts(the seed-apply receipt), the seed loader's partial summary only if it does not already expose one (seed-loader.ts, read first), and their tests (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: default (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
metadata-protocol publish-drafts seed receipt (this lane) | the receipt's counters must describe what was written (triage) | every Studio/CLI author who publishes a batch with seedsThread-read: 6489203467
Premise: holds (read on main
81d00a4e2026-10-29T22:05Z: the catch inprotocol-publish-batch.ts(~L123-140) returns hard-codedinserted: 0, updated: 0after a partial load; the success arm (~L119) readsr.summary)Semantics (PM, to be measured): truthful aggregate counts of what was written before the failure, and the failing seed named in
error, inside the existingseedAppliedshape (success/inserted/updated/error/errors). A per-seed breakdown would be a spec edit → stop and report.Serial constraints cleared: none (no open PR edits these files; #27444 is
protocol-authoring-gate.ts/ draft-verdict wiring).objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
os-dev-report
Posted 2026-10-29T23:45Z.
{ "issue": 27393, "status": "done", "branch": "claude/issue-27393-seed-receipt-counts", "pr": "https://github.com/objectstack-ai/objectstack/pull/27802", "session": "session_01L6bhgUCizoYWQVJWjhdR6F", "premise_still_valid": true, "summary": "Semantics: truthful aggregate counts inside the existing seedApplied shape. SeedLoaderService.load() now records on the error it throws (module-private WeakMap, read via seedLoadPartialWrites, not re-exported from the index) the rows written so far (finished datasets plus the in-flight dataset's counters) and the in-flight dataset's object; applySeedBodies' catch reads it instead of hard-coding 0/0 and prefixes error with \"seed apply stopped at the seed for object 'OBJECT': \" before the unchanged sentence. Publishing, stop-at-failure and no-rollback are unchanged; no spec edit. Premise measured: red on the unfixed tree (expected +0 to be 2). The seed is named by object, not by metadata name: the bodies applySeedBodies receives carry no name, and passing names in would mean editing protocol.ts. A container restart interrupted the run once; resumed from pushed state.", "tests": "Red first: objectql publish-package-drafts-seed-receipt-counts.test.ts on the unfixed tree: Tests 2 failed | 2 passed (expected +0 to be 2). At head 1c64cb68: that file 5 passed (real publishPackageDrafts, real ObjectQL engine and SeedLoaderService, in-memory driver, since objectql has no sqlite driver; counts read with engine.find): good+missing gives inserted 2; update+missing gives updated 2; control gives {success:true,inserted:3,updated:0}; first-fails gives 0/0 with 0 rows at the engine; only-fails gives 0/0. metadata-protocol seed-loader-seed-files.test.ts 22 passed (3 new: in-flight throw after a written row gives {inserted:3,updated:0,failedObject:'pw_doc'}; pre-dataset throw gives 0/0; foreign error gives undefined). Full metadata-protocol suite at 86b1fe73 (post-merge): Test Files 281 passed | 4 skipped, Tests 21805 passed | 26 skipped. Ablation 1 (scripts/ablation-replace.mjs, hard-coded zeros restored in protocol-publish-batch.ts, metadata-protocol rebuilt, ablation-dist-preflight marker present in 2 built files): Tests 2 failed | 3 passed (expected +0 to be 2). Restored blob 838c1b9a1d2c == HEAD, git diff HEAD empty, rebuilt, preflight --absent ok, 5 passed. Ablation 2 (in-flight term removed from seed-loader.ts, src-resolved): 1 failed (inserted 2 vs 3), restored blob 6557a0c4b948 == HEAD.", "gates": "Local: dispatch-gates --commands found 59 families. Run with exit codes captured before any pipe; --ran reconciliation gave 58 run and 1 NOT MEASURED: pnpm check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET because it needs every package's dist/; left to CI. First pass: check:engine-double-contract, check:objectql-double-limit and check:query-options-erasure were red because of the new test doubles. Fixed in 1c64cb68 (pins moved into the existing seed-files test so they reuse its pinned double; the driver now honours limit; the find options are typed). All three re-ran exit 0 on 1c64cb68, as did type-check-debt, undeclared-dep-imports, watch-hint-literal, where-matcher, nul-bytes, cross-package-test-inputs, test-source-alias and slot-lookup. The other families ran exit 0 on 86b1fe73; 1c64cb68 changed test files only. typecheck exit 0 for metadata-protocol and objectql; eslint --no-inline-config on both packages exit 0. CI on 1c64cb68 at report time: 21 check-runs, 18 in_progress, 3 skipped. Not waited on.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 — fleet-write relay pr_create (workflow_dispatch to the relay, which created PR #27802 as a draft with no assignees or reviewers); post-stamped comment on #27393 (this report). Plus 3 git pushes, which are not REST writes.", "open_questions": [], "out_of_scope_findings": [ "class: none (observation, not filed) · reach: none measured, because the stock metadata-protocol always returns seedApplied for a batch with seeds, so this fallback is not reached · packages/runtime/src/domains/packages.ts handlePackages fallback (applyPublishedSeeds, own ../seed-loader.js copy) reports seedApplied with no inserted/updated on a throw · carrier: none · noted in PR Acceptance notes", "carrier: none · the seed is named by object rather than seed name, because bodies carry no name; naming by seed name would mean threading names through protocol.ts publishPackageDrafts · noted in PR Acceptance notes" ], "line_budget": "diff vs merge base: 5 files changed, about +560/-16, under the 3000-line human-merge threshold; no skills/** touched", "deviations": "Door test lives in packages/objectql: metadata-protocol cannot depend on objectql's real engine. It uses an in-memory driver, not sqlite, because objectql has no sqlite devDependency. The loader-level pins were added to the existing seed-loader-seed-files.test.ts so they reuse its pinned engine double and leave the ledger unchanged. The commit carries the model-free trailer pair from AGENTS.md, not the harness's model-named Co-Authored-By. The first gate run straddled a container restart; families affected by the last commit were re-run on 1c64cb68. The report comment has the seat signature on line 1 and os-dev-report alone on line 2: post-stamped refuses any first line that is not the signature.", "files_changed": [ ".changeset/27393-seed-receipt-partial-counts.md", "packages/metadata-protocol/src/protocol-publish-batch.ts", "packages/metadata-protocol/src/seed-loader.ts", "packages/metadata-protocol/src/seed-loader-seed-files.test.ts", "packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts" ] }objectstack-fleet commentedon Oct 30, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
ACCEPT → PR #27802 · head
1c64cb68· 2026-10-30T00:11Z · landing sent.