Filed by the domain:cli PM seat in objectstack-ai/objectstack at ACCEPT time for objectstack-ai/objectstack#9267, as the consuming-side follow-up. Unassigned, not started.
Blocked-by: objectstack-ai/objectstack#9267
What changes upstream
objectstack-ai/objectstack#9267 makes POST /api/v1/cloud-connection/bind/poll emit a body that satisfies its own declared contract. On a device-authorization failure the envelope goes from:
to:
The RFC 8628 spelling was never a member of the closed ApiErrorSchema.code vocabulary, so stamping it into code produced a body that failed its own schema. It now rides declaredCode, the open producer-authored channel ADR-0112 declares for exactly that case.
Why this repo is affected — and why it is NOT a break
packages/app-shell/src/console/cloud-connection/CloudConnectionPanel.tsx is the only consumer of this route outside the framework's own tests. Verified against origin/main here:
- Control flow is safe. The polling state machine keys on
body?.data?.pending (line ~120), and the upstream change deliberately keeps data.pending: false on the failure body for exactly this reason. Nothing about the loop changes. - The panel does not branch on any code value — no
=== 'expired_token' comparison exists. It only displays the code.
The actual (cosmetic) consequence
Line ~128 renders the failure as:
setPhase({kind: 'error',message: body?.error?.code??t('cloudConnection.errors.bindFailed')});Reading error.code directly, so after the upstream change a user sees DEVICE_CODE_FAILED where they previously saw expired_token. Both are machine codes shown to a human — the upstream card names that accommodation as a symptom of the missing message, not a feature.
The fix
Read the readable half, which the wire now actually carries:
message: body?.error?.message??body?.error?.code??t('cloudConnection.errors.bindFailed')⭐ This file already uses that exact precedence in its own getJson helper (line ~76: body?.error?.message ?? body?.error?.code ?? body?.error ?? …). So this is bringing one call site in line with the house pattern already present in the same file, not introducing a convention.
Worth checking while there: whether cloudConnection.errors.* should now prefer a translated string over any machine code at all, given a human-readable message is available on the wire. That is a UX call for this repo, deliberately not decided here.
Sequencing
⛔ Do not land before objectstack#9267 merges — until then the wire still sends expired_token in code, and reading message first would show the old body's undefined. There is no pin-file coupling in this direction, so the upstream merge is the only gate.
Generated by Claude Code
Filed by the
domain:cliPM seat inobjectstack-ai/objectstackat ACCEPT time for objectstack-ai/objectstack#9267, as the consuming-side follow-up. Unassigned, not started.Blocked-by: objectstack-ai/objectstack#9267
What changes upstream
objectstack-ai/objectstack#9267 makes
POST /api/v1/cloud-connection/bind/pollemit a body that satisfies its own declared contract. On a device-authorization failure the envelope goes from:{ "success": false, "error": { "code": "expired_token" } } // beforeto:
{ "success": false, "data": { "pending": false }, "error": { "code": "DEVICE_CODE_FAILED", "declaredCode": "expired_token", "message": "Device authorization failed: expired_token" } }The RFC 8628 spelling was never a member of the closed
ApiErrorSchema.codevocabulary, so stamping it intocodeproduced a body that failed its own schema. It now ridesdeclaredCode, the open producer-authored channel ADR-0112 declares for exactly that case.Why this repo is affected — and why it is NOT a break
packages/app-shell/src/console/cloud-connection/CloudConnectionPanel.tsxis the only consumer of this route outside the framework's own tests. Verified againstorigin/mainhere:body?.data?.pending(line ~120), and the upstream change deliberately keepsdata.pending: falseon the failure body for exactly this reason. Nothing about the loop changes.=== 'expired_token'comparison exists. It only displays the code.The actual (cosmetic) consequence
Line ~128 renders the failure as:
Reading
error.codedirectly, so after the upstream change a user seesDEVICE_CODE_FAILEDwhere they previously sawexpired_token. Both are machine codes shown to a human — the upstream card names that accommodation as a symptom of the missingmessage, not a feature.The fix
Read the readable half, which the wire now actually carries:
⭐ This file already uses that exact precedence in its own
getJsonhelper (line ~76:body?.error?.message ?? body?.error?.code ?? body?.error ?? …). So this is bringing one call site in line with the house pattern already present in the same file, not introducing a convention.Worth checking while there: whether
cloudConnection.errors.*should now prefer a translated string over any machine code at all, given a human-readablemessageis available on the wire. That is a UX call for this repo, deliberately not decided here.Sequencing
⛔ Do not land before objectstack#9267 merges — until then the wire still sends
expired_tokenincode, and readingmessagefirst would show the old body'sundefined. There is no pin-file coupling in this direction, so the upstream merge is the only gate.Generated by Claude Code