Skip to content

fix(client): retire the pre-#3842 parking-spot read; category/retryable read the declared spot (#4006, #4007) - #4016

Merged
os-zhuang merged 2 commits into
mainfrom
claude/dispatcher-http-status-code-rhryfw
Jul 30, 2026
Merged

fix(client): retire the pre-#3842 parking-spot read; category/retryable read the declared spot (#4006, #4007)#4016
os-zhuang merged 2 commits into
mainfrom
claude/dispatcher-http-status-code-rhryfw

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Executes the maintainer ruling on #4007delete 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:

  1. One release train. SDK and server ship as a changesets fixed group; the pairing is not a supported deployment. The objectui#2869 analogy the old comment leaned on does not carry: the console is a platform component with its own pin/release track (.objectui-sha), while the client SDK upgrades in the same package.json as the server it talks to.
  2. Location-compat without value-compat protects nothing. ADR-0112 batches 1–2 renamed the code values. A code dug out of an old server's parking spot (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:

Read What it is Fate
errorBody.code flat envelope's declared spot (@objectstack/rest, live today) stays
errorBody.error.details.code pre-#3842 parking spot — dug only when talking to a pre-#3842 server deleted
errorBody.error.code wrapped envelope's declared spot (dispatcher, live today) stays

The 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's splitSemanticCode is move-semantics, so no current server emits error.details.code — the deleted read was undefined against every live surface.

D9b (#4006)

err.category / err.retryable were read from the body top level, where no envelope ever put them — undefined against every conformant server. They now read from inside error, where ApiErrorSchema declares 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/retryable read errorBody.error.*.
  • packages/client/src/client.test.ts — the "older server, newer SDK" pin flips to pin the retirement (legacy shape → code undefined, 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 thrown code into error.code; details carries fields only (the details.code wording 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 (403PERMISSION_DENIED), the rider @objectstack/client reads category/retryable from the body top level — the contract puts them inside error (ADR-0112 D9b, unowned after the batch split) #4006 called for.
  • Changeset: @objectstack/client minor.

Verification

  • @objectstack/client: 14 files, 200/200 green; tsc --noEmit clean; eslint clean.
  • @objectstack/spec: 6890/6890 green (error-catalog headings lock unaffected).
  • check:doc-authoring, check:release-notes green.
  • Behavior against every current server pairing: unchanged (the deleted read was dead on live wires); category/retryable go from never-populated to populated.

Closes #4006. Closes #4007.

🤖 Generated with Claude Code

https://claude.ai/code/session_018u8vDQLWPJxBWQESznDpSj


Generated by Claude Code

…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
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 2:58am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling and removed size/m labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/client.

13 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/client)
  • content/docs/api/client-sdk.mdx (via @objectstack/client)
  • content/docs/api/data-flow.mdx (via @objectstack/client)
  • content/docs/api/environment-routing.mdx (via @objectstack/client)
  • content/docs/api/error-catalog.mdx (via @objectstack/client)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/client)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/client)
  • content/docs/kernel/runtime-services/index.mdx (via packages/client)
  • content/docs/permissions/authentication.mdx (via @objectstack/client)
  • content/docs/plugins/packages.mdx (via @objectstack/client)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/client)
  • content/docs/releases/implementation-status.mdx (via @objectstack/client)
  • content/docs/releases/v16.mdx (via @objectstack/client)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…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
@os-zhuang
os-zhuang marked this pull request as ready for review July 30, 2026 03:20
@os-zhuang
os-zhuang merged commit cbd5181 into main Jul 30, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/dispatcher-http-status-code-rhryfw branch July 30, 2026 03:20
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 tests tooling

Projects

None yet

2 participants