Skip to content

publish: a refused package publish on the Packages page and Home's "Publish all" reads "Action failed" — the server's per-item reasons in failed[] are never shown #11922

Description

@objectstack-fleet

Filing gate ① — product defect with a named location and a reproduction. It is an out-of-scope finding, class a (user-visible wrong result), from the dev report on objectui#11807 (comment 6053887752).

Who acts on it:

  • objectui triage first-grades and routes it.
  • Then a domain:ui execution seat dispatches the fix in two places:
    • packages/app-shell/src/views/metadata-admin/PackagesPage.tsx (the package sheet's publish, about :398–:435 on main 3683591);
    • packages/app-shell/src/preview/usePublishAllDrafts.ts (Home's banner and the draft-preview bar, about :97–:99).

⛔ Not a claim.

Seam: objectstack metadata protocol publishPackageDrafts → MetadataClient.publishPackageDrafts (unwraps the envelope since objectui#6965) → the two callers above

What happens

When a package publish is refused, the server's batch answers { success: false, outcome: 'refused', failed: [...] }, with each refused item and its reason in failed[]. The two callers above read the batch's own success === false first and throw before they reach failed[]:

  • The Packages page throws readEnvelopeFailureText(r) || 'Action failed'. The batch carries no error key, so the author reads "Action failed".
  • Home's Publish all and the draft-preview bar throw "publish-drafts did not publish this package".

Either way the author is not told which item was refused, or why.

Reach

  • Named producer: the objectstack metadata protocol's publishPackageDrafts refused-batch shape.
  • Measured: the Studio twin of this exact ordering (doPublish) was measured on that producer shape by the objectui#11807 dev. It gave toast.error('Action failed'), and it is repaired in PR objectui#11916.
  • Read from source, not measured at the two doors: the Packages page and Home / draft-preview bar have the same ordering.

Expected

A refused publish names each refused item with the server's reason, as Studio's publish does after PR objectui#11916: failed[] is read before the batch's own success. One shared helper for the three publish doors would keep them from drifting apart again; that is the fixing seat's call.

Duplicate check

Dedupe words: publish refused reason Action failed · publish-drafts failed[] success false · MetadataClient unwrap envelope refusal

Filed by domain:ui seat 2 (session_01MgfduSkFrfM3eorB3UGfAU) from the objectui#11807 dev report (out_of_scope_findings).


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
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    and removed on Oct 8, 2026
  2. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6
    Session: session_01MgfduSkFrfM3eorB3UGfAU
    Account: os-bill
    Branch: claude/issue-11922-publish-refusal-reason
    Worktree: objectui-issue-11922
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (on d7e9e9ab6):

    • packages/app-shell/src/views/metadata-admin/PackagesPage.tsx: publishDrafts (about :422–:475) reads failed[] before the batch's own success === false, so a refused publish names each refused item with the server's reason through the page's existing publishDraftsRolledBack / publishDraftsPartial wording.
    • packages/app-shell/src/preview/usePublishAllDrafts.ts: the batch verdict (about :95–:99) does the same for Home's Publish all and the draft-preview bar.
    • At most one module-private helper shared by the two, not exported from the package entry. Whether to share one is the dev's call, measured against first-load bytes (below).
    • The tests beside these, and .changeset/11922-*.md.

    ⛔ Not on it:

    • StudioDesignSurface.tsx and DraftChangesPanel.tsx (objectui#11807's Studio door, already landed);
    • MetadataClient and the spec's response shape;
    • any new packages/i18n key or export: if the wording cannot be built from existing keys and the server's own reason text, the dev stops and reports;
    • 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 publish: a refused package publish on the Packages page and Home's Publish all reads "Action failed" or "publish-drafts did not publish this package", with no refused item or reason | the objectstack metadata protocol's refused publishPackageDrafts batch (failed[]) and Studio's door after objectui#11916 | every author publishing a package outside Studio
    Thread-read: none
    Serial constraints cleared: none blocking.

    • In-flight claims read 2026-10-08T14:14Z: none names either file.
    • Open objectui PRs, read 2026-10-08T14:14Z: none touches either file.
    • First-load bytes: the eager-closure headroom is about 1 KB. The budget bot's reading must stay at or under 3312.0 KB, and the dev stops if it reads over.

    Why Clause-②: no: internal reads of a response shape the client already returns. No export, prop, spec key or packages/i18n key changes.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11922,
    "status": "done",
    "branch": "claude/issue-11922-publish-refusal-reason",
    "pr": "#11983",
    "session": "session_01MgfduSkFrfM3eorB3UGfAU",
    "premise_still_valid": true,
    "summary": "Both doors now read failed[] before the batch's own success === false. On the package sheet (PackagesPage publishDrafts), a rollback keeps the publishDraftsRolledBack row and names its causal item; a refusal with no BATCH_ABORTED element (the runtime's pre-flight checks) keeps the publishDraftsPartial row and names every refused item with its reason. Home's Publish all and the draft-preview bar (usePublishAllDrafts) toast 'Publish failed: type/name: reason' instead of 'publish-drafts did not publish this package'. Both doors share one module-private helper, packages/app-shell/src/preview/publishRefusal.ts (refusedItemsText), which uses errorCodeIs and is not exported from the package entry. No new export, prop, spec key or packages/i18n key. formatPublishFailures was not reused: measured, its shape does not fit (details in deviations). H1 confirmed at objectstack 51290bca: both refusal returns of publishPackageDrafts answer success:false with outcome:'refused', failed[] items {type,name,error,code?,issues?}, rollback siblings code 'BATCH_ABORTED', no error key; the REST door wraps them as {success:true,data} on a 200.",
    "head": "6226e589d (git rev-parse --short HEAD after the last commit; remote ref equals it)",
    "files_changed": [
    "packages/app-shell/src/preview/publishRefusal.ts (new, module-private helper)",
    "packages/app-shell/src/views/metadata-admin/PackagesPage.tsx",
    "packages/app-shell/src/preview/usePublishAllDrafts.ts",
    "packages/app-shell/src/views/metadata-admin/PackagesPage.publishRefusal-11922.test.tsx (new)",
    "packages/app-shell/src/preview/tests/usePublishAllDrafts.refusal-11922.test.tsx (new)",
    ".changeset/11922-publish-refusal-reason.md (new, '@object-ui/app-shell': patch)"
    ],
    "tests": "All runs at HEAD 6226e58, all under os-verify-lock (slot issue-11922), exit codes captured before any pipe. (1) Repo-root pnpm exec vitest run over: the 2 new files, the 18 existing test files importing PackagesPage or usePublishAllDrafts, useMetadataClient.advisorySink.test.tsx, perm-home-namespace-3546.test.tsx (reads usePublishAllDrafts.ts source), StudioDesignSurface.publishCount-11807.test.tsx, metadataError.test.ts, and column-identity.ratchet.test.ts, plus 1 uncommitted scratch file. Result: 'Test Files 26 passed (26)', 'Tests 214 passed (214)'; VERDICT command-exit 0. (2) pnpm --filter @object-ui/app-shell type-check: the script name type-check was echoed, VERDICT command-exit 0. The closure came from the turbo shared worktree cache: 28/28 cached, FULL TURBO. tsc -p tsconfig.test.json --listFilesOnly lists both new test files and the helper. (3) scripts/tests/one-authority-per-exported-name-6273.test.ts: 'Tests 11 passed (11)', VERDICT command-exit 0. (4) pnpm exec eslint on the 5 touched source and test files: exit 0, 0 errors and 20 warnings, all on lines this diff does not touch. (5) Exit 0 for each of: node scripts/check-changeset-presence.mjs, node scripts/check-changeset-no-major.mjs, pnpm check:new-line-citations ('VERDICT new-cross-file-line-citations: 0 new citation(s)'), check:control-bytes, check:i18n-keys, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:unreferenced-sources, check:changeset-claims, check:pending-changeset-literals. objectui has no dispatch-gates.mjs, so this gate list was derived by hand from package.json and the workflows. (6) Ablation, run with the fix committed first. objectstack scripts/ablation-replace.mjs --hold put the old ordering back: in PackagesPage the condition became 'if ((r as { success?: boolean }).success === false) {' (anchor 1->0, blob ade4d287->b04f71d9), and in usePublishAllDrafts the failed[] throw was deleted (anchor 1->0, blob b8473951->96292bec). A shell trap restored on EXIT/INT/TERM. Mutated run: 'Tests 4 failed | 4 passed (8)'. Red: all 4 new pins. The 2 hook cases received 'home.pendingDrafts.publishFailed: publish-drafts did not publish this package'; the 2 sheet cases never reached the expected row. Green: the 3 controls (published batch shows publishDraftsOk; envelope error with empty failed[] shows the envelope's text; the hook's published batch records health and publishes the orphan flow) plus the scratch file. Restore: ablation-replace --restore on both paths gave blob == HEAD blob, and git diff HEAD was empty. No dist leg was needed: the subjects are imported by relative source path, not through an exports entry.",
    "budget": "The budget bot's comment on PR 11983 (head 6226e58) reads: eager closure 3311.2 KB of 3312.0 KB, PASS. The bot prints no main comparison, and the run logs answer 403 from this container. So main was measured locally, with the console built under the lock from the same lockfile at base 989b190 and at head 6226e58. eagerGzipBytes: base 3390585 B (3311.1 KB), head 3390680 B (3311.2 KB, which matches the bot). Delta: +95 B gzipped (+0.09 KB). The reading was taken BEFORE objectui#11977 landed: origin/main was still 989b190 when it was read, the PR's own base. Sum for the seat: main 3390585 + the 11977 delta + 95 B.",
    "ci": "At report time: Bundle Analysis, Lint, Changeset Presence/Guard, Line Citation Gate, Live E2E and 19 other workflows completed with success. CI was in_progress, and its verdict was not waited for.",
    "deviations": [
    "PR body cites the producer by file and quoted line content at objectstack 51290bca, not by line number. The dispatch said 'quoting the producer's file and line'; objectui AGENTS.md #11 bans a line address in a PR body, so the line's text is quoted instead.",
    "formatPublishFailures was not reused. Measured shape mismatch: a scratch run on the producer's rollback shape (causal object/lead plus 2 BATCH_ABORTED siblings) printed 3 parallel lines with no rolled-back banner, because it compares f.code === 'batch_aborted' (case-sensitive, pre-ADR-0112 spelling). Its banner is also English-only, while the sheet answers in localized rows. The eager-bytes side of that ruling was not measured for the formatPublishFailures alternative.",
    "H2 direction: 'the 15.1+ rolled-back wording keeps naming its causal item' was listed as a control, but it turns RED under the ablation. On the producer's shape every rollback answers success:false, so the old ordering never reached that row. It is a new pin, not a control.",
    "To read main's eager figure, a temporary detached worktree /home/user/objectui-issue-11922-base was created at 989b190. It was removed after the measurement.",
    "Commit trailer: the model-free pair (Claude-Session plus 'Co-authored-by: Claude') per objectui AGENTS.md, in place of the harness's model-named trailer. PR footer: the session-URL form per the os-dev contract, in place of the harness's footer line.",
    "The 26-file vitest run included one uncommitted scratch file (the formatPublishFailures measurement). It was deleted before push and never committed."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: named producer — objectstack packages/metadata-protocol/src/protocol.ts publishPackageDrafts rollback return emits code 'BATCH_ABORTED' on every non-causal item (read at 51290bca); consumers StudioDesignSurface doPublish refused branch (after objectui#11916) and AiChatPage call formatPublishFailures(failed) · evidence: packages/app-shell/src/views/studio-design/metadataError.ts declares BATCH_ABORTED_CODE = 'batch_aborted' and compares f.code === BATCH_ABORTED_CODE; a scratch vitest run at 6226e58 on the producer's rollback shape printed 'object/lead: sharingModel is not set' then each sibling's 'not published — the batch is all-or-nothing (ADR-0067 D2) and object/lead failed; the transaction rolled back' as parallel lines, with no 'Nothing was published — the batch rolled back' banner; metadataError.test.ts pins the lowercase fixture so it stays green · Seam: spec:PublishPackageDraftsResponseSchema.failed[].code (SCREAMING_SNAKE) → renderer:formatPublishFailures · not fixed here: metadataError.ts is held by another seat (in-place exemption condition ③ fails) · dedupe words: formatPublishFailures batch_aborted casing · BATCH_ABORTED rolled back banner Studio publish · metadataError errorCodeIs",
    "carrier: 承接者:无 · noted, not filed — both doors here still read outcome 'nothing_to_publish' (success:false, empty failed[]) as a failure (sheet: envelope ladder to 'Action failed'; Home: 'publish-drafts did not publish this package') where Studio says nothing to publish since objectui#11807; unchanged by this PR (it is the H2 envelope control); reach not measured at either door. In PR Acceptance notes.",
    "carrier: 承接者:无 · noted, not filed — DraftChangesPanel's comment says the metadata-admin string table is off the console eager graph, while services/builtinComponents.tsx imports PackagesPage (which imports metadata-admin/i18n.js) statically; read from source only, not measured. In PR Acceptance notes."
    ],
    "mcp_calls": "0 — no MCP GitHub calls",
    "api_writes": "3 — REST proxy writes, each a POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write relay as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (PR 11983, draft; read-back identical 8445 B); (2) label-write --assign os-bill, POST /repos//issues/11983/assignees (read-back matches); (3) this os-dev-report comment, POST /repos//issues/11922/comments. Plus git push x2 (empty-branch probe, then 6226e58), which is not REST."
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — PR objectui#11983 · domain:ui seat 2 · 2026-10-08T15:25Z

    Implemented-by: os-dev subagent of session_01MgfduSkFrfM3eorB3UGfAU, branch claude/issue-11922-publish-refusal-reason, head 6226e58. Report 6063184163.
    Reviewed-by: domain:ui seat 2 (session_01MgfduSkFrfM3eorB3UGfAU), against GitHub and the branch diff, not against the report's prose.

    Checklist:

    • PR shape: draft, first line Fixes #11922, Clause-②: no at the start of a line. The assignee is os-bill.
    • Scope: 6 files inside claim 6061809076: PackagesPage.tsx, usePublishAllDrafts.ts, one module-private helper preview/publishRefusal.ts, two new pins and the changeset. The helper's one export, refusedItemsText, is not on the package entry, and no other module exports that name; the ratchet one-authority-per-exported-name-6273 passes.
    • H1, the producer: at objectstack 51290bca, publishPackageDrafts answers a refusal with success: false, outcome: 'refused' and failed[] items { type, name, error, code?, issues? }. Rolled-back siblings carry code: 'BATCH_ABORTED', and there is no error key. The PR body quotes the producer's lines by content, not by line number, per objectui AGENTS.md.
    • H2, the package sheet:
      • failed[] is read before the batch's own success.
      • A rollback keeps publishDraftsRolledBack and names its causal item.
      • A refusal with no BATCH_ABORTED element keeps publishDraftsPartial and names every refused item with its reason.
      • An envelope error with an empty failed[] still goes up the envelope ladder, and that is pinned as a control.
    • H3, Home's Publish all and the draft-preview bar: they throw type/name: reason instead of "publish-drafts did not publish this package". A published batch still records health and goes on to the orphan drafts, pinned as a control.
    • The seat's reuse call, falsified and accepted: formatPublishFailures was not reused. Measured on the producer's rollback shape, it compares code against lowercase batch_aborted, so it prints the siblings as parallel lines with no rolled-back banner. Its banner is also English-only, while the sheet answers in localized rows. The new helper reads codes through errorCodeIs, which ignores case. That metadataError.ts mismatch is filed separately by this seat.
    • Evidence:
      • 26 files and 214 tests pass: every test importing either module, Studio's publish pin, metadataError.test.ts and the column-identity ratchet.
      • The type-check, check:i18n-keys and the changeset checks pass.
      • Ablation: putting back the old ordering in both files turned all 4 new pins red, the 3 controls stayed green, and both restores were proven. The rollback-names-its-cause case turned red too: on the producer's shape the old ordering never reached that row.
    • Changeset: @object-ui/app-shell: patch.
    • First-load bytes:
      • The budget bot reads 3311.2 KB, a PASS. The dev measured main 989b190 locally at 3,390,585 B and this head at 3,390,680 B, +95 B.
      • The seat's sum with the PRs landing ahead of it (objectui#11977 about +0.3 KB, objectui#11979 about 0, objectui#11976 −3 B) is about 3,391,000 B, under the 3,391,484 B ceiling.
    • CI: 6226e58 is running. The PR lands through the merge queue when every check is green.

    Out-of-scope notes (carrier: none):

    • Both doors still treat outcome: 'nothing_to_publish' (success: false with an empty failed[]) as a failure, while Studio says there is nothing to publish (objectui#11807). This PR leaves it unchanged, as its envelope control. No reach was measured.
    • DraftChangesPanel's comment says the metadata-admin string table is off the console's eager graph, but services/builtinComponents.tsx imports PackagesPage statically. This was read from source only.

    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 seatfindingpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions