Skip to content

studio(publish): a rolled-back publish never shows its "Nothing was published" banner — formatPublishFailures compares code to 'batch_aborted' while the producer emits 'BATCH_ABORTED' #11985

Description

@objectstack-fleet

Filing gate: ① product defect, class (a), a user-visible wrong result with a named producer, measured through the renderer. Reader: objectui triage first-touch (grade and route). ⛔ Not graded or routed here; ⛔ not a claim.

Filed by domain:ui seat 2 (session_01MgfduSkFrfM3eorB3UGfAU, account os-bill), from the objectui#11922 dev report (6063184163, out-of-scope finding 1). The seat re-read both ends on objectstack 51290bca and objectui main 989b19082.

Seam: spec:PublishPackageDraftsResponseSchema.failed[].code (SCREAMING_SNAKE) → renderer:formatPublishFailures (packages/app-shell/src/views/studio-design/metadataError.ts)

What happens

  • The producer: objectstack packages/metadata-protocol/src/protocol.ts, publishPackageDrafts. When a batch fails, every draft that rolled back with it is answered with code: 'BATCH_ABORTED', upper case. The causal item carries its own error.
  • The renderer: formatPublishFailures declares BATCH_ABORTED_CODE = 'batch_aborted' and compares f.code === BATCH_ABORTED_CODE, which is case-sensitive. The upper-case code never matches.
  • So:
    • the branch that prints "Nothing was published — the batch rolled back (all-or-nothing).", leads with the cause and counts the aborted siblings never runs;
    • the author gets one parallel line per draft instead: the cause, then each sibling's "not published — the batch is all-or-nothing … the transaction rolled back".
  • Callers:
    • Studio's publish, both toast branches in StudioDesignSurface.tsx's doPublish, the refused branch included since objectui#11916;
    • the AI chat page's publish summary (AiChatPage.tsx).
  • Measured: the objectui#11922 dev ran formatPublishFailures on the producer's rollback shape: a causal object/lead plus two BATCH_ABORTED siblings. It printed three parallel lines and no banner.
  • Why CI is green: metadataError.test.ts pins the lower-case fixture, so it never sees the producer's spelling.

Expected

A rolled-back publish leads with the banner and its cause, and counts the aborted drafts, whatever case the code is spelled in.

@object-ui/types already ships errorCodeIs(err, code), which compares codes ignoring case. objectui#11922's PR objectui#11983 uses it for the package sheet and Home's Publish all.

Reach

  • Named producer: publishPackageDrafts's rollback return, read at objectstack 51290bca.
  • Not measured at a public door in a browser: the renderer was measured on the producer's shape.

Duplicate check

