fix(client): retire the pre-#3842 parking-spot read; category/retryable read the declared spot (#4006, #4007) - #4016
Merged
Conversation
…le read the declared spot (#4006, #4007) - err.code no longer falls back to error.details.code. The "newer SDK, older server" pairing that read served is not a supported deployment (SDK and server ship as a changesets fixed group), and ADR-0112 batches 1-2 renamed the code VALUES anyway — a code dug out of an old server's parking spot matches no branch written against the current catalog. Location-compat without value-compat protects nothing (#4007 maintainer ruling; ADR-0112 D5 amended in place). - err.category / err.retryable now read from inside error, where ApiErrorSchema declares them; the old top-level read yielded undefined against every conformant server (ADR-0112 D9b, #4006). - Tests: the "older server, newer SDK" pin flips to pin the retirement; two new cases cover the D9b reads (present and absent). - Docs: client-sdk.mdx / wire-format.mdx stop describing the retired fallback; error-catalog.mdx's actions rejection example corrected to the real wire (envelope assembly promotes the thrown code into error.code — details carries fields only); v17.mdx notes the retirement and uses post-rename spelling in the derived-map example. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018u8vDQLWPJxBWQESznDpSj
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 13 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…TION_FAILED, and category/retryable are optional The example printed 'VALIDATION_ERROR' where data.create's validation failure actually emits VALIDATION_FAILED (the registered extension code; the batch-1 rename re-spelled the example's stale value without fixing it), and showed category/retryable as populated when no server surface emits either field today — they are optional contract fields the SDK now reads from the declared spot (#4006), set only when the server sends them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018u8vDQLWPJxBWQESznDpSj
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.
Executes the maintainer ruling on #4007 — delete now (option A) — and lands #4006 in the same diff, as that issue's own text anticipated.
The ruling, and why it is right
The probe's legacy read protected the "newer SDK, older server" pairing. Two facts kill it:
.objectui-sha), while the client SDK upgrades in the samepackage.jsonas the server it talks to.permission_denied, old catalog) matches no branch written against the current catalog (PERMISSION_DENIED) — so in the one scenario the read existed for, it delivered a value nobody can branch on.Scope precision (narrower than the issues' shorthand)
#4006/#4007 said "three-location probe"; the actual chain is:
errorBody.code@objectstack/rest, live today)errorBody.error.details.codeerrorBody.error.codeThe two survivors are the two live envelopes' declared spots — a present-tense fact, not a fallback chain. Retiring the flat shape itself belongs to the envelope-convergence line (#3843 family), not here. Verified before deleting:
buildApiError'ssplitSemanticCodeis move-semantics, so no current server emitserror.details.code— the deleted read wasundefinedagainst every live surface.D9b (#4006)
err.category/err.retryablewere read from the body top level, where no envelope ever put them —undefinedagainst every conformant server. They now read from insideerror, whereApiErrorSchemadeclares them. Per the #4007 ruling's own logic, they read the declared spot only — no top-level fallback to a location that has no producer (#4006 sketched a two-shape read; this is the deliberate tightening, same rationale as the deletion above).Changes
packages/client/src/index.ts— delete the parking-spot read; rewrite the comment block (the "NOT debt" argument is retired with the read);category/retryablereaderrorBody.error.*.packages/client/src/client.test.ts— the "older server, newer SDK" pin flips to pin the retirement (legacy shape →codeundefined, everything else still normalises); "all three envelopes" becomes both live envelopes; two new cases pin the D9b reads (present and absent). 200/200 green (was 196).docs/adr/0112-error-code-vocabulary-and-ledger.md— D5 amended in place: ruled and done, with the reasoning and the scope note, so the next agent working from the ADR sees the settled state instead of re-deriving either side.content/docs/api/client-sdk.mdx,wire-format.mdx— stop describing the retired fallback.content/docs/api/error-catalog.mdx— corrects the actions-rejection example to the real wire: envelope assembly promotes the throwncodeintoerror.code;detailscarriesfieldsonly (thedetails.codewording came from fix(actions)!: failures speak HTTP — rejections are 400, success is a single wrap (#3962) #3969's shorthand).content/docs/releases/v17.mdx— notes the retirement in the SDK entry; the derived-map example uses the post-rename spelling (403→PERMISSION_DENIED), the rider@objectstack/clientreadscategory/retryablefrom the body top level — the contract puts them insideerror(ADR-0112 D9b, unowned after the batch split) #4006 called for.@objectstack/clientminor.Verification
@objectstack/client: 14 files, 200/200 green;tsc --noEmitclean; eslint clean.@objectstack/spec: 6890/6890 green (error-catalog headings lock unaffected).check:doc-authoring,check:release-notesgreen.category/retryablego from never-populated to populated.Closes #4006. Closes #4007.
🤖 Generated with Claude Code
https://claude.ai/code/session_018u8vDQLWPJxBWQESznDpSj
Generated by Claude Code