ADR 0162: a merge produces a tiered plan the mesh keeps; dependencies are one relation; design 30; issues 184, 186
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
---
|
||||
topic: the mesh
|
||||
status: accepted
|
||||
date: 2026-10-01
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 02-DECISIONS/0157-a-build-narrates-on-the-bus.md
|
||||
---
|
||||
|
||||
# 162. A merge produces a tiered plan the mesh keeps, and a module's dependencies are one relation in the catalogue
|
||||
|
||||
## Context
|
||||
|
||||
A merge on the forge reaches the controller as an event, and the controller asks the build
|
||||
machine for what that merge changed. Until today that meant the modules whose recorded source is
|
||||
that repository; since this afternoon it also means everything standing on what moved
|
||||
([issue 186](../04-ISSUES/186-a-release-across-repositories-is-an-order-in-a-persons-head/00-report.md)).
|
||||
Both are done inside the handler that received the event: it asks one build, waits for it, asks the
|
||||
next, and returns when the last is done. Three things followed from that shape on 2026-10-01:
|
||||
|
||||
- The controller hears nothing else for the length of the work — twenty-five minutes for the runtime
|
||||
image and its forty-three dependents ([issue 184](../04-ISSUES/184-a-merge-announcement-blocks-the-controllers-receive-loop/00-report.md)).
|
||||
- A controller replaced mid-merge loses the rest of the merge: the redelivered announcement reads as
|
||||
history, and the dependents are asked by hand.
|
||||
- Nothing is deployed between builds. A merge that changes the build machine and something the build
|
||||
machine builds asks for both in order, but the second is built by whichever build machine is running
|
||||
— the old one, unless somebody pushed in between. The order the dependents are sorted in exists for
|
||||
the artifacts; it says nothing about what must be *running*.
|
||||
|
||||
And the knowledge the order is computed from is scattered: a manifest's `build.on`, the artifacts a
|
||||
build was made against, the repositories a build read, and the fact that every source-built module is
|
||||
built by the build machine, each read by a different function in the merge handler.
|
||||
|
||||
## Decision
|
||||
|
||||
**1. A module's dependencies are one relation in the catalogue.** `depends-on` edges, each with the
|
||||
kind of dependency on it: `stands-on` (the module's artifact is built on the other's), `packages` (the
|
||||
module's build reads the other's repository), `built-by` (the module is built by the holder of the
|
||||
build-machine seat), and `declared` (a manifest's `build.on`). The relation is answered by one query
|
||||
of the catalogue — the controller's inventory today, the catalogue seat's tool when something outside
|
||||
the controller needs it — and nothing else computes an edge.
|
||||
|
||||
**2. A merge produces a plan, and the plan is a record.** The controller takes the modules the merge
|
||||
changed and everything reachable from them along `depends-on` edges, and sorts that set into tiers:
|
||||
tier 0 depends on nothing else in the set, tier 1 only on tier 0, and so on. The plan — the merge it
|
||||
answers, the tiers, and each module's state — is written to the store before any build is asked. The
|
||||
handler asks tier 0 and returns. Every build's outcome, taken in by the same handler that takes every
|
||||
outcome in, advances the plan it belongs to; a controller replaced mid-plan resumes it from the store.
|
||||
|
||||
**3. A tier is done when it is built, and when what the next tier needs from it is running.** A
|
||||
module whose roll-out policy says *roll out* is sent to its machines when it moves, as today. The next
|
||||
tier is asked only once every module in this tier is built and every rolled-out module of this tier
|
||||
that a later tier is `built-by` or `packages` has been applied by the machines running it — the
|
||||
machines' reports say so. A module whose policy says *record* is built and not waited for. So a merge
|
||||
touching the build machine and the controller builds the build machine, waits until it is the build
|
||||
machine that is running, and only then asks for the controller's build.
|
||||
|
||||
**4. A plan is read where the mesh is read.** `status` lists every open plan: the merge, the tier it
|
||||
is at of how many, what it is waiting for and since when; `builds` lists the asked beside the built.
|
||||
A plan that has waited past a bound is named red there, which is the first fact of
|
||||
[issue 187](../04-ISSUES/187-the-mesh-tells-nobody-when-it-stops-working/00-report.md)'s list.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The merge handler returns in milliseconds; the receive loop is never held by a build again. Issue
|
||||
184's remaining cause — a handler that waits for its own work — is removed rather than worked
|
||||
around; the bus's heartbeats stop being dropped under a merge.
|
||||
- A controller roll in the middle of a plan costs nothing: the plan is in the store and the asks are
|
||||
in the queue (mesh-controller 194).
|
||||
- A release across repositories is a plan whose edges cross repositories; the order a person kept in
|
||||
a work-order file is the order the tiers give. Issue 186's third fault is answered by the plan,
|
||||
not by a separate release record.
|
||||
- The explicit `build --on <base>` stays as the way to ask for the same plan by hand.
|
||||
- A module's `build.on` remains the one place a manifest states a dependency the store cannot see.
|
||||
|
||||
## How this is checked
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| Dependencies are one relation, each edge with its kind | an inventory test over a fixture catalogue: a runtime image, a module on it, a module packaging the controller's source, and the build machine; the four kinds come back from one call |
|
||||
| A merge's set is sorted into tiers, each depending only on earlier ones | a unit test on the tiering: the runtime, a module on it, a plugin on that, and an unrelated module left out |
|
||||
| The plan is written before any build is asked, and the handler returns | a controller test: a merge announcement produces a plan row with its tiers and one asked build per tier-0 module, and the handler is back before any outcome |
|
||||
| An outcome advances its plan; a complete tier asks the next; a tier with a rolled-out base waits for the machines' reports | store-backed tests over a two-tier plan: the first outcome marks built; the tier's roll-out gate holds until the report; the next tier is asked after |
|
||||
| A controller restarted mid-plan resumes it | a test that opens a plan, drops the handler, and advances from the store alone |
|
||||
| `status` lists open plans and names one that waits past the bound | the status JSON test with a fixture plan |
|
||||
| Live | a catalogue merge touching a base and a dependent: the plan's tiers in `status`, the base rolled before the dependent is asked |
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0157](0157-a-build-narrates-on-the-bus.md), [ADR 0121](0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md)
|
||||
- [Design 30 — The mesh updates itself on a push](../03-DESIGN/01-to-be/30-the-mesh-updates-itself-on-a-push.md)
|
||||
- Issues [184](../04-ISSUES/184-a-merge-announcement-blocks-the-controllers-receive-loop/00-report.md), [186](../04-ISSUES/186-a-release-across-repositories-is-an-order-in-a-persons-head/00-report.md), [187](../04-ISSUES/187-the-mesh-tells-nobody-when-it-stops-working/00-report.md)
|
||||
@@ -175,6 +175,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0159** — [A tool call names the machine it is for, every answer says which machine answered, and a holder's runtime serves its seat's verbs](0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md)
|
||||
- **0160** — [The mesh issues an assignment's subjects, and a runtime serves what it is issued](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md)
|
||||
- **0161** — [What deserves a seat: a role of a module is a seat, a singular fact about machines is a placement with a capacity of one, and a holder's software is the machine's](0161-what-deserves-a-seat.md)
|
||||
- **0162** — [A merge produces a tiered plan the mesh keeps, and a module's dependencies are one relation in the catalogue](0162-a-merge-produces-a-tiered-plan-the-mesh-keeps.md)
|
||||
|
||||
### Its tiers, from the bottom up
|
||||
|
||||
|
||||
Reference in New Issue
Block a user