You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(client): bind packages.get to the bare InstalledPackage row on both clients (#12034) (#17419)
`client.packages.get` declared `{ package: any }` and its environment-scoped
twin declared `{ package: InstalledPackage }`. Neither body is emitted by any
surface: the runtime dispatcher's `/packages` domain has been the single
implementation of `GET /packages/:id` since #16628 removed the REST
registrar's read routes, and it answers the bare row —
`success(withWritableVerdict(qlService, toPackageResponse(pkg)))`, the same
expression it maps over every `list` row.
Maintainer ruling of 2026-09-09 (Option A) converged this route on the bare
row. The producer half arrived by subtraction rather than convergence, so this
change is SDK-side only.
Also corrects the rationale pinned beside the scoped binding, which reasoned
from a REST mount that #16628 deleted, and moves the scoped method's
direction-2 evidence to the pin function of this card, where it is red before
the fix rather than green in both states.
Clause-②: yes — a `.package` read compiles today and will not after this.
Claude-Session: https://claude.ai/code/session_01DapQyvYrFb1MxSYe7BL2nt
Co-authored-by: Claude <noreply@anthropic.com>
fix(client): `packages.get` binds the bare `InstalledPackage` row on both the global and the environment-scoped client, replacing a `{ package }` envelope no surface emits (#12034)
6
+
7
+
`client.packages.get(id)` and `ScopedEnvironmentClient.packages.get(id)` now resolve to **`InstalledPackage`** — the row itself — instead of an object wrapping it.
8
+
9
+
**Migration — read the row directly, not `.package`:**
FROM `{ package: any }` (global) and `{ package: InstalledPackage }` (scoped) TO `InstalledPackage` on both.
22
+
23
+
This is a **narrowing**: a `.package` read compiles today and stops compiling after this change. That is the point of the change rather than a side effect of it — the wrapper was never what the wire sent, so every one of those reads was already `undefined` at runtime, and on the global method the `any` member is what kept the falsehood invisible. Nothing about the request or the wire changes; only the declaration moves to match what the server has been sending.
24
+
25
+
Why it can be bound now, when #11925 deliberately left it erased: this route used to be served by two implementations that disagreed — the runtime dispatcher sent the bare row, the `@objectstack/rest` registrar sent `{ package }` — so no declaration was true on both. The registrar's read routes were removed in #16628, leaving the dispatcher's `/packages` domain as the single implementation. It builds the detail body with the same expression it maps over every `list` row, which is why this type now agrees with the `InstalledPackage[]` that `packages.list` has already declared, and with `GetInstalledPackageResponseSchema` in `@objectstack/spec`, which has declared `data: InstalledPackageSchema` all along.
26
+
27
+
The environment-scoped method is the sharper half of the change: its member was a real `InstalledPackage`, not `any`, so `.package` reads there looked type-safe while returning `undefined` against every surface that has served that path since #16628.
0 commit comments