drogna

A demonstration harness. Every number in it is invented.

ADR-0030: components are scheduled modules on the main thread, wired by one composition root

Status: Accepted Date: 29 August 2026 Feature: 101 (foundations & shell)

Context

SRD-v2 §10 delegates to feature 101 whether the in-browser backend components run as Web Workers or as scheduled modules. Separately, Constitution XI forbids any import between front-end (shell/, panels/) and backend (backend/) — yet something must construct both halves and hand each its configuration and its seam client.

Decision

Scheduled modules on the main thread. Web Workers communicate by asynchronous message passing whose interleaving the browser schedules; AT-04's byte-identical lockstep replay would require rebuilding determinism on top of that with a per-worker lockstep protocol — precisely the class of complexity V2 exists to shed (spikes/seam-interception/FINDING.md). As scheduled modules, broker delivery is synchronous in subscription order and reproducible by construction. The components remain genuine programs — each has its own configuration document, seed stream, heartbeat and lifecycle, and stops by ceasing to be scheduled, not by a flag. The transport interface is the honest port: a worker pool, like a V3 network backend, would replace the transport, never the components.

One composition root: app/src/bootstrap/ is the only module allowed to import from both sides of the seam. It reads the run configuration document, creates the run manifest (the single point where entropy may enter, and is recorded), constructs the backend runtime, installs the fetch shim (ADR-0029), and mounts the shell — handing each half only configuration and seam interfaces. Nothing imports the bootstrap. The import-boundary gate encodes exactly this: shell|panelsbackend imports are violations anywhere, bootstrap may import both, and only seam/ and generated/ are importable from everywhere.

Alternatives rejected