Skip to content

fix(app-shell): Browse Marketplace reads an unreachable catalog as unreachable, its raw cause behind More - #26213

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-25891-marketplace-unreachable-copy
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-25891-marketplace-unreachable-copy

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #25891

Clause-②: no
Metadata-protocol: none

What changed

The runtime's marketplace proxy answers 502 MARKETPLACE_PROXY_FAILED only when its own fetch to the catalog throws (packages/cloud-connection/src/marketplace-proxy-plugin.ts). Upstream answers pass through with their own status and code. marketplaceApi.ts call() already stamps code on the error. The load-error panel in MarketplacePage.tsx never read it: it picked its class line by status alone. So a runtime that could not reach the catalog read "The marketplace catalog answered with an error (HTTP 502)", with the raw Node text fetch failed in full view.

  • LoadFailure now carries code and an unreachable class: no status (no server answered), or code === 'MARKETPLACE_PROXY_FAILED'.
  • The unreachable class reads marketplace.load.unreachable. Its raw transport text (fetch failed, Failed to fetch) sits behind a More / Less disclosure with a copy button, reusing the existing refusalDetails.more/less/copy/copied keys, the same shape RefusalMessage gives a refusal's detail. RefusalMessage itself is not reused because it splits a server text into lead + rest, and here the lead is our own sentence, not part of the text.
  • Answered failures (a 403, or a 502/503/504 relayed from upstream) keep answeredError with their status and the server's text in full view, as objectui#11688 made them.
  • The online / same-origin hint logic is unchanged.
  • No locale pack is edited: only existing keys are used.

Tests

ui/packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.loadFailureCause-11688.test.tsx, real call() over a stubbed fetch:

  • flipped: the proxy's 502 MARKETPLACE_PROXY_FAILED now reads marketplace.load.unreachable, never answeredError; fetch failed is not on screen until More is pressed, then it is, More reads Less, and the copy button is labelled refusalDetails.copy. The configured-plane hint is still drawn.
  • new controls: a relayed 503 SERVICE_UNAVAILABLE and a 403 plain-text refusal both keep answeredError, their text in full view, and no disclosure. The 503 keeps the hint and the 403 does not.
  • the no-answer case (TypeError: Failed to fetch) also asserts the unreachable line and that the raw text is not in full view.

Run at head a7b293f0, under the verify lock: vitest run packages/app-shell/src/console/marketplace/__tests__/MarketplacePage*: Test Files 9/9, Tests 67/67. The suites include MarketplacePage.sameOriginHint-11726.test.tsx and MarketplacePage.loadingStates-12710.test.tsx unchanged and green. pnpm --filter @object-ui/app-shell run type-check passed with exit 0 (both tsc --noEmit and tsconfig.test.json). eslint on the two changed files reported 0 errors and 3 warnings, all on pre-existing lines.

Ablation, run at head 24315db2 via scripts/ablation-replace.mjs: unreachable: status === undefined || code === PROXY_UNREACHABLE_CODE, became unreachable: status === undefined,. The anchor count went from 1 to 0 and the blob from f19da1750ab1 to 46ac6aa14f87. The 11688 suite then had 1 failed and 9 passed; the failure was exactly the flipped 502 case. The tool then restored the file (blob equals HEAD, and git diff HEAD is empty).

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at a7b293f0: 35 commands, every one run, every one exit 0. --ran reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero, exit codes recorded). node scripts/check-sdui-manifest.mjs passed with exit 0, and scripts/sdui-manifest.* is untouched. The roster gate flagged for .changeset/, node scripts/check-changeset-fixed.mjs, passed with exit 0. CI-owned families (the type-check lanes, Test UI, Lint UI) are left to this PR's check-runs.

