ADR-011: Mono-Repo Manifest as a Dedicated File, Auto-Detected by repo init¶
| Field | Value |
|---|---|
| Date | 2026-07-23 |
| Status | Accepted |
| Deciders | Holger Zahnleiter |
| Supersedes | — |
| Superseded by | — |
Context and Problem Statement¶
This generator's default philosophy is one repository per artifact (see mono_repo_spec.md,
root). Some situations still call for one Git repository hosting several artifacts — e.g. an
electronics project pairing PCB production files with firmware, or a service bundled with its
frontend for pragmatic reasons. CLI flags describe exactly one artifact and cannot express a
list of them, so a YAML-based manifest is needed for the mono-repo case.
Today, a single artifact is described by RepoConfig (internal/repo_config.go), persisted as
.repo.yaml in the artifact's own directory: written by repo init when flags are used, then
read by both repo init (when flags are absent) and repo update. Introducing multi-artifact
generation raises two questions: how the new list-of-artifacts format relates to this existing
per-artifact format, and how the CLI triggers it.
Decision Drivers¶
- Backward compatibility: existing single-repo behavior (
.repo.yamlshape,repo init/repo updatesemantics) must not change. - Minimal blast radius: reuse the existing capability registry
(
internal/templating/capability/) and generation pipeline unchanged; mono-repo support should be an outer loop, not a parallel generation mechanism. - No new ambiguity: a single file must not carry two structurally different meanings depending on which keys happen to be present.
- Consistency with existing CLI conventions:
repo initalready auto-detects.repo.yamlwhen--nameis absent; the mono-repo case should follow the same pattern rather than introduce a parallel one.
Considered Options¶
- Reuse
.repo.yamlfor both cases, discriminated by the presence of arepos:key - A new, dedicated manifest file
.mono-repo.yamlat the mono-repo root, with each generated sub-repo still receiving its own ordinary.repo.yaml← chosen - A new dedicated CLI subcommand (e.g.
repo init-mono) instead of extendingrepo init's auto-detection ← rejected in favor of extendingrepo init
Decision Outcome¶
Chosen option: Option 2 — a dedicated .mono-repo.yaml manifest, detected by the existing
repo init command.
.mono-repo.yaml is a header (name, copyrightStatement, ci) plus a repos: list. Each
list entry has the same shape as RepoConfig (language, type, name,
description.brief/detailed, artifact.name/module/registry), plus a new authors field.
It is, per the initiative's own framing, "a container for" the existing single-repo format —
each entry converts to a ProjectDescriptor via the same RepoConfig.ToProjectDescriptor logic
already in use today.
repo init's existing rule — "no --name flag ⇒ read a config file from --targetpath" — gets
one more file checked before falling back to .repo.yaml: .mono-repo.yaml. If found, the
generator, for each entry:
- Validates the entry exactly as a single-repo spec is validated today (language, type, CI, name, description — see business rules below).
- Refuses if
<targetpath>/<artifact.name>already exists (mirrors the existing single-repoGEN-S03guard incmd/init.go, applied per entry). - Builds a
ProjectDescriptorand runs the existingtemplating.NewTemplatingGenerator(...).PopulateRepository()into that subdirectory — unchanged from single-repo generation. - Writes a normal
.repo.yamlinto that subdirectory, sorepo updatekeeps working per sub-repo exactly as it does today.
This means internal/templating/capability/* needs no changes at all — mono-repo support is
a new outer loop around the existing single-repo pipeline, not a new generation mechanism.
copyrightStatement and authors are accepted and validated for shape (present or absent) but
not yet rendered into any generated file. Rendering them into file header comments is a
distinct future capability (candidate design recorded in mono_repo_spec.md) — every
file-writing Action in the capability registry would need to consult it, which is a larger,
separate change deliberately deferred.
.mono-repo.yaml is write-once; each .repo.yaml is independently authoritative afterward¶
.mono-repo.yaml is never modified by generation. It stays exactly as authored — a
declarative snapshot of generation intent, not a synchronized twin of the .repo.yaml files it
produces. After repo init runs, each <artifact.name>/.repo.yaml becomes the sole, independent
source of truth for that artifact, exactly as for a standalone repo today: repo update already
only ever reads a directory's own .repo.yaml, never a parent manifest, so there is no runtime
consistency hazard between the two files — only a one-time duplication of values at generation
time, comparable to a commit message summarizing a diff rather than staying mechanically in sync
with it.
Two alternatives were considered and rejected:
- Rewrite generated entries into references (e.g.
repo: ./flight-booking/.repo.yaml) once their subdirectory exists. Rejected: makes the entry schema polymorphic (detail-struct or reference), which is exactly the kind of "invalid state representable" problem this repo's own ADR-007 warns against, and destroys the manifest's main value — a one-page overview of the mono-repo's artifacts, readable without opening every subdirectory. - Make the manifest the perpetual master, regenerating every
.repo.yamlfrom it on each mono-repo-level update. Rejected: would silently discard a direct edit made in one sub-repo's.repo.yaml, violating this project's ownuser_experience.mdrule ("never discard user data without explicit confirmation").
Positive Consequences¶
- Zero changes to existing single-repo code paths, structs, or file formats — mono-repo support is purely additive.
- Zero changes to the capability registry; multi-artifact generation is "run the existing generator N times," not a new dispatch mechanism.
- Follows the CLI's existing "flags absent ⇒ read config file" convention instead of introducing a second, differently-shaped entry point.
- Each sub-repo remains independently updatable via
repo update, unmodified.
Negative Consequences / Risks¶
- Two YAML file formats now exist (
.repo.yaml,.mono-repo.yaml) instead of one — mitigated by each entry in.mono-repo.yamlbeing structurally a superset ofRepoConfig, so there is only one mental model to learn, not two. repo init's auto-detection now checks two candidate files instead of one — a minor addition to an already-existing code path, not a new one.
Pros and Cons of the Options¶
Option 1 — Same file, discriminated by repos: key¶
- Pro: one filename to document and remember.
- Con:
.repo.yaml's documented contract ("written byrepo init, read byrepo update, describes one artifact") would silently change meaning based on file shape — a human or script inspecting the file cannot know which contract applies without parsing it. - Con:
repo update's single-repo read path would need a shape check on every invocation to rule out having been pointed at a mono-repo header by mistake.
Option 2 — Dedicated .mono-repo.yaml (chosen)¶
- Pro:
.repo.yaml's contract never changes; existing code, tests, and docs describing it stay valid verbatim. - Pro: the two file formats are trivially distinguishable by name alone.
- Con: introduces a second manifest filename to document.
Option 3 — New subcommand (e.g. repo init-mono)¶
- Pro: a clearly separate command surface in
--helpandcli.yaml. - Con: duplicates flag/validation plumbing (
--targetpath, error messages) thatrepo initalready has, for what is otherwise the same operation at a different granularity. - Con: breaks the established "no flags ⇒ read config file" convention users already know
from single-repo
repo init.
Links¶
mono_repo_spec.md(root) — initiative notes, including the deferred header-comment rendering design forcopyrightStatement/authors.internal/repo_config.go—RepoConfig, the per-artifact shape each manifest entry mirrors.cmd/init.go— existinginitFromCLIArgs/initFromRepoConfigdispatch to be extended with a.mono-repo.yamlcheck.specs/domain/use_cases/UC005-init_mono_repo.mdandspecs/domain/features/UC005-init_mono_repo.feature— behavior specification.- ADR-007 (
ADR-007-identity_vs_capability_based_generation.md) — the capability registry this decision deliberately leaves unchanged.