ADR 0030: the repository structure, and the rule that names them

The tiers were settled and the product was named, but the repositories
themselves existed only in a research sketch. That had already caused two
problems.

ADR 0029 makes the lab phase 0 of the migration and could not say where it
lives, because no record named a repository.

And the sketch contradicted an accepted record: it listed mesh-hq while
ADR 0028 had decided novox/hq and explicitly rejected that name. A design
resting on research is resting on something that can change without a
decision. Corrected in the research too.

The naming rule, which both earlier records implied and neither stated: a
repository belonging to a product carries that product's prefix; a
company-scoped one does not. That is why this repository is hq and the
mesh's are mesh-*.

Seven repositories recorded — host, substrate, control, surfaces, sdk, lab,
and this one. The lab gets its own: it ships to nobody, outlives any single
tier, and drives virtualisation on a workstation, which nothing else does.
Inside the host it would couple development tooling to a shipped
component; inside the control plane the bootstrap scenario would depend on
a tier that does not exist when it is needed.

Tier 4 is deliberately not decided. Whether the catalogue is one
repository, one per domain or one per application stays open from ADR 0015
and is blocked on research 005 — how many repositories hold domains cannot
be answered before knowing what the domains are. mesh-catalog appears in
the sketch and is not decided by this record.

The cost is stated rather than glossed: seven release cadences where there
is one, and cross-repository changes that used to be one commit.
This commit is contained in:
2026-08-23 22:04:04 +02:00
parent b4904fec7e
commit 09489a298c
5 changed files with 122 additions and 2 deletions
+17
View File
@@ -18,6 +18,23 @@ and a forge address is an operational detail (see [`README`](../README.md)).
| `hq` | This repository, under the company organisation — mission, research, design, decisions, issue diagnosis. Company-scoped ([ADR 0028](../02-DECISIONS/0028-hq-is-company-scoped.md)); the mesh is its first product. The source of truth for *why*. Carries no implementation. | | `hq` | This repository, under the company organisation — mission, research, design, decisions, issue diagnosis. Company-scoped ([ADR 0028](../02-DECISIONS/0028-hq-is-company-scoped.md)); the mesh is its first product. The source of truth for *why*. Carries no implementation. |
| *(one per application)* | Every standalone application, site or side-project gets its own repository, with `module.yml` at the root. Registered with the mesh as a build source; built and deployed by the same pipeline as anything in the monorepo. | | *(one per application)* | Every standalone application, site or side-project gets its own repository, with `module.yml` at the root. Registered with the mesh as a build source; built and deployed by the same pipeline as anything in the monorepo. |
## What the mesh becomes
[ADR 0030](../02-DECISIONS/0030-the-repository-structure.md) records the repositories the
monorepo decomposes into. **None exist yet** — they are the target, not the present.
| Repository | Tier | Holds |
|---|---|---|
| `mesh-host` | 0 | the node host — the one binary installed by hand |
| `mesh-substrate` | 1 | the four pinned services, as declarations |
| `mesh-control` | 2 | the control plane and its contexts |
| `mesh-surfaces` | 3 | tools, web, cli |
| `mesh-sdk` | — | contracts shared across tiers |
| `mesh-lab` | — | the lab — built first, per [ADR 0029](../02-DECISIONS/0029-the-labs-first-scenario-has-no-pipeline.md) |
Tier 4's shape is open, and deliberately so: see ADR 0030 and
[research 005](../01-RESEARCH/005-domain-grouping/00-overview.md).
## What lives where inside the monorepo ## What lives where inside the monorepo
Named by role, because the layout is itself part of the as-is design — see Named by role, because the layout is itself part of the as-is design — see
@@ -256,7 +256,7 @@ mesh-surfaces/ TIER 3
mesh-catalog/ TIER 4 mesh-catalog/ TIER 4
<domain>/<module>/ layout as above <domain>/<module>/ layout as above
mesh-lab/ mesh-sdk/ mesh-hq/ mesh-lab/ mesh-sdk/ hq/ (company-scoped — ADR 0028)
``` ```
## What this does not settle ## What this does not settle
@@ -84,7 +84,8 @@ mesh-catalog/ TIER 4 — what the mesh hosts
mesh-lab/ the whole mesh, disposable, on one machine mesh-lab/ the whole mesh, disposable, on one machine
mesh-sdk/ contracts shared across tiers — types, not behaviour mesh-sdk/ contracts shared across tiers — types, not behaviour
mesh-hq/ this repository
hq/ company-scoped, not a mesh repository — ADR 0028
``` ```
## The dependency rule ## The dependency rule
@@ -83,6 +83,9 @@ enough to state directly.
a lab node is a virtual machine, and the scale argument for system containers was found to a lab node is a virtual machine, and the scale argument for system containers was found to
have been invented rather than required. The design text did not follow the decision. It does have been invented rather than required. The design text did not follow the decision. It does
now. now.
- The lab's home is `novox/mesh-lab`, recorded in
[ADR 0030](0030-the-repository-structure.md) — written after this record, because this one
needed a repository that no decision had yet named.
- The bootstrap scenario's fidelity is its whole value, and also its risk: if it diverges from - The bootstrap scenario's fidelity is its whole value, and also its risk: if it diverges from
how a real node is raised, it certifies something that does not happen. That is the same how a real node is raised, it certifies something that does not happen. That is the same
hazard the existing design names for the full scenario, and the same answer applies — hazard the existing design names for the full scenario, and the same answer applies —
@@ -0,0 +1,99 @@
---
status: accepted
date: 2026-08-23
deciders: jochen
reconstructed: false
---
# 30. The repository structure, and the rule that names them
## Context
The tiers are settled ([research 006](../01-RESEARCH/006-mesh-from-scratch/code-skeleton.md))
and the product is named ([ADR 0027](0027-the-product-is-novox-mesh.md)), but the repositories
themselves were only ever sketched in research. Two consequences had already appeared.
[ADR 0029](0029-the-labs-first-scenario-has-no-pipeline.md) makes the lab phase 0 of the entire
migration and **could not say where it lives**, because no record named a repository.
And the research contradicted an accepted record: it listed `mesh-hq` for this repository, while
[ADR 0028](0028-hq-is-company-scoped.md) had decided `novox/hq` and explicitly rejected that
name. A design resting on research is a design resting on something that can change without a
decision.
There is also an implied naming rule that has never been written down. ADR 0027 says
repository names take `mesh`; ADR 0028 gives this repository no prefix at all. Both are right,
for a reason neither states.
## Considered options
Only the naming rule had genuine alternatives; the tier repositories follow from the tiers.
1. **No prefix — `novox/host`, `novox/control`.** The organisation already says Novox, so the
prefix reads as stutter. Rejected once it was established that Novox delivers more than the
mesh: with several products the prefix is not stutter, it is the product namespace doing
real work, and the forge has no nested groups to do it instead.
2. **An organisation per product — `novox-mesh/host`.** Puts the product boundary where the
forge's only real grouping primitive lives, so permissions and teams attach to it. Rejected
for now as premature: no per-product access boundary exists yet, and it costs `novox-`
repeated across every organisation.
3. **Product-prefixed repositories in the company organisation.** Chosen.
## Decision
**The naming rule:** a repository that belongs to a product carries that product's prefix. A
repository that is company-scoped does not.
That is why this one is `hq` and the mesh's are `mesh-*`. Both records were already correct;
the rule connecting them is stated here.
**The repositories:**
| Repository | Tier | Holds |
|---|---|---|
| `novox/mesh-host` | 0 | the node host — the one binary installed by hand |
| `novox/mesh-substrate` | 1 | the four pinned services, as declarations |
| `novox/mesh-control` | 2 | the control plane and its contexts |
| `novox/mesh-surfaces` | 3 | tools, web, cli — thin, no logic |
| `novox/mesh-sdk` | — | contracts shared across tiers: types, not behaviour |
| `novox/mesh-lab` | — | the lab: scenario lifecycle, networking, placement |
| `novox/hq` | — | this repository. Company-scoped ([ADR 0028](0028-hq-is-company-scoped.md)) |
**The lab is its own repository.** Its lifecycle differs from everything else in the list: it
is never shipped to a node, it outlives any single tier, and it drives virtualisation on a
workstation — which nothing else in the mesh does. Putting it inside the host would couple
development tooling to a shipped component; putting it inside the control plane would make the
bootstrap scenario depend on a tier that does not exist when it is needed.
**Tier 4 is deliberately not decided here.** Whether the catalogue is one repository, one per
domain, or one per application remains open from
[ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md) and is blocked on
[research 005](../01-RESEARCH/005-domain-grouping/00-overview.md): how many repositories hold
domains cannot be answered before what the domains are. Recording the gap is the point —
`mesh-catalog` appears in the research sketch and is **not** decided by this record.
## Consequences
- [ADR 0029](0029-the-labs-first-scenario-has-no-pipeline.md) can name its target. Phase 0 has
a home, which was the immediate blocker.
- The research sketch stops being load-bearing. It remains what it is — a sketch — and the
design layer can now cite a record instead.
- **Seven repositories where there is currently one**, for a mesh that today lives in a single
monorepo. That is the cost, and it is not small: seven release cadences, seven sets of
dependencies, and cross-repository changes that were previously one commit. The offsetting
argument is the tier rule — a boundary that only points downward is enforceable across
repositories and merely conventional inside one.
- The prefix will read as redundant for as long as the mesh is the only product with
repositories. That is accepted deliberately: the alternative is renaming everything at the
moment a second product appears, which is the class of migration this project is trying to
stop performing.
- Nothing is created yet. This records what the repositories *are*; creating them is part of
phase 0 and after.
## References
- [ADR 0027](0027-the-product-is-novox-mesh.md) — the product name the prefix comes from.
- [ADR 0028](0028-hq-is-company-scoped.md) — why this repository has no prefix.
- [ADR 0029](0029-the-labs-first-scenario-has-no-pipeline.md) — the lab, and why it is first.
- [Research 006](../01-RESEARCH/006-mesh-from-scratch/code-skeleton.md) — the tiers, and the
sketch this supersedes as a source.