ADR-0002: The substrate lives inside the flare-dispatch monorepo, with no name of its own
- Status: Accepted
- Date: 2026-08-06
- Implementation:
shipped— the placement holds, and the lint rule this record promises now exists:.oxlintrc.jsonrunsno-restricted-importsrepo-wide withapps/substrate/**exempted, so a new consumer tree is covered the day it appears rather than when someone remembers to list it. It restricts both routes in — a path into the substrate directory, and the workspace package@fractalboxdev/flare-dispatch-substrateplus its subpaths, which no path glob matches and which aworkspace:*dependency makes the easier of the two. Static,export *and dynamicimport()forms all failpnpm lintwith the contract package named; a type-positiontypeof import(…)does not, and crosses no runtime boundary.src/facade-boundary.test.tsholds the rule’s shape so it cannot drift towarnor lose a route silently.
Context
Section titled “Context”The substrate needs a home and an identity. The candidates: a hard module inside this repo (the founding spec’s reviewed recommendation), a standalone repo (briefly created, with a product-style name), or a separately branded product. The facade contract is young and will churn hardest during the carve — exactly when cross-repo publish → pin-bump → adapt cycles cost the most — and the BYOC deploy already ships dispatcher and substrate from one upstream pin in one action run.
Decision
Section titled “Decision”The substrate is a component of the flare-dispatch monorepo: the worker at apps/substrate
(wrangler name flare-dispatch-substrate, frozen at the first BYOC deploy) and the facade contract
at packages/substrate-contract, published so out-of-repo consumers can pin it. It carries no
product name of its own — in prose it is “the flare-dispatch substrate”. The briefly-created
standalone repo and its naming ADR are retired.
Out-of-repo consumers depend on two artifacts, neither of which requires this repo: the deployed
worker, reached by an account-level service binding to flare-dispatch-substrate, and the
published contract package. fractalbot is the first such consumer. The substrate deploys
independently of the dispatcher — a substrate-only deploy job and per-component tags — so a
security patch or a consumer-driven substrate release never queues behind a dispatcher release,
and an org that wants the substrate without CI simply deploys this worker alone.
Consequences
Section titled “Consequences”- Contract iterations during the carve are atomic PRs; the facade freeze can happen on evidence rather than on publish-cycle pressure.
- One
UPSTREAM_SHAcovers both workers for BYOC orgs; a security release is one tag. - The facade boundary is enforced by workspace mechanics, not geography: consumers (the dispatcher
included) import only
packages/substrate-contract; a lint rule forbids imports from substrate internals; the SDK’s unfenced exec is never exported (ADR-0003). - Standalone extraction remains a topology change reserved for a promotion tripwire (an external
consumer or paid demand for the execution environment alone) —
apps/substrate+packages/substrate-contractlift out with history. Branding it is that day’s decision, not today’s. - The fold counter-tripwire stands: if Cloudflare ships first-party equivalents of the policy delta, the substrate thins toward the SDK in place.