Acceptance notes

  • The card's literal copy names the catalog host. That needs a new structured wire field on the proxy's failure, plus new pack keys. It is out of this claim, as the claim's Premise records, and is a follow-up if wanted. Today no failure the proxy returns carries the upstream host in a field or a header, and the page never parses server prose for one (objectui#11726).
  • MARKETPLACE_PROXY_FAILED is compared as a string literal in app-shell. The spec's error-code ledger lists it, but there is no per-code exported constant to import.
  • Open PR feat(app-shell,i18n): the Console draws the DEPENDENCY_MISSING install refusal in the author's terms #26123 edits other regions of MarketplacePage.tsx (an import near middleTruncate.js, a hook, installLocal). This diff stays out of those regions.

Generated by Claude Code

…ble 502 as unreachable

The runtime's marketplace proxy answers 502 MARKETPLACE_PROXY_FAILED only
when its own fetch to the catalog throws. The panel picked its class line
by status alone, so that 502 read as the catalog answering with an error,
and the raw transport text (fetch failed) sat in full view. The load
failure now carries the stamped code: that code, or no status at all, is
the unreachable class, whose raw text sits behind a More / Less disclosure
reusing the refusalDetails keys. Answered failures keep answeredError and
their visible server text; the online hint is unchanged.

Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:ui seat 2 · session_01SMps7wq7rBTk53e8KTSEup · 2026-10-26T15:51Z · head a7b293f0

  1. 形态 (shape): a draft PR into main, assigned to os-steve. The body leads with Fixes #25891. Clause-②: no and Metadata-protocol: none lead their lines in the body and in the changeset. no is correct: the change is page-local and no published signature moves. The PR touches 3 files with +161/−20: MarketplacePage.tsx, the 11688 test and the root .changeset/. That matches the claim's surface exactly. No locale pack, marketplaceApi.ts, packages/cloud-connection or sdui file is touched.
  2. 前提实测 (premise, measured): the seat read the diff at this head.
    • toLoadFailure now reads the code that call() already stamps. The unreachable class is status === undefined || code === 'MARKETPLACE_PROXY_FAILED', which is the proxy's own throw-only answer.
    • That class shows marketplace.load.unreachable. The raw transport text moves behind a More/Less disclosure with a copy button, built only from the existing refusalDetails.more/less/copy/copied keys.
    • Answered failures keep answeredError and their visible server text (objectui#11688). These are a 403, and a 502, 503 or 504 relayed from upstream with its own code.
    • Server text stays a text child.
    • The online-hint logic (UNREACHABLE_STATUSES) is unchanged, so sameOriginHint-11726 and loadingStates-12710 stay green.
    • Ablation: dropping the code arm turned exactly the flipped proxy-502 case red.
  3. 门禁 check-runs (gates): CI was in progress when I read it. Landing waits until every check is green.
  4. 接受的偏差 (accepted deviations):
    • Extra pins on the no-answer case, which also moved its raw text behind More. That pin is required by the behaviour change, so it is accepted.
    • The copied key is reused. Accepted, because it is an existing key.
    • The ablation ran at the pre-merge head, and the merge brought only unrelated commits. Accepted.
  5. 范围外去向 (out of scope):
    • Open question (share a disclosure with RefusalMessage through a lead prop): option A, keep the local component. No follow-up card until a third surface needs the same lead-plus-raw-detail shape.
    • The card's literal copy that names the catalog host stays out of scope, as the claim's Premise rules. It would need a new wire field on the proxy envelope and new pack keys. If it is ever wanted, it gets its own card.
  6. 落地条款 (landing terms):

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat 2 · 2026-10-26T16:00Z · CI red that is not this PR's

Test UI (8/16) fails ui/packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts:161 with expected 48 to be 47 (check-run 121679452481). This PR touches only the marketplace page, its test and a changeset. The failing guard counts ObjectSchema's top-level keys, and #26005 (5e113b3) added sharing on main. Every ui PR's merge ref fails it the same way.

The fix is card #26218, claimed and in development: a test-only re-point of the guard to the measured count. Nothing is ported into this PR, so its diff stays as reviewed. Once #26218 merges, a merge of main into this branch re-runs CI. The seat's ACCEPT carries over that merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 17:17
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 17:17
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit b239a01 Oct 26, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-25891-marketplace-unreachable-copy branch October 26, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

2 participants