Decision records
ADR 0044 — No compiler plugin API; the manifest is the public build-time contract
- Status: Accepted (2026-07-13)
- Date: 2026-07-13
- Builds on: ADR 0006 (static composition, not plugin magic), ADR 0011 (codegen decoupled from the heavy compiler), ADR 0023 (K11 flow-code hash over compiler-normalized manifest content), ADR 0043 (the kernel/1P/3P triage this applies to the build-time layer).
- Part of: the plugin-boundary analysis. No kernel or runtime behaviour changes.
- Related: requirements (
loom/docs/requirements/2026-07-13-composition-layer-plugins.md) (§“Compiler plugin support — analysed, rejected”).
Context
The plugin question was raised for @loom/compiler — the build-time Vite plugin + CLI that does
step$/flow$/skill$ extraction, handler$/instructions$ chunking, flow-graph +
non-determinism analysis, and emits loom-manifest.json. Grounded facts:
loomPlugin()is itself a Vite plugin (enforce: "pre"), and takes no options — there is no extension surface today (tools/compiler/src/plugin.ts:10).- The recognised markers (
step$,flow$,skill$,handler$,instructions$) are a closed, string-matched set validated by AST checks; adding one touches three layers (scanner, transform, visitor) (tools/compiler/src/transform.ts). - The emitted manifest is a versioned contract (
COMPILER_MANIFEST_VERSION = "1",tools/compiler/src/manifest.ts:5) carrying per-step/chunk hashes. Those hashes feed K11 flow-code versioning, resume-refusal, anddeploy-simdrift detection;@loom/specpins the version and@loom/appcross-checks flows against it at boot. ADR 0023 requires the hash be computed over “compiler-normalized manifest content, never raw bundler output” so it is reproducible.
Decision
Do not add a compiler plugin / transform-hook API. Apply the ADR-0043 triage to the build-time layer:
- The transform pipeline + marker set + manifest schema are the compile-time ABI → kernel-tier,
closed. They are the build-time twin of
KERNEL_SYSCALLS/EVENT_SCHEMA. They evolve only by a deliberate core/language decision (versioned), never by third-party contribution. A new$-marker is a language change, not a plugin. - A transform-hook plugin API is rejected as the build-time twin of fold-path extensibility. The compile output feeds determinism-critical machinery (K11 hash → resume-refusal, deploy-sim drift). If third-party passes participate, the manifest/hash becomes a function of plugin set, version, and order — resume decisions flap and K11 becomes unenforceable. This is precisely the hazard ADR 0023 exists to prevent.
- Generic build-time extension already has a home: the Vite/Rollup plugin pipeline.
loomPlugincomposes into it viaenforce: "pre". Building a second plugin system inside a Vite plugin is redundant and would freeze@loom/compilerinternals as public ABI on a young compiler.
Where build-time extensibility legitimately lives — both read-only, downstream of the pass:
- The manifest is the public build-time output contract.
loom-manifest.jsonis versioned and already consumed by@loom/spec/@loom/app. Formalise it: a documented schema + a stability guarantee tied toCOMPILER_MANIFEST_VERSION, so first- and third-party build-time tooling (custom validators, dashboards, codegen, drift reports) can depend on it read-only. This is the build-time twin of “analytics reads the event log as a lens, never hooks the fold.” - Custom checks attach at the
validateseam, not the compiler. Project- or plugin-specific flow-graph lints are a pureFlowGraphValidator[]passed toloom check/defineConfig({validate})— read-only over the already-built graph, additive, no ABI exposure. Ship this only when demand is real (it is a one-function seam extension, not a subsystem).
Consequences
@loom/compilergains no public extension API; its internals stay free to refactor.- The manifest schema is documented as the versioned public output contract (see the requirements
doc and
docs/); third-party build tooling targets that JSON, never the compiler’s pipeline. - Determinism guarantees (K11, deploy-sim, resumability) remain a function of core code only.
Non-goals / explicitly deferred
- No transform-hook API, custom-marker registry, or manifest-mutation hook — ever (they break §Decision 1–2).
- The
FlowGraphValidator[]seam (Decision 5) is demand-gated, not built now. - Whether
@loom/sdkshould re-export adapter packages is tracked in ADR 0043, not here.
Source: docs/adr/0044-no-compiler-plugin-api-manifest-as-output-contract.md in the loom repository.