ADR 0002: Facts are derived exactly once, in resolve
- Status: accepted
- Date: 2026-09-23
Context
In generator systems, derivations creep into every emitter: each lens recomputes routes, defaults, and names, and they drift. Debugging "which one decided this?" stops being possible.
Decision
mosaic-core::resolve is the only place a fact is derived: CRUD ops
derived from entities, route paths, op ordering, the CLI command table,
the deploy merge. Its output is the single ResolvedPlan. Renderers
receive the plan and print it; the plan is a pure function of the
workspace, so rendering is deterministic by construction.
Consequences
- A lens can never disagree with another lens.
- New lenses (ui, docs, voice) are emitters only: small, reviewable, testable against goldens.
- All validation errors carry a structured diagnostic with a stable code and a repo-relative path.
Proof
cargo test -p mosaic-conformance → render_is_a_pure_function_of_the_plan
(rendering the same plan twice yields byte-identical file maps).