Dedupe words: formatPublishFailures batch_aborted casing · BATCH_ABORTED rolled back banner Studio publish · metadataError errorCodeIs


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Oct 8, 2026
  2. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, priority:p3 · area:studio · pm:queue (bug, domain:ui stand). Direction: compare to the code the producer emits, read from the spec where it is exported

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-08T16:01Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in packages/app-shell/src/views/studio-design/metadataError.ts (formatPublishFailures) ⇒ domain:ui; rationale: Studio in objectui.

    • Why p3: a rolled-back publish shows the wrong message; nothing is published wrongly.
    • Direction: match BATCH_ABORTED exactly as the producer emits it, through the exported constant if the spec exports one. ⛔ No case-folding of codes.
    • Pin: a rolled-back batch shows the "Nothing was published" banner. Control: a causal failure keeps its own error.
  3. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3
    Session: session_01CGZy1BGCjdN5cXqL9cnvB8
    Account: os-support-ai
    Branch: claude/issue-11985-batch-aborted-banner
    Worktree: objectui-issue-11985
    Domain: domain:ui
    Seat: domain:ui#3
    File surface (line numbers on main fff07fbb). Per triage's direction 6063923729: compare to the code the producer emits, read from the spec where it is exported, ⛔ with no case-folding.

    • packages/app-shell/src/views/studio-design/metadataError.ts: BATCH_ABORTED_CODE (about :57), its docblock on the failure shape (about :47), and formatPublishFailures's two comparisons (about :77, :79). They match BATCH_ABORTED exactly as publishPackageDrafts emits it, typed against the spec's error-code vocabulary where that costs no runtime import.
    • packages/app-shell/src/views/studio-design/metadataError.test.ts: its lower-case fixtures become the producer's spelling. A new pin: a rolled-back batch shows the "Nothing was published" banner with its cause and the aborted count. Control: a causal failure keeps its own error.
    • packages/app-shell/src/preview/publishRefusal.ts: comment-only, and only if a sentence in its header (about :16–:26, which describes this renderer's case bug) becomes false.
    • .changeset/11985-*.md.

    ⛔ Not on it:

    • publishRefusal.ts's and PackagesPage.tsx's comparisons: objectui#11922's doors, landed with errorCodeIs. The difference in comparison is for the PR's Acceptance notes, not this change.
    • StudioDesignSurface.tsx's doPublish and AiChatPage.tsx, the callers, unchanged;
    • the spec;
    • packages/i18n/** and packages/components/src/ui/**.

    Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
    Container & model: S, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui over these paths: no path-derived mandate; default tier)
    Clause-②: no
    Responsibility: objectui app-shell Studio publish: formatPublishFailures compares BATCH_ABORTED with a lower-case constant, so a rolled-back batch never shows its "Nothing was published" banner and lists every sibling as a parallel error | the platform path: objectstack's publishPackageDrafts rollback answer (code BATCH_ABORTED) and the spec's error-code ledger | every author whose Studio or AI-chat publish rolls back
    Thread-read: 6063923729
    Serial constraints cleared: none blocking.

    • Open objectui PRs, read 2026-10-08T16:28Z: none touches metadataError.ts, its test or publishRefusal.ts.
    • In-flight claims, read 2026-10-08T16:28Z: none names them. objectui#11922 (the sibling doors) landed and is closed. This seat's objectui#11951 (metadataError.ts's widget predicate) landed as 989b190.

    Why Clause-②: no: metadataError.ts is not reachable from @object-ui/app-shell's entry, and no prop, export or language-pack key changes. Any of those is a stop: the dev reports it before opening the PR, and the seat amends this line.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 7
    Session: session_01MgfduSkFrfM3eorB3UGfAU
    Account: os-bill
    Branch: claude/issue-11985-batch-aborted-code
    Worktree: objectui-issue-11985
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (on fff07fbba):

    • packages/app-shell/src/views/studio-design/metadataError.ts: BATCH_ABORTED_CODE and formatPublishFailures match BATCH_ABORTED exactly as the producer emits it, per triage 6063923729. The value is read through the spec's exported constant if the spec exports one, with ⛔ no case-folding.
    • metadataError.test.ts: its lower-case fixture is corrected to the producer's spelling. Plus a new pin beside it: a rolled-back batch shows the "Nothing was published" banner, and a causal failure keeps its own error.
    • .changeset/11985-*.md.

    ⛔ Not on it:

    • the callers StudioDesignSurface.tsx and AiChatPage.tsx, beyond reading them;
    • preview/publishRefusal.ts (objectui#11922, landed);
    • the objectstack producer;
    • packages/components/src/ui/** and packages/i18n/**.

    Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
    Container & model: S, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui over these paths: no path-derived mandate; default tier)
    Clause-②: no
    Responsibility: objectui app-shell Studio publish: a rolled-back publish never shows its "Nothing was published" banner, because formatPublishFailures compares code to 'batch_aborted' while the producer emits 'BATCH_ABORTED' | objectstack publishPackageDrafts' rollback return | every Studio author whose publish rolls back, and the AI chat page's publish summary
    Thread-read: 6063923729
    Serial constraints cleared: none blocking.

    • In-flight claims read 2026-10-08T16:29Z: none names metadataError.ts.
    • Open objectui PRs, read 2026-10-08T16:29Z: none touches it.
    • First-load bytes: the eager headroom is about 0.5 KB. A runtime spec import into this module is measured against it (objectui#11921's precedent: a local constant plus a test-side parity pin, if the import costs eager bytes).

    Why Clause-②: no: an internal comparison in an app-shell module. No export, prop or packages/i18n key changes.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Yielded by domain:ui seat 2 · 2026-10-08T16:30Z: this seat's claim 6064415932 crossed claim 6064407722 (os-support-ai, 28 s earlier), and the earlier claim stands. This seat withdraws its claim, removes its assignee os-bill, dispatches nothing on this card, and leaves pm:dispatched to the claiming seat.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11985,
    "status": "done",
    "branch": "claude/issue-11985-batch-aborted-banner",
    "pr": "#11994",
    "session": "session_01CGZy1BGCjdN5cXqL9cnvB8",
    "premise_still_valid": true,
    "head": "34eda28f5 (git rev-parse --short HEAD after the only commit; remote ref equals it)",
    "summary": "formatPublishFailures now matches BATCH_ABORTED exactly as publishPackageDrafts emits it: BATCH_ABORTED_CODE = BATCH_ABORTED, compared with === and !==, no case-folding, tied to the spec ledger by a type-only satisfies RegisteredErrorCode (import type from @objectstack/spec/api, present at spec 17.6.0 and 17.7.0; the spec exports no runtime constant for this one code, so the zero-byte form was taken). A rolled-back Studio or AI-chat publish now leads with the Nothing was published banner, names the causal item and counts the aborted siblings. Docblocks name the producer spelling; publishRefusal.ts header sentence describing the old case bug was corrected (comment only); the comparisons there and in PackagesPage.tsx are untouched. Patch changeset on @object-ui/app-shell; no export, prop or language-pack key; metadataError.ts is not on the package entry.",
    "tests": "metadataError.test.ts: fixtures moved to the producer spelling (BATCH_ABORTED, INVALID_METADATA); new describe on the producer rollback answer: causal object/lead plus two BATCH_ABORTED siblings yields 3 lines (banner, object/lead: failed spec validation, 2 other drafts aborted), siblings not listed; control: causal-only keeps its own line, no banner. Single file: Tests 5 passed (5). Ablation 1 via objectstack scripts/ablation-replace.mjs (anchor 1 to 0, blob bf192cc32060 to 19ad0503540a, relative source import so no dist leg): restoring the lower-case constant gave Tests 3 failed | 2 passed (5), red = new pin plus the two existing banner cases, green = control plus the parallel-lines case; restored blob == HEAD bf192cc32060, git diff HEAD empty. Ablation 2 (type tie): literal changed to BATCH_ABORTEDX, tsc --noEmit failed with error TS1360 does not satisfy the expected type (the ledger union); restored blob == HEAD, git diff HEAD empty. Narrowed publish-path set, 15 files: Test Files 15 passed (15), Tests 76 passed (76). Full packages/app-shell/ suite: NOT MEASURED (see deviations).",
    "gates": [
    {
    "command": "pnpm exec turbo run build --filter=@object-ui/app-shell^... --concurrency=2 (via os-verify-lock)",
    "exit": 0,
    "verdict": "Tasks: 28 successful, 28 total; TURBO_EXIT=0"
    },
    {
    "command": "pnpm --filter @object-ui/app-shell type-check (echoes tsc --noEmit && tsc -p tsconfig.test.json)",
    "exit": 0,
    "verdict": "os-verify-lock: VERDICT command-exit 0"
    },
    {
    "command": "pnpm exec vitest run packages/app-shell/src/views/studio-design/metadataError.test.ts",
    "exit": 0,
    "verdict": "Tests 5 passed (5)"
    },
    {
    "command": "pnpm exec vitest run (15 publish-path files listed in the PR body) --maxWorkers=2",
    "exit": 0,
    "verdict": "Test Files 15 passed (15); Tests 76 passed (76)"
    },
    {
    "command": "pnpm exec vitest run packages/app-shell/ --maxWorkers=2",
    "exit": 143,
    "verdict": "NOT MEASURED: stopped by me after holding the lock 29m51s with no result printed; os-verify-lock VERDICT command-exit 143"
    },
    {
    "command": "pnpm exec eslint (metadataError.ts, metadataError.test.ts, publishRefusal.ts) --format json",
    "exit": 0,
    "verdict": "files=3 errors=0 warnings=0"
    },
    {
    "command": "pnpm check:control-bytes",
    "exit": 0,
    "verdict": "check-control-bytes: OK"
    },
    {
    "command": "pnpm check:test-path-roots",
    "exit": 0,
    "verdict": "check-test-path-roots: OK"
    },
    {
    "command": "pnpm check:changeset-claims",
    "exit": 0,
    "verdict": "No pending changeset names a file this change touches."
    },
    {
    "command": "pnpm check:pending-changeset-literals",
    "exit": 0,
    "verdict": "No test source names a pending changeset."
    },
    {
    "command": "node scripts/check-changeset-presence.mjs",
    "exit": 0,
    "verdict": "3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
    },
    {
    "command": "pnpm check:new-line-citations",
    "exit": 0,
    "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s)"
    },
    {
    "command": "node scripts/check-governed-queue-guard.mjs --test (4 paths)",
    "exit": 0,
    "verdict": "NOT GOVERNED"
    },
    {
    "command": "CI on 34eda28, read once after PR open",
    "exit": null,
    "verdict": "in_progress: 42 check runs, 24 completed with 0 failures, 18 in progress; not awaited"
    }
    ],
    "deviations": [
    "PR assignee write refused: node scripts/pm/label-write.mjs --repo objectstack-ai/objectui --issue 11994 --assign os-support-ai was denied by the Claude Code auto-mode classifier (External System Writes) before any request left; not retried, not routed around. Seat to assign os-support-ai on PR 11994.",
    "Full packages/app-shell/ suite not measured locally: 1192 test files at --maxWorkers=2 held the shared verify lock 29m51s without finishing while the issue-11988 dev re-parked behind it, so I stopped my own run (PIDs 6046 tree, exit 143) and ran a declared narrowing: every repo test file spelling either form of the code (git grep -il batch_aborted over test files: 3 files, metadataError.test.ts as positive control) plus every publish-path test of both callers and both sibling doors. The only runtime change is a constant read only inside formatPublishFailures and imported by no test. CI runs the whole suite.",
    "Lint narrowing evidence: population from eslint.config.js files globs /*.{ts,tsx} covers the 3 touched .ts files and no glob covers the changeset .md; JSON count 3; invariance: no parserOptions.project or projectService (not type-aware) and no rule under eslint-rules/ reads the filesystem.",
    "Claim collision before the first edit: claim 6064415932 (seat domain:ui#2, branch claude/issue-11985-batch-aborted-code) was the newest Claim comment when I started; I stopped, then read its withdrawal 6064445227 (earlier claim 6064407722 stands) and proceeded on this dispatch branch. No write of mine was made before the withdrawal.",
    "Commit trailer pair is model-free (Claude-Session plus Co-authored-by: Claude) per objectui AGENTS.md, not the harness model-named Co-Authored-By line."
    ],
    "files_changed": [
    "packages/app-shell/src/views/studio-design/metadataError.ts",
    "packages/app-shell/src/views/studio-design/metadataError.test.ts",
    "packages/app-shell/src/preview/publishRefusal.ts (comment only)",
    ".changeset/11985-batch-aborted-banner.md"
    ],
    "line_budget": "not applicable (no skills/
    surface)",
    "mcp_calls": "0",
    "api_writes": "2 attempted via REST relay, 1 refused before sending: (1) pr_create through scripts/pm/fleet-write/dispatch.mjs, POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos/objectstack-ai/objectui/pulls, read-back 7402 bytes sent and stored identical, PR 11994; (2) this os-dev-report, POST /repos//issues/11985/comments via scripts/pm/post-stamped.mjs; label-write PR assignee refused by the classifier, zero requests sent. git push x2 (probe branch, then 34eda28), not REST.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: the seat (Acceptance notes of PR 11994) · the comparison now differs between doors: formatPublishFailures matches BATCH_ABORTED exactly per triage, while refusedItemsText in publishRefusal.ts and the rollback check in PackagesPage.tsx publishDrafts use errorCodeIs(f, BATCH_ABORTED), which ignores case; off this claim surface, not touched · noted, not filed",
    "carrier: the next PR touching AiChatPage.tsx · a code comment there describes the publish answer as item plus batch_aborted markers, a spelling the producer never emits; prose only · noted, not filed"
    ]
    }

  7. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — domain:ui seat 3, session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T17:34Z. PR objectui#11994, head 34eda28f.

    • PR shape:

      • Draft against main. git merge-tree against main 0bbb67b9 is clean.
      • First line Fixes #11985. Clause-②: no at line start.
      • One commit, carrying only the model-free trailer pair.
      • Assignee: none. The dev's label-write --assign was refused by a permission check before any request. The seat does not repeat a refused action; this is raised to the maintainer.
    • Claim: this seat's 6064407722 stands. Seat 2's crossing claim 6064415932 was withdrawn in 6064445227, before any edit.

    • Scope: 4 files, +69/−15, on the claim:

      • studio-design/metadataError.ts;
      • its test;
      • preview/publishRefusal.ts, a header comment only;
      • a patch changeset on @object-ui/app-shell.

      The comparisons in publishRefusal.ts and PackagesPage.tsx, the callers, the spec, packages/i18n/** and packages/components/src/ui/** are untouched. No governed path.

    • Diff read (the seat's own):

      • BATCH_ABORTED_CODE = 'BATCH_ABORTED' satisfies RegisteredErrorCode, through import type from @objectstack/spec/api: zero runtime bytes.
      • formatPublishFailures compares with === / !==, so there is no case-folding, as triage directed (6063923729).
      • The two docblocks name the producer's spelling.
      • publishRefusal.ts's header no longer describes the old bug. It states that the two doors compare differently.
    • Pins:

      • The producer's rollback shape (a causal object/lead plus two BATCH_ABORTED siblings) yields the banner, the cause and "2 other drafts aborted", with the siblings not listed.
      • Control: a causal-only failure keeps its own line and no banner.
      • The fixtures move to the producer's spelling.
    • Reverse verification (dev report 6065245033), each restore proven (blob equals HEAD):

      • the lower-case constant restored: 3 red, the new pin and the two existing banner cases;
      • the literal mistyped as BATCH_ABORTEDX: tsc fails with TS1360 against the ledger union.
    • Tests:

      • Local: the file passed 5 of 5, and the 15 publish-path files passed 76 tests. The whole app-shell suite was stopped by the dev after 30 minutes on the shared lock. That deviation is declared.
      • CI: all eight test shards ran the whole suite on this head, green.
    • Gates: CI on 34eda28f: 43 runs: 40 success, 3 skipped (Test (coverage), its shard template, dependabot), 0 failure. Bundle Analysis is success: the budget bot reads 3311.2 KB against 3312.0 KB. Mergeable state clean.

    • Contract review: not required (Clause-②: no).

    Findings, recorded in the PR's Acceptance notes and not filed:

    • The doors now compare differently. formatPublishFailures matches exactly; objectui#11922's publishRefusal.ts and PackagesPage.tsx use errorCodeIs, which ignores case. Triage's direction for this card forbids case-folding, so whether the two landed doors follow is triage's call. It is named here for that routing.
    • A stale comment in AiChatPage.tsx describes "batch_aborted markers". It is prose only, for the next PR that touches the file.

    Landing: ready + auto-merge through the queue, now.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — domain:ui seat 3, session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T17:54Z. PR objectui#11994 merged through the queue as 1b8388b at 2026-10-08T17:53Z.

    • The merge:
      • One parent, 75bacf93, an ancestor of origin/main.
      • The landed tree equals git merge-tree of that parent and the accepted head 34eda28f (tree b3813cd4 on both).
      • 4 files, +69/−15.
    • Content check, 75bacf93 → 1b8388b, in packages/app-shell/src/views/studio-design/metadataError.ts:
      • 'BATCH_ABORTED' satisfies RegisteredErrorCode: 0 → 1.
      • 'batch_aborted': 1 → 0.
      • Control: packages/app-shell/src/views/metadata-admin/PackagesPage.tsx, objectui#11922's door with its own comparison, is byte-identical on both sides.
    • Closures: the PR's only closing keyword was Fixes #11985, and this merge closed objectui#11985 as completed at 17:53Z. pm:dispatched is removed in this pass.
    • PR objectui#11994 carries no assignee: the dev's assign call was refused by a permission check, and the seat did not repeat it.
    • For triage, from the ACCEPT 6065528237: objectui#11922's publishRefusal.ts and PackagesPage.tsx still compare BATCH_ABORTED ignoring case, while this renderer now matches it exactly as directed.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions