Mosaic one model, many lenses

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).