ADR-013: Mono-Repo Root-Level Specs and Cross-Artifact Contract Referencing¶
| Field | Value |
|---|---|
| Date | 2026-07-23 |
| Status | Accepted |
| Deciders | Holger Zahnleiter |
| Supersedes | — |
| Superseded by | — |
Context and Problem Statement¶
ADR-011 and ADR-012 generate each .mono-repo.yaml entry exactly as repo init would generate it
standalone — full specs/ tree, full CI pipeline — plus one small root-only file
(.gitlab-ci.yml, ADR-012). Two questions came up before populating the demo mono-repo that
neither ADR answers:
- Where does content that spans more than one artifact live? Every generated artifact
already gets its own
specs/architecture/ADRs/andspecs/domain/business_rules.md— but a decision like "which technology orchestrates a saga across services" or a rule like "payment is always the last step of the saga" doesn't belong to any single artifact. Today there is no place for this at all; it currently lives informally in the (gitignored, not-checked-in)mono_repo_spec.md. - How does one artifact reference another artifact's API contract? Every generated artifact
already gets
specs/architecture/APIs/{provided,consumed}/README.md(internal/templating/general/files/specs/architecture/APIs/{provided,consumed}/README.md.tmpl— currently placeholder content in every generated repo). Theconsumed/convention already distinguishes copying a file from referencing a URL "to avoid drift" — but that convention was designed for APIs that live in a genuinely different repository. A mono-repo sibling is not external: it shares one Git commit with its consumer, so referencing it by URL (or copying it) discards a same-repo guarantee the URL convention was never able to offer in the first place.
Decision Drivers¶
- Stay consistent with ADR-011's core philosophy: no shared master that risks silently
overwriting or discarding an artifact's own data (the same reasoning that keeps
.mono-repo.yamlwrite-once and each.repo.yamlindependently authoritative applies equally to specs — an artifact's ownspecs/must stay its own). - Don't invent a new kind of generator-managed artifact (a "specs-only" project) just to hold
cross-artifact content — reuse the root-file precedent ADR-012 already established
(
writeMonoRepoRootGitlabCI) instead. - Keep the addition small: only scaffold what is genuinely cross-artifact, not a second copy of everything every artifact already gets (guidelines, use cases, API directories of its own).
- Contract referencing must degrade gracefully to today's behavior for artifacts consuming a genuinely external API — this is an additional convention, not a replacement.
Considered Options¶
Root-level specs scaffold:
- Generator scaffolds a small root
specs/skeleton atrepo inittime, containing onlyarchitecture/ADRs/,domain/business_rules.md, and anindex.mdtying them together. (chosen) - Leave it to hand-maintained documentation — e.g. a cleaned-up, checked-in version of
mono_repo_spec.md, with no generator involvement.
Cross-artifact contract referencing:
- Extend
consumed/README.md.tmplwith a mono-repo-local*.path.txtconvention — a relative path to a sibling artifact'sspecs/architecture/APIs/provided/...file, alongside the existing*.url.txtconvention for genuinely external APIs. (chosen) - Reuse the existing
*.url.txtconvention unchanged — treat a mono-repo sibling exactly like any external API. - Centralized root-level
contracts/directory holding every artifact's canonical spec, referenced by all consumers.
Decision Outcome¶
Chosen: Option 1 (generator-scaffolded root specs) + Option 3 (relative-path contract reference), kept deliberately small.
Root-level specs scaffold¶
repo init, when run from a .mono-repo.yaml manifest, additionally writes:
— alongside, not instead of, each entry's own full specs/ tree in its own subdirectory. New
templates live under internal/templating/general/files/mono_repo_root/specs/... and are copied
by a new general.WriteMonoRepoRootSpecs function, called once, unconditionally, from
cmd/init.go's initFromMonoRepoConfig — unlike the GitLab-only writeMonoRepoRootGitlabCI, this
has no CI-provider dependency, so it always runs for any mono-repo.
Deliberately not included in the root skeleton:
specs/guidelines/*— each artifact already gets its own language-appropriate guideline set; a root copy would be redundant.specs/architecture/APIs/*— the mono-repo root has no API surface of its own.specs/domain/use_cases/andspecs/domain/features/— the one genuinely cross-artifact use case in a typical saga demo (e.g. "book a trip") is naturally owned by whichever artifact implements the orchestration, so it lives in that artifact's ownspecs/domain/use_cases/, not at root. Revisit only if a use case shows up that doesn't fit any single artifact.specs/architecture/system_context.md— each artifact's own system_context already covers its own boundary; a root-level system-of-systems diagram can be added later without breaking anything, but isn't required to make a handful of artifacts understandable.
Rejected Option 2 (hand-maintained only) because the content this scaffold holds — cross-artifact architecture decisions and invariants — degrades the same way undocumented decisions always do without a fixed home; the generator already has a working precedent (ADR-012's root CI file) for writing exactly this kind of root-only content cheaply.
Cross-artifact contract referencing¶
specs/architecture/APIs/consumed/README.md.tmpl gains a *.path.txt row alongside the existing
*.url.txt one: a relative path from the consumer's own consumed/ directory to the producer
sibling's specs/architecture/APIs/provided/... file (e.g.
../../../../flight-booking/specs/architecture/APIs/provided/openapi.yaml). Because both files
live in the same repository and the same commit, this reference cannot drift the way a URL
reference to an external repository can — the whole point of the existing *.url.txt
"prefer a URL to avoid drift" guidance was to manage a staleness risk that same-repo co-location
removes structurally. specs/guidelines/api_design.md.tmpl gains one line recommending the
relative-path form whenever the consumed API's producer is a sibling artifact in the same
mono-repo.
This is documentation and convention only — no generator code enforces, resolves, or
validates the reference. Neither provided/ nor consumed/ has real content in any artifact
today (both remain placeholder-only, per their existing templates); this ADR only makes sure the
convention is in place before an artifact's spec actually needs to reference a sibling's.
Rejected Option 4 (reuse the URL convention unchanged) because it forgoes a drift-safety property
same-repo co-location provides for free. Rejected Option 5 (centralized contracts/ directory)
because it reintroduces the shared-master duplication hazard ADR-011 already rejected for
.mono-repo.yaml/.repo.yaml, and because it would require inventing a new artifact type this
generator has no other use for.
Positive Consequences¶
- Cross-artifact architectural decisions and invariants get a fixed, discoverable home instead of living only in an informal, gitignored planning document.
- Contract referencing between mono-repo siblings becomes structurally drift-proof, an improvement over what's possible for a genuinely external dependency.
- No new generator artifact type, no change to how any single entry is generated.
Negative Consequences / Risks¶
- A relative-path reference is only valid as long as both artifacts keep their relative
positions under the mono-repo root; renaming an artifact's subdirectory would require updating
any
*.path.txtfiles that reference it. Accepted: the same is already true of any relative path in a monorepo, and mono-repo entries are already refused-to-rename by construction (BR-021 refuses to regenerate over an existing subdirectory). - The root
specs/architecture/ADRs/anddomain/business_rules.mdstart empty; nothing enforces that a genuinely cross-artifact decision actually gets written there instead of duplicated into one artifact's own specs by habit. Same discipline gap that already exists for every other guideline in this generator's output — not new to this ADR.
Pros and Cons of the Options¶
Root scaffold Option 1 — Generator-scaffolded (chosen)¶
- Pro: cheap, reuses the ADR-012 root-file precedent.
- Pro: guaranteed to exist from the first
repo init, not dependent on someone remembering to write it by hand. - Con: starts empty — provides a home, not the content itself.
Root scaffold Option 2 — Hand-maintained only¶
- Pro: zero generator code.
- Con: exactly the gap this ADR closes — cross-artifact decisions have no fixed, discoverable home in the generated output.
Contract referencing Option 3 — Relative-path convention (chosen)¶
- Pro: structurally drift-proof for same-repo siblings.
- Pro: additive — existing
*.url.txtconvention untouched for genuinely external APIs. - Con: breaks if a referenced artifact's subdirectory is renamed or removed; no automated validation today.
Contract referencing Option 4 — Reuse URL convention unchanged¶
- Pro: no new convention to document.
- Con: forgoes the drift-safety property same-repo co-location provides for free.
Contract referencing Option 5 — Centralized contracts/ directory¶
- Pro: one place to look for every artifact's contract.
- Con: reintroduces the shared-master duplication hazard ADR-011 rejected for
.mono-repo.yaml/.repo.yaml. - Con: requires a new artifact type this generator has no other use for.
Links¶
specs/architecture/ADRs/ADR-011-mono_repo_manifest.md— the.mono-repo.yaml/.repo.yamlindependence philosophy this decision stays consistent with.specs/architecture/ADRs/ADR-012-mono_repo_ci_composition.md— the root-file precedent (writeMonoRepoRootGitlabCI) this decision reuses for root-level specs.internal/templating/general/files/specs/architecture/APIs/consumed/README.md.tmpl— gains the*.path.txtconvention.internal/templating/general/files/specs/guidelines/api_design.md.tmpl— gains the mono-repo-sibling recommendation.internal/templating/general/files/mono_repo_root/specs/...— the new root-only templates.specs/domain/business_rules.md(BR-024) — the root specs scaffold's testable business rule.specs/domain/use_cases/UC005-init_mono_repo.md,specs/domain/features/UC005-init_mono_repo.feature— behavior specification.