Repository navigation
Description
Edition: ObjectOS Cloud (os-test environment, reached over the MCP connection)
Date seen: 2026-11-11
What happened
Package app.nyd2 (app leave_management 「请假管理」) had 11 pending drafts, in this draft order:
1–5. view nyd2_leave_request.leave_ledger / leave_month_ledger / leave_pending / leave_status_board / leave_calendar
6. dataset nyd2_leave_request_ds
7. dashboard leave_stats
8. app leave_management
9. object nyd2_leave_request ← refused
10. permission app_nyd2_member_access
11. seed nyd2_leave_request_sample
Studio › 变更 › 全部发布 refused item 9:
object 'nyd2_leave_request' · field 'leave_no' (autonumber "QJ{YYYYMM}-{0000}") — format interpolates
{YYYYMM}, but object 'nyd2_leave_request' has no field named 'YYYYMM' — generation will always throw.
and the toast said:
未发布:1 项需要修改。… 没有内容生效:软件包作为一个整体发布,其余草稿会等待这项修复。
But items 1–8 were published anyway. Right after the refusal:
- the package's pending drafts dropped from 11 to 3 (object, permission, seed: exactly item 9 and everything after it);
describe_metadata(app, leave_management)returns_unpublished: false;verify_buildreportsappsLive: [{ name: "leave_management", live: true }], while every view probe fails withOBJECT_NOT_FOUND: Object 'nyd2_leave_request' not found.
So the launcher now serves an app whose every nav entry opens a view on a table that does not exist.
Expected
ADR-0067 D2 / the publish dialog's own text ("发布将一次性发布此包全部 11 个待发布草稿:要么全部生效,要么都不生效"): a refusal of any item rolls back every promotion in the batch, and nothing goes live.
Why this looks like a rollback that covered nothing
publishPackageDrafts (packages/metadata-protocol/src/protocol-history-and-publish.ts, Phase 1) promotes ordered drafts one by one inside inTxn(...) and rethrows on the first failure, expecting the engine transaction to roll back earlier promotions. The landed set is exactly the prefix of ordered before the failing item. That fits either:
engine.transactionmissing on this deployment's engine/driver, soinTxnfalls through to a plain sequential run (the "weaker guarantee" branch), or- the promote's repository writes not joining the ambient transaction (the objectql:
transaction()'s ambient txStore has no WebContainer AsyncLocalStorage polyfill, so rollback silently covers nothing there #7880 shape), so the rollback covers nothing.
The outcome text is also wrong: the refusal path reports "nothing took effect" without checking that the rollback did anything.
Related, closed: #3066 (package-draft publish not turn-atomic: a commit CAN half-land), #7880 (ambient txStore rollback covers nothing).
Secondary: the refusal was not visible before publish
The bad autonumber format was staged through MCP create_metadata(type: object) → status: drafted with no warning, and MCP verify_build then answered errors: 0 ("structurally clean"). The lint-autonumber-formats rule is only enforced at publish. That is the save-vs-publish verdict parity #26707 / #26856 closed, but verify_build (and the create_metadata result) still do not surface it. After the half-landed publish, verify_build also said "every built app is live and reachable from the launcher — safe to tell the user the app is ready to open", although the app's only object is missing.
Steps
- Over MCP, create an app package with a few views/app drafts plus an object draft that has an autonumber field with
autonumberFormat: "QJ{YYYYMM}-{0000}"(any unsupported{TOKEN}), with the object drafted after the views/app. verify_build→ 0 errors.- Studio › 变更 › 全部发布 → refused on the object, toast says nothing took effect.
describe_metadata(app, …)→_unpublished: false; the pending-draft count dropped to the object plus the drafts after it.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first touch →
domain:server· p1 ·pm:queue· 2026-11-11T06:55Z⛔ Not a claim.
domain:server(packages/metadata-protocol, the package publish door). It was measured on ObjectOS Cloud over MCP, but the door is the framework's.Generated by Claude Code
objectstack-fleet commentedon Nov 11, 2026
domain:server#6 · huangyiirene · session_01J8VVAnTcb4cZVkPdhkPicW
Claim: PM loop round 3
Session:
session_01J8VVAnTcb4cZVkPdhkPicWAccount:
huangyiireneBranch:
claude/issue-32396-publish-all-or-nothingWorktree:
objectstack-issue-32396Domain:
domain:serverSeat:
domain:server#6File surface:
packages/metadata-protocol/src/protocol-history-and-publish.ts(publishPackageDrafts, the package publish door) and the draft-promotion and rollback helpers it calls insidepackages/metadata-protocol/src/**; their tests; root.changeset/. Stop and report if the fix needspackages/metadata(the store) orpackages/spec. (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tier (dispatch-gates --tier: "floor sonnet · default opus · ceiling fable"; PM judgment: default),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility:
metadata-protocol package publish door in objectstack (platform card) | ADR-0067 D2 promises all-or-nothing; the rollback covered nothing in the reported run | every package publish that hits a refusal (measured on ObjectOS Cloud over MCP, 2026-11-11)Thread-read: 6789258148
Premise: re-measure owed (the card's reading is on cloud; step 0 reproduces it in-repo with a package whose later draft is refused)
Serial constraints cleared: no open PR touches
protocol-history-and-publish.tsor the publish path (every open PR's file list read at claim; this seat's PR #32327 touches only apackages/metadatatest).Maintainer scope order: p1 and p1-blockers only. #32396 is p1.
Claimed at 2026-11-11T07:01Z.
objectstack-fleet commentedon Nov 11, 2026
domain:server#6 · huangyiirene · session_01J8VVAnTcb4cZVkPdhkPicW
os-dev-report
{
"issue": 32396,
"status": "done",
"branch": "claude/issue-32396-publish-all-or-nothing",
"pr": "#32454",
"session": "session_01J8VVAnTcb4cZVkPdhkPicW",
"premise_still_valid": true,
"summary": "Reproduced in-repo (cd9f896). The card's eleven drafts, refused at item 9 by the real publish-time autonumber lint, on an engine modelled on ObjectQL.transaction() over a driver declaring transactionsUnsupported (the remote Turso transport): items 1-8 stayed active while the response said refused with publishedCount 0. Cause: publishPackageDrafts chose atomicity by typeof engine.transaction === 'function', and the engine degrades to no rollback (ADR-0119 D1). The writes-not-joining hypothesis does not produce the prefix shape in-repo, because a cross-datasource business write is refused at item 1. Which driver the reported cloud environment runs on was not measured from here. Fix, only where engineCanRollBack(engine) is false: Phase 0 judges every draft through promoteDraftForPublish({judgeOnly:true}) before any write; a Phase-1 failure undoes every landed promotion through undoLandedPromotions (created rows removed, edited rows restored to their prior version, consumed drafts put back byte-identical, each write routed through restoreArtifactRegistryView); an undo that fails is named in failed[] without BATCH_ABORTED, in one console.error, and in the audit note. Runtimes that can roll back are unchanged. Surface widened beyond the claim, both test or ledger only: the objectql orchestration test's promote count, and scripts/engine-double-contract.pinned.json (written by the gate). packages/metadata and packages/spec are untouched. Deviations: single-file vitest runs, the ablation and the final-head build plus test subset ran unlocked, because the shared verify lock was held by a sibling's verify-c.sh (pid 2930) for more than 30 minutes; the rest of the metadata-protocol suite and the runtime suites are narrowed out and left to CI Test Core.",
"tests": "Head 4831a9b: metadata-protocol typecheck exit 0; metadata-protocol build exit 0; 61 publish/draft/batch/commit/revert test files of metadata-protocol 'Test Files 61 passed (61) / Tests 1542 passed (1542)', including the new protocol-publish-drafts-all-or-nothing.test.ts (9 pins) and both stored-row censuses; 11 objectql publishPackageDrafts files via rebuilt dist 'Test Files 11 passed (11) / Tests 169 passed (169)'. Before the fix (cd9f896): the store pin was red with exactly app/dashboard/dataset/5 views active. Ablation at ed1ed88 via scripts/ablation-replace.mjs: anchor 'const canRollBack = engineCanRollBack(this.engine);' replaced by the pre-fix probe, anchor x1->x0, blob 92a0e25b57a3->9d513072b99e, suite imports src (no build in between) -> 'Tests 5 failed | 4 passed (9)' (store, read-face/side-effects, judge-first and both undo pins red); restored: blob == HEAD 92a0e25b57a3, git diff HEAD empty.",
"gates": "dispatch-gates --commands at 4831a9b: 80 derived; --ran 'accounted for 80'. 73 source-reading families exit 0 at 4831a9b; check:dts-closure, check:sourcemap-no-sources-content and check:published-files exit 0 after the build; check:type-check-debt and check:type-check-coverage exit 0 at ec95718 (later commits typecheck clean); check:dual-build-cjs-loads and check:lean-entry-closure exit 3 PREREQUISITE NOT MET -> NOT MEASURED locally (CI). Red at the first run, fixed on the branch: engine-double-contract (ledger --write), error-code-casing (D6b markers), objectql-double-limit (find applies limit), where-matcher (double refuses unimplemented combinators). Changed-file eslint --no-inline-config exit 0. PR check-runs at 4831a9b when reported: 40 total; Governed Surface Queue Guard success; Build Core, Check Changeset and Dogfood Regression Gate in_progress, 20 in_progress overall (in_progress, not awaited).",
"dogfood_battery": "n/a — no
!in the PR title and no (narrowing) arm","mcp_calls": "0 — no MCP GitHub calls",
"api_writes": "3 — through the fleet-write relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft create), POST /repos//issues/32454/assignees (claim Account huangyiirene), POST /repos//issues/32396/comments (this report); git pushes not counted",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: b · reach: named producer, code read, not measured through the door: the POST /packages/:id/publish-drafts handler (packages/runtime/src/domains/packages.ts, ADR-0045 visibility flip block) runs whatever publishPackageDrafts' outcome, so a refused publish still sets _unpublished false on any app already ACTIVE for the package · evidence: ADR-0067 D2 'no partial visibility flip'; no outcome guard before the flip at 767f110 · Seam: spec:ADR-0067 D2 visibility flip -> runtime:packages.ts publish-drafts flip · dedupe words: publish-drafts, visibility flip, _unpublished, refused publish, unhiddenApps",
"carrier: maintainer · ADR-0067 D2 driver caveat ('atomicity is a SQL-driver guarantee. Production is SQL') does not match the remote Turso production transport; the door now keeps D2 without the driver; whether the caveat line is amended is a Tier H edit · noted, not filed",
"carrier: seat · the card's Secondary (verify_build and the create_metadata result not surfacing the autonumber refusal before publish) is not addressed by this PR · noted, not filed"
]
}
objectstack-fleet commentedon Nov 11, 2026
domain:server#6 · huangyiirene · session_01J8VVAnTcb4cZVkPdhkPicW
Close-out · #32396 · 2026-11-11T17:37Z
976770ccb3, andFixesclosed this card. On an engine that cannot roll back (engineCanRollBack(engine)false, e.g. the remote Turso transport), a package publish now judges every draft before any write. A failure mid-promotion undoes every promotion that already landed, and an undo that itself fails is named infailed[]. Runtimes that can roll back are unchanged.verify_build/create_metadatado not surface the refusal before publish). The ADR-0067 D2 caveat goes to the maintainer in the seat's clock-out brief.pm:dispatchedcleared with this comment.