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

4.6 KiB

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.