|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +"@objectstack/runtime": patch |
| 4 | +--- |
| 5 | + |
| 6 | +fix(spec,runtime): `resolveService` returns the slot's contract too, and the `: any` escapes on core slots are gone (#4127) |
| 7 | + |
| 8 | +Batch 2 of the #4127 gate. #4168 typed `getService` — easy, because every one of |
| 9 | +its call sites already passed a `CoreServiceName`. `resolveService` is the mixed |
| 10 | +one, and it is where the remaining `any` lived. |
| 11 | + |
| 12 | +**Overloads split it exactly where the evidence does.** A `CoreServiceName` |
| 13 | +resolves to the slot's contract; anything else keeps `any`: |
| 14 | + |
| 15 | +- **Core slots, however written.** 17 call sites address a core slot with a bare |
| 16 | + literal — `'metadata'` ×10, `'automation'` ×3, `'auth'` ×3, `'ai'` — rather |
| 17 | + than `CoreServiceName.enum.*`. The same slot was being addressed two ways; |
| 18 | + both resolve to the contract now, with no edit to the call sites. |
| 19 | +- **Everything else** — `protocol` (×22), `objectql` (×9), `mcp`, |
| 20 | + `kernel-resolver`, `security`, `scope-manager`. Real services with no |
| 21 | + `CoreServiceName` entry and no written contract. They keep `any` rather than |
| 22 | + being given a shape here that nothing verifies: **that `any` is where the |
| 23 | + ledger honestly ends**, and writing those contracts is its own change. |
| 24 | + |
| 25 | +**The typing was being erased at three call sites, and that is the actual |
| 26 | +finding.** A `const x: any = await deps.resolveService('auth', …)` defeats every |
| 27 | +bit of this — the annotation wins, and #4168's work does nothing there. Sweeping |
| 28 | +for the pattern found three on core slots: |
| 29 | + |
| 30 | +**`/mcp` ×2 — two more undeclared methods.** The domain calls |
| 31 | +`authService?.getMcpResourceUrl?.()` and `?.getMcpResourceMetadataUrl?.()`. |
| 32 | +`AuthManager` implements both (and plugin-auth uses them internally); |
| 33 | +`IAuthService` declared neither. Classic #4127 shape — call site and |
| 34 | +implementation agree, the contract is the thing nobody wrote. |
| 35 | + |
| 36 | +The `: any` + optional-chaining combination made this *worse* than the earlier |
| 37 | +gaps, not better: it made the call invisible to the type system **and** |
| 38 | +accidentally safe. An absent method returns `undefined`, so the skill route |
| 39 | +silently fell back to deriving an MCP URL from the request host — meaning a real |
| 40 | +disagreement between the auth service's canonical value and the derived one |
| 41 | +would have looked exactly like normal operation. The whole point of |
| 42 | +`getMcpResourceUrl` is that it comes off the auth `basePath` so the two *cannot* |
| 43 | +disagree about the API prefix; the route's own comment says "the auth service |
| 44 | +owns the canonical value". |
| 45 | + |
| 46 | +Both are declared optional: an auth provider without MCP/OAuth support fills the |
| 47 | +slot legitimately, and `getMcpResourceMetadataUrl` returning `null` (OAuth track |
| 48 | +off — AS disabled or the origin fails the OAuth 2.1 transport rule) stays |
| 49 | +distinct from the method being absent. |
| 50 | + |
| 51 | +**`/packages` ×1 —** `const metadata: any = await deps.getService(…metadata)`, |
| 52 | +feeding `new SeedLoaderService(ql, metadata, …)`. Annotation dropped; it |
| 53 | +typechecks against `IMetadataService` now. Its neighbours `protocol` and `ql` |
| 54 | +keep their `any` for the honest reason above. |
| 55 | + |
| 56 | +No other core-slot lookup is annotated away — the sweep is exhaustive over |
| 57 | +`domains/*.ts`. |
| 58 | + |
| 59 | +Verified: `@objectstack/runtime` **937 tests / 65 files**, `@objectstack/spec` |
| 60 | +**7112 / 273** (3 new on the auth contract), adapter-hono **73**; `tsc --noEmit` |
| 61 | +on spec, runtime, downstream-contract and all four examples; `pnpm lint`; all |
| 62 | +nine `check:*` gates. `api-surface.json` is unchanged — the two additions are |
| 63 | +interface MEMBERS, not new exports. |
0 commit comments