ADR 0004: Renderers are thin — they print, they do not decide
- Status: accepted
- Date: 2026-09-23
Context
If a renderer derives a fact (a route, a default, a name), it becomes a hidden second source of truth. The system then has as many "models" as it has lenses.
Decision
The public entry point of mosaic-render is
render(plan: &ResolvedPlan, user_src: &BTreeMap<String, String>) -> Rendered.
It takes the resolved plan and nothing else. The dependency graph
enforces the direction: render -> core -> grammar; core never depends
on render. A test parses crates/mosaic-core/Cargo.toml and fails the
build if a mosaic-render dependency ever appears.
Consequences
- Adding a lens cannot introduce a new decision point.
- The plan type is the entire contract between the compiler and the lenses; it is small enough to read in one sitting.
Proof
cargo test -p mosaic-render --test thin_renderers →
core_never_depends_on_render (dependency-graph proof) and
render_entry_takes_a_resolved_plan (signature proof, compiled).