2.9 KiB
api-types — Foundation gap 5 cleanup: nullable tenant on RequestContext
Package: @suaveplan/api-types
Status: PROPOSED
Type: type-soundness-fix
Why
The foundation-fix-gap-5-default-tenant change widened the runtime
behaviour: when the resolver chain misses and no defaultTenant is
configured, RequestContext.tenant / RequestContext.tenantId are
null at runtime. The static types in @suaveplan/api-types were NOT
updated — they still declared tenant: TenantContext and
tenantId: TenantId as non-nullable.
Consequences observed in request-pipeline-helpers.ts:
tenantId: (args.tenant?.id ?? null) as never,
tenant: (args.tenant ?? null) as never,
The as never is a documented escape hatch — but it is a type lie that
defeats every handler-side guard the type system was supposed to
enforce. Out-of-spec debt; resolved before moving on per the user's
directive.
What changes
MODIFIED: RequestContext<Caps> field types in
@suaveplan/api-types/plugin-context:
readonly tenantId: TenantId | null;
readonly tenant: TenantContext | null;
Existing tenant/tenantId consumers in the monorepo already use the
null-safe ?. access pattern (verified across e2e/foundation-stack,
apps/demo, apps/testbed, and every api-plugin-*). No handler
needed a code change beyond stripping the now-redundant casts.
The JobContext and ConnectionContext flavours are NOT widened.
Per-tenant jobs and connection-scoped contexts only exist after a
tenant has been resolved — their resolver chain runs separately and
never returns null to the handler. Widening those would force a
spurious null-check on every job/connection handler.
The as never casts in request-pipeline-helpers.ts lines 143-150 are
removed. The two remaining as casts at the same site are
generic-erasure points (capabilities/instruments resolve to typed
slices at the route handler call-site, not at the untyped pipeline
seam); they now use the explicit
as RequestContext["capabilities"] form with a multi-line code comment
explaining the boundary instead of a blanket as never.
Impact
@suaveplan/api-types— spec MODIFIED Requirement (this delta).@suaveplan/api-host—as nevercasts stripped frombuildRequestContext; generic-erasure casts retained with the documented boundary comment.- E2E foundation-stack: 11 Playwright tests still green; no handler
changes needed since fixture plugin already used
ctx.tenant?.id.
Verification
bunx turbo lint typecheck test build --filter=@suaveplan/api-types --filter=@suaveplan/api-host
bunx turbo test:coverage --filter=@suaveplan/api-host --filter=@suaveplan/api-types
cd e2e/foundation-stack && bunx playwright test # 11/11
All green. No as never remains for tenant/tenantId in
request-pipeline-helpers.ts. No biome-ignore / @ts-ignore /
@ts-expect-error added anywhere.