fix(app-shell): PackageFormDialog reads the producer-marked error.userMessage - #8052
Merged
os-steve merged 1 commit intoSep 6, 2026
Merged
Conversation
The package create/edit dialog's own `apiJson` held a fourth copy of the ADR-0112 failure-envelope ladder — character for character the one `PackagesPage` had before objectui#7959. It read the diagnostic `error.message` and stopped, so a producer-marked `error.userMessage` (objectstack#9934, emitted by both doors that serve `/api/v1/packages`) arrived on the wire with nowhere to appear, and `error.code` never reached the author at all. In the 5xx band the door substitutes the generic `Internal server error` into `message` while the mark rides through untouched, so a marked 500/503 showed the author the generic sentence and discarded the specific one written for them. The read now comes from the one shared rule, `readEnvelopeFailureText`. The two rungs below it stay here: a bare-string `error` and a top-level `message` are older runtimes' shapes, not this envelope, and they are live for this call site alone. Pinned over the four combinations (message only / userMessage only / both / neither) plus the code interaction, matching the shape of #7959's pin. The message-only case is deliberately code-less so it stays green with the fix reverted: it is what stops "prefer userMessage" from being implemented as "read userMessage instead", which would blank every ordinary refusal on the wire today. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnpvbdoRisQdRAczkLwnf5
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-steve
marked this pull request as ready for review
September 6, 2026 13:27
os-steve
deleted the
claude/issue-7979-packageformdialog-envelope-reader
branch
September 6, 2026 13:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7979
PackageFormDialog's ownapiJsonheld the FOURTH copy of the ADR-0112 failure-envelopeladder — character for character the one
PackagesPagehad before objectui#7959:It read the diagnostic and stopped, so a producer-marked
error.userMessage(objectstack#9934,emitted by BOTH doors that serve
/api/v1/packages) arrived on the wire with nowhere to appear,and
error.codenever reached the author at all. In the 5xx band the producing door substitutesthe generic
Internal server errorintomessagewhile the mark rides through untouched — so ona marked 500/503 this dialog showed the author the generic sentence and discarded the specific one
written for them. Create/edit is exactly where an author meets a refusal that names what to fix.
The change
packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx— the envelope read nowcomes from the ONE shared rule,
readEnvelopeFailureText(utils/apiErrorEnvelope.ts, landedwith PR objectui#7981), which prefers the mark over the diagnostic and appends the declared code
behind whichever prose won.
errorand atop-level
messageare older runtimes' shapes, not this envelope, live here and for no otherconsumer of the rule. Folding them into the shared helper would hand every other consumer a
tolerant dialect it never asked for. Pinned in §6.
data-testid="package-form-error"so the pin can read exactly what theauthor is shown, mirroring
packages-load-erroron the sibling page. No other behaviour change.narrowing by band would re-create at the reading end the divergence the writing end refuses to
create (already ruled on objectui#7938).
The pin — shape copied from objectui#7981, not invented
PackageFormDialog.envelopeUserMessage.test.tsx, 16 cases: the four combinations (§1 message only/ §2 userMessage only / §3 both / §4 neither), the
codeinteraction (§5, five cases including anon-string and an empty-string mark), the legacy rungs (§6), and the 200 that declares failure (§7).
⭐ §1 "message only" is deliberately code-less and stays GREEN with the fix reverted. The file
says why in as many words: this reader already rendered
error.message, so the unmarked refusal —everything on the wire today — must come through byte for byte. It is the pin that stops "prefer
userMessage" from being implemented as "readuserMessageINSTEAD", which would blank everyordinary refusal this dialog serves.
over: no case here uses 403 or 409. Unlike the page, this dialog's
catchhas two STATUS-drivenarms in front of the banner (409 to the localized "already exists" copy, 403 to the localized
capability copy, objectstack#8270), so on those two statuses the envelope prose is not what the
person reads whatever the ladder returned. Every ladder case therefore uses a status that reaches
the
elsearm, and §8 pins the two arms as untouched by this card.Ablation — predicted first, then measured, no delta
objectui#7959 measured that
fetchPackages's message-only pin goes RED on revert (that readernever opened the body at all) while the
apiJsontwin keeps the classic green-when-reverted guard.This site is
apiJson-shaped, so theapiJsonbehaviour was predicted before running it.Method: fix committed first, then the single rung mutated on disk (
readEnvelopeFailureText(payload)to
payload?.error?.message), occurrence counts taken both directions as proof it landed(
readEnvelopeFailureText(payload)1 to 0,payload?.error?.message0 to 1), run, then restored viagit checkout HEAD -- pathunder anEXIT INT TERMtrap and proven byte-identical(
git hash-object=41e9eca…= the HEAD blob;git diff HEADempty).Measured: 9 failed | 7 passed (16) — exactly the prediction, case for case.
Restored tree re-run: 16 passed (16).
Verification (objectui has no
dispatch-gates.mjs; gates derived by hand from the workflows)All exit codes captured before any pipe. Final commit
82ddb12.pnpm exec vitest runover the blast radius — every test file that renders this dialog plus theenvelope family (
PackageFormDialogx4,CreatePackageDialog.namespace,EditPackageDialog,PackagesPage.envelopeUserMessage,apiErrorEnvelope,packages-iox3): 11 files, 109 tests,all passed.
pnpm --filter @object-ui/app-shell type-check(tsc --noEmit+tsc -p tsconfig.test.json,whose
includeissrc/**/*.test.ts(x)— so the new pin IS type-checked): exit 0. It firstfailed with
TS2307 Cannot find module '@object-ui/*'until the dependency closure was built(
pnpm --filter '@object-ui/app-shell^...' build, exit 0); recorded because that failure readsexactly like a broken import.
pnpm --filter @object-ui/app-shell lint: exit 0 (2894 pre-existing warnings, 0 errors —ESLint fails on errors only here). Targeted
eslint --format jsonon the two changed files:2 files linted, 0 errors, 3 warnings, all on pre-existing lines (82, 133, 228). Repo-wide
ESLint is CI's run, not this seat's; the config enables no type-aware linting (no
parserOptions.project/projectServiceineslint.config.js), so this diff cannot move theverdict on any untouched file.
check:control-bytes,check:vi-mock-specifiers,check:vi-mock-inherit,check:i18n-keys,check:i18n-drift,check:unreferenced-sources,check:doc-example-readers,lint:coverage,check-changeset-presence.mjs,check-changeset-no-major.mjs— all exit 0.Plus a control-byte self-scan over the three changed paths: no hits.
not the whole package. The full package (and every other gate in the farm) is CI's run.
Changeset
.changeset/7979-package-form-dialog-envelope-reader.md,@object-ui/app-shell: patch(user-visiblefix; never
majorper the fixed-group rule).skip-changesetis inert in this repo and was notapplied.
Filed, not fixed here
objectui#8051 — the same file's 409 / 403 localized arms still discard a marked
userMessage, andthose are the two most likely refusals for create/edit. Resolving it means choosing between the
mark and the localized posture objectstack#8270 deliberately put there, which is a product decision
about the ARMS, not about the ladder. Out of this card's declared file surface; no assignee.
Seat session:
session_01QnpvbdoRisQdRAczkLwnf5.Generated by Claude Code