Files
suaveplan/openspec/archive/2026-05-26-foundation-fix-gap-4-db-split/proposal.md
2026-05-27 23:29:53 +01:00

104 lines
4.6 KiB
Markdown

# foundation-fix-gap-4-db-split
- **Status**: PROPOSED
- **Spec deltas**:
- ./specs/api-host/spec.md
- ./specs/api-types/spec.md
## What & why
The post-Stage-4 foundation-coherence audit (Gap #4) flagged that
`HostAdapters.db` was shaped for the narrow `LedgerDb` interface
(`bun:sqlite`-style `prepare(sql)` / `run(sql)`) consumed by the
host-internal `PluginLedger`, while `@suaveplan/api-db-adapter`
produces a multi-engine `AdminDatabaseLike` façade plugins consume.
The two surfaces are type-incompatible — there is no safe way to
share a single `adapters.db` slot. The E2E foundation-stack server
documented the gap explicitly (`@suaveplan/api-db-adapter` was
swapped out for a raw `bun:sqlite` handle to satisfy the ledger).
This change splits the slot:
1. **`HostAdapters.ledgerDb: LedgerDb`** — host-private, narrow
SQLite handle the `PluginLedger` keeps using. Renamed from the
former `HostAdapters.db`.
2. **`HostAdapters.db?: AdminDatabaseLike`** — optional,
consumer-facing. Populated by `createDbAdapter(...).admin` from
`@suaveplan/api-db-adapter`. The host threads it onto
`PluginContext.db` (admin-scoped) and applies
`createTenantScopedWrapper(admin, tenantId)` per request to
expose it on `RequestContext.db` (tenant-scoped).
3. **`PluginContext.db: AdminDatabaseLike | null`** + **`RequestContext.db: TenantScopedDatabaseLike | null`** added as
top-level fields. `null` when (a) the host was booted without
`adapters.db` configured, or (b) for `RequestContext` only, the
tenant resolver chain returned `null` on this request.
4. **E2E foundation-stack** restores `createDbAdapter({ engine:
"sqlite", url: ":memory:" })` as `adapters.db` and adds a
`GET /api/db-ping` route exercising `ctx.db?.execute` +
`ctx.db?.query` through the tenant-scoped wrapper. A
companion test asserts 200 + non-empty rows.
## Impact
### Affected dependents
- `@suaveplan/api-types` — `PluginContext` / `RequestContext` /
`JobContext` / `ConnectionContext` each gain a `db` field. Source-
incompatible for any consumer constructing a `PluginContext`
literal — but the only in-tree literal consumer is the host's own
test harness (`{} as never` mock), which already type-erases.
- `@suaveplan/api-host` —
- `HostAdapters.db` renamed to `ledgerDb`; new optional
`HostAdapters.db: AdminDatabaseLike`.
- Host config Zod schema updated.
- `RequestPipelineOptions` gains optional `db: AdminDatabaseLike`.
- `buildRequestContext` accepts + propagates `db`.
- Request-pipeline driver applies `createTenantScopedWrapper`
when both `adapters.db` is set AND a tenant is resolved.
- New runtime dep `@suaveplan/api-db-adapter`.
- `@suaveplan/e2e-foundation-stack` — wires `createDbAdapter` +
registers an inline `bun:sqlite` engine factory; adds the
`/api/db-ping` route + an E2E test.
Blast radius (atomic update list in this commit):
- `packages/api-stack/api/api-types/src/plugin-context/plugin-context.types.ts`
- `packages/api-stack/api/api-host/src/host/host.types.ts`
- `packages/api-stack/api/api-host/src/host/host.ts`
- `packages/api-stack/api/api-host/src/host/host.test.ts`
- `packages/api-stack/api/api-host/src/request-pipeline/request-pipeline.types.ts`
- `packages/api-stack/api/api-host/src/request-pipeline/request-pipeline-driver.ts`
- `packages/api-stack/api/api-host/src/request-pipeline/request-pipeline-helpers.ts`
- `packages/api-stack/api/api-host/package.json`
- `e2e/foundation-stack/src/server.ts`
- `e2e/foundation-stack/src/fixture-plugin.ts`
- `e2e/foundation-stack/tests/foundation.spec.ts`
- `e2e/foundation-stack/package.json`
### Backward compatibility
The `HostAdapters.db → HostAdapters.ledgerDb` rename is a hard rename
(no compatibility shim) because the genesis branch has no shipped
consumers outside the in-tree atomic update list above.
`PluginContext.db` / `RequestContext.db` are NEW fields, additive at
the type level for any future consumer.
## Parity Checklist
This is a genesis-original fix; no upstream parity table required.
## Verification
`bunx turbo lint typecheck test build --filter=@suaveplan/api-host --filter=@suaveplan/api-types --filter=@suaveplan/api-db-adapter --filter=@suaveplan/e2e-foundation-stack`
→ green on api-host / api-types / api-db-adapter. (The e2e package's
typecheck error is a pre-existing module-resolution issue unrelated
to this change — confirmed via git-stash baseline.)
`bunx turbo test:coverage --filter=@suaveplan/api-host --filter=@suaveplan/api-types`
→ 100% lines on touched modules (statements/branches/functions
unchanged at baseline).
E2E suite: `bun test:e2e` → 11/11 pass, including the new
`db adapter — ctx.db wired through tenant-scoped wrapper` test.