Decision records
ADR 0033 — SPI consolidation + conformance contracts
- Status: Accepted (2026-07-10) — accepted item #6 in the Execution-OS evaluation (
loom/docs/requirements/2026-07-10-execution-operating-system-evaluation.md). Implemented: C1 the SPI map (loom/docs/loom-2.0/12-extension-seams.md); C2runEventStoreConformance/runSnapshotStoreConformance/runBranchRegistryConformancein@loom/testing, self-tested against in-memory + SQLite backends. Added a referenceInMemoryBranchRegistry. - Date: 2026-07-10
- Builds on: ADR 0016 (K1 kernel ABI + conformance test, K5 event-schema governance), the existing store / dispatcher / worker / effect / scheduler SPIs
Context
Loom’s extensibility (“Kernel Extensions” + “Runtime Contracts”, P3) is mostly shipped: there are already pluggable SPIs for event store, snapshot store, branch registry, dispatcher, worker runtime, effect, scheduler, and warmer, and K1/K5 give the syscall surface a versioned contract plus a conformance test. Two gaps remain:
- The SPIs are documented unevenly — each in its own package, no single map of “here is every replaceable seam and its contract.”
- Only the K1 syscall ABI and K5 event schema have conformance tests. The placement, memory, projection, and capability SPIs have interfaces but no portable test suite a third-party implementation can run to prove it conforms.
Decision
- C1 — one SPI map. A single doc (
docs/loom-2.0/) enumerating every extension seam (scheduler, placement, memory, decision, projection, transport, persistence, capability, auth, policy, tracing), its interface, its invariants, and its reference impl — the “kernel extension model” the requirements asked for, as documentation over the SPIs that already exist. - C2 — conformance kits. Extend the K1/K5 conformance-test pattern to each SPI: a shared
@loom/testingsuite (runEventStoreConformance(factory),runDispatcherConformance(factory), …) that any implementation imports and runs against itself. Makes third-party runtimes provable, not just possible.
No new kernel mechanism — this is consolidation + test scaffolding over the existing seams.
Consequences
- Third parties can implement a store/dispatcher/memory backend and prove conformance in CI.
- Kernel genericity strengthened (P10): the contract, not the reference impl, becomes normative.
- No runtime overhead — pure docs + test kits.
Verification
- Each conformance kit passes against Loom’s own reference impls and fails a deliberately-broken stub.
- The SPI map lists every seam with a link to its interface and a reference impl.
Amendments (from the Execution-OS evaluation)
- Merges the requirements’ “Kernel Extensions” (mostly shipped) and “Runtime Contracts” (partial) into one consolidation item; “Fiber Contract” dropped (fibers rejected).
Coverage extension (2026-07-13)
Conformance kits now cover 9 SPIs: runEventStoreConformance, runSnapshotStoreConformance,
runBranchRegistryConformance, runExecutionDispatcherConformance, runTimerStoreConformance,
runProjectionRuntimeConformance, plus runWorkerRuntimeConformance (worker factory + package
factory; self-tested against LocalWorkerRuntime in @loom/worker-runtime-local),
runLlmProviderConformance (complete shape + stream), and runCapabilityAuthorizerConformance
(decision shape + determinism). The loom plugin new <seam> scaffolder (ADR 0043) emits the
matching conformance-wired test for every seam it knows — run loom plugin seams for the
list, which is authoritative in a way a list written here cannot stay. Remaining seams
without a kit: widget-renderer, effect-middleware, telemetry (framework-specific or
passive-observer contracts — lower value).
Amended 2026-09-16. The enumeration above used to be spelled out here and named six
seams; by the time it was noticed it was missing five. The relationship is now the other way
round and is enforced rather than described: every SPI that publishes a conformance kit is
scaffoldable, checked by a fitness test in tools/scaffold. Publishing a kit without
writing its seam fails the build, so this ADR no longer has a list to keep current.
ESM distribution (2026-07-13)
The kits moved out of @loom/testing/conformance into their own package @loom/conformance,
shipped as ESM ("type": "module") with vitest as a peer dependency. The reason: the kits
import { describe, it } from "vitest" at module scope; as a CJS build that becomes
require("vitest"), which vitest forbids — so no other package could consume the kit in its own
vitest suite. As ESM the import is native, so any plugin (1P or 3P) runs the kit in its own
tests — e.g. @loom/plugin-sqlite/src/conformance.test.ts now runs all four store kits against
the real SQLite backend. This also dissolved the @loom/testing ↔ @loom/plugin-sqlite build cycle
(the kits’ self-test against the in-memory references lives in @loom/conformance; durable-backend
conformance lives in each backend’s own package). Consumers add default export conditions on the
SPI packages so the ESM kit can resolve them (harmless for CJS require consumers).
Source: docs/adr/0033-spi-conformance-contracts.md in the loom repository.