diff --git a/01-RESEARCH/005-domain-grouping/00-overview.md b/01-RESEARCH/005-domain-grouping/00-overview.md new file mode 100644 index 0000000..ae54aae --- /dev/null +++ b/01-RESEARCH/005-domain-grouping/00-overview.md @@ -0,0 +1,60 @@ +--- +status: active +initiated: 2026-08-23 +touches: + - 02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md + - 03-DESIGN/00-as-is/10-module-catalogue.md + - 03-DESIGN/00-as-is/02-modules-and-manifests.md +became: [] +--- + +# 005 — Which domains the catalogue groups into + +[ADR 0017](../../02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md) settles +that modules outside the platform core are grouped by domain rather than by single function, +and deliberately does not settle the list. This effort settles the list — and, first, tests +whether the premise survives measurement. + +## What is being investigated + +Eighty-nine modules sit outside the platform core. The question is which of them belong +together, and the method is evidence rather than intuition: **which modules actually change +together**, measured across the full history of the code repository. + +## Why + +The argument in ADR 0017 is that the catalogue's shape records what was installed rather than +what anything is for — that four modules constituting "how a node is reachable" have no +relationship the mesh can see, so a change to connectivity is made four times. + +That argument is testable. If those modules genuinely change together, the grouping is +justified by more than tidiness. If they do not, the premise needs revising before a list is +drawn from it. + +## What it touches + +The catalogue's shape, the manifest, and — for one candidate grouping — the provisioning +reference itself, since a requirement names a provider **module**. Grouping providers would +change what a consumer names. + +## Status + +Measurement is done and is in [`analysis.md`](analysis.md). It **partly contradicts the +premise**, in a way that narrows the effort usefully: + +- Non-platform modules overwhelmingly change **alone** — 10% of commits touch more than one, + and 50 of 89 never co-change with anything. +- Two clusters do exist. One of them, reachability, holds up as a domain. +- The other, the provisioned infrastructure providers, co-changes for a reason that argues + **against** grouping rather than for it. + +The remaining work is the list itself, for the modules where grouping is justified, plus the +open questions below. + +## Open questions + +| Question | Why it is open | +|---|---| +| Whether provider modules group at all, and if so what a consumer's requirement names instead of a module. | The provisioning reference is load-bearing; getting it wrong is expensive. Opinion and evidence in the analysis; not yet decided. | +| Whether applications group into domains now and leave the monorepo later as a unit, or leave first. | Decided in principle — group first, then split — but the migration order has real cost either way. | +| What to do with the ~50 modules that co-change with nothing. | The evidence gives no grouping signal for them at all. That may mean they are correctly sized already. | diff --git a/01-RESEARCH/005-domain-grouping/analysis.md b/01-RESEARCH/005-domain-grouping/analysis.md new file mode 100644 index 0000000..62b5f97 --- /dev/null +++ b/01-RESEARCH/005-domain-grouping/analysis.md @@ -0,0 +1,144 @@ +--- +effort: 005-domain-grouping +updated: 2026-08-23 +--- + +# Which modules actually change together + +## Method + +Every commit in the code repository's main branch that touches the module catalogue, reduced +to the set of modules it touched. Platform-namespace modules are excluded — their +decomposition is settled by +[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md). Modules that no +longer exist are excluded, because pre-rename names dominate the raw signal and describe a +catalogue nobody works in. + +Commits touching more than eight modules are excluded from pair counting: a sweep across +forty modules says the sweep happened, not that the modules are related. + +## Finding 1 — these modules overwhelmingly change alone + +| Measure | Value | +|---|---| +| Non-platform modules today | 89 | +| Commits touching at least one | 312 | +| Commits touching two to eight together | 33 — **10%** | +| Modules that never co-change with anything | **50 of 89** | + +Nine commits in ten touch exactly one module. More than half the catalogue has never been +edited alongside anything else. + +This is the first thing a grouping proposal has to survive, and it is not what the premise +predicts. + +## Finding 2 — two clusters exist + +Everything above two co-changes falls into one of two groups. + +**The provisioned infrastructure providers.** The densest cluster: the relational stores with +each other and with the cache, the broker, the object store and the image registry — a +strongly connected set, six to two co-changes per pair. + +**Reachability.** The reverse proxy with the resolver, the firewall and the VPN; the firewall +with the intrusion filter. Three to two co-changes per pair. + +Outside those: a torrent client with the VPN, twice. A login manager with a media player, +twice. Nothing else reaches two. + +## Finding 3 — the provider cluster is manifest churn, not cohesion + +This is the result that matters, and it is only visible by reading the commits rather than +counting them. + +Every multi-provider commit is a **cross-cutting change to the machinery, applied N times**: + +| Date | What the commit did | +|---|---| +| 2026-04-15 | modules own their tools — decentralised tool serving | +| 2026-04-24 | filesystem-driven feature detection; manifest boilerplate removed | +| 2026-04-27 | remove runtime dependencies that were only build dependencies | +| 2026-05-01 | hooks move into their own directory with their own package | +| 2026-05-05 | hooks renamed to a stage-and-feature convention; legacy install scripts removed | +| 2026-08-06 | verify **by shape in the SDK**, not by copying a script into every module | +| 2026-08-11 | bind declared volume paths that leaked an anonymous volume per deploy | +| 2026-08-23 | scope services to the local network and mesh; respect the profile gate | + +**Not one of them is a change to what a database is.** They are all "the manifest contract +changed, therefore every manifest changed". + +Two consequences follow. + +Merging the providers into one module would not have prevented a single one of these commits. +It would have made the same edit land in one file rather than six — which is a diff-size +improvement, not a boundary. + +And the history already shows the correct fix being applied, repeatedly and successfully: +*verify by shape in the SDK, not by copying a script into every module*. When the same edit +must be made in every provider, the answer that worked was **moving the concern into the +machinery**, not merging the modules that carry it. + +Co-change here measures coupling to the manifest contract. It does not measure domain +cohesion, and using it as though it did would group the catalogue by which modules are most +boilerplate-heavy. + +## Finding 4 — reachability holds up + +The same reading applied to the reachability cluster gives a different answer. Most of its +twenty-five multi-module commits are the same machinery churn — but not all, and the +remainder are genuine: + +| Date | What the commit did | +|---|---| +| 2026-07-29 | let the mesh's proxy coexist with another listener on the same port | +| 2026-08-03 | derive public split-DNS from node accessors so requests stop hairpinning | +| 2026-08-06 | firewall mesh-only by default, public by declaration | + +Each is one intent — *change how a node is reachable* — landing across the proxy, the +resolver, the firewall and the VPN together. That is exactly the shape ADR 0017 describes, and +it is the only place in the catalogue where the measurement finds it. + +The 2026-08-23 scoping commit is the sharpest case: it spans the reachability cluster **and** +two providers, because "which network is this exposed on" is a reachability question asked of +a database. + +## What this means for ADR 0017 + +The record's principle stands, and its scope needs narrowing. Grouping by domain is: + +- **Justified by evidence** for reachability. One intent, several modules, repeatedly. +- **Argued against by evidence** for the providers. The coupling is to the manifest contract, + and the demonstrated fix is to move the concern into the machinery. +- **Unsupported either way** for the fifty modules that co-change with nothing. Silence is not + evidence of independence — many are simply rarely touched — but there is no measured basis + for grouping them, and a proposal that groups them is drawn from intuition. It should say so. + +The catalogue's shape is still wrong in the way the record describes. Measurement says +grouping is the right fix in fewer places than the record implies, and that a second fix — +moving cross-cutting concerns into the machinery — accounts for most of what looks like +grouping pressure. + +## Recommendation on the provisioning reference + +Asked directly, and stated as an opinion because it is not yet decided. + +**Do not group the provider modules.** Three reasons, in order of weight: + +1. **The evidence for grouping them is the wrong evidence.** Finding 3. +2. **A requirement names a provider module.** Grouping providers means a consumer names a + resource type and something else chooses the implementation. That is not a folder move; it + is implementation selection, a substantially larger design with its own failure modes, and + nothing currently asks for it. +3. **It would hide which implementation serves a requirement** — the one place the mesh most + needs to be explicit, and precisely the indirection ADR 0017 warns grouping causes. + +A provider module is already exactly one purpose: it provisions one resource type. That is a +boundary, not an accident of installation. + +## Still to do + +- Draw the list for the cases where grouping is justified, and say plainly which entries rest + on measurement and which on judgement. +- Decide the ~50 silent modules: correctly sized, or unmeasured? +- Test the reachability grouping against the migration cost — the modules in it are among the + most-changed in the catalogue, so churn during a move is not hypothetical. diff --git a/02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md b/02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md index ba9578d..0da332b 100644 --- a/02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md +++ b/02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md @@ -81,7 +81,11 @@ metrics, storage and data services — but naming them here would be reconstruct that has not been taken. Settling the list is a research effort, not an act of this record. Until it concludes, this -ADR stays `proposed`. +ADR stays `proposed`. That effort is +[`01-RESEARCH/005-domain-grouping`](../01-RESEARCH/005-domain-grouping/00-overview.md), and its +first measurement already narrows this record's scope: co-change analysis supports grouping for +reachability, argues against it for the provisioned infrastructure providers, and finds no +signal either way for the fifty modules that never change alongside anything. ## References