ADR 0007: Deployments are data, not code
- Status: accepted
- Date: 2026-09-23
Context
Deployment logic written as scripts (build steps, kubectl calls, registry pushes) is the part of a codebase that rots first: it is environment-coupled, unauditable, and duplicated per target.
Decision
A deployment is a declarative DeploySpec (deploy/<name>.yaml, or
the inline deploy: sugar in mosaic.yaml): name, target
(local | docker | k8s), host, base_path, port, replicas, literal
env, and secret names (values never live in the repo). The renderer
turns each target into inert artifacts — run.sh, Dockerfile, or a
deployment/service/ingress triple — under deploy/<name>/ in the
output directory. Conflicting sources (inline deploy: and a
deploy/ directory) are a hard error. local is a first-class target,
not an afterthought. Config precedence, when a later version adds
overrides: compiled default < mosaic config < deploy overrides <
runtime env var.
Consequences
- Deploying is diff-able: the artifacts are generated files, reviewed like anything else.
- Adding a target is adding a vocabulary value plus a renderer branch.
- Secrets enter the system by name; no value is ever authored.
Proof
cargo test -p mosaic-core → load::tests::inline_deploy_conflicts_with_deploy_dir
(plus inline_deploy_alone_is_accepted and
missing_deploy_dir_yields_implicit_local, which pin the sugar and the
implicit local deployment).