diff --git a/04-ISSUES/300-a-new-module-is-not-built-at-its-merge/00-report.md b/04-ISSUES/300-a-new-module-is-not-built-at-its-merge/00-report.md new file mode 100644 index 00000000..da3435f0 --- /dev/null +++ b/04-ISSUES/300-a-new-module-is-not-built-at-its-merge/00-report.md @@ -0,0 +1,50 @@ +--- +status: located +opened: 2026-10-07 +located-in: [mesh-controller cmd/mesh-controller (upgrades.go, the merge handler)] +fixed-by: mesh-controller PR #122 +amended-design: +--- + +# 300. A new module is not built at its merge + +## Symptom + +A pull request to the catalogue added one module, `systemd-resolved` (ADR 0247). Its delivery plan, +posted with the pull request's check, said: + +> builds new: modules/systemd-resolved, sent nowhere + +At the merge, 20:14:37 UTC on 2026-10-07, the controller said: + +> novox/mesh-catalog merged into main (…); it changed nothing any module the mesh holds is built from + +The module was not built and not registered. `assign` then refused it with "no module of that name". +It took a hand `mesh-controller.build` to register it. So the plan said one thing and the merge did +another, with nothing on the mesh noting the difference. + +## Why it is a design issue + +ADR 0238 makes the change plan the planner's one answer for a pull request's check, the merge gate and +the merge. The plan counts a new module's directory as something the merge builds. The merge handler +read the same answer (`touchedBy`'s `added`) and dropped it: it only walks modules the catalogue already +holds, and a new one is not held until it is built. The code said so outright: "a new module, which a +check judges before it merges and a merge does not build". The take-in refuses a build off the trunk +with "merge it, and the merge builds it". So one part of the controller promised the build and another +never made it. + +Assigning a module is a person's act, and it needs the module registered first. A merge that leaves a +new module unregistered turns each new module into two acts by hand, and the plan says it is one. + +## How it is checked + +- `TestAMergeBuildsTheNewModuleItAdds` replays this case. A held catalogue module, and a merge whose + changed files are only a new module's directory: the merge asks the build seat for that directory, at + the branch merged into, from the repository as the mesh spells it, and opens no plan. It fails on the + commit before the fix. +- `TestAMergeBuildsItsNewModuleBesideItsPlan`: a merge that changes a held module and adds a new one + plans the held one and asks for the new one beside the plan. A merge that adds no module asks for + nothing. + +The trail is in [01-diagnosis.md](01-diagnosis.md). This is a core issue: at resolution it names its +replay, or says in `replay-none:` why none is possible (ADR 0237). diff --git a/04-ISSUES/300-a-new-module-is-not-built-at-its-merge/01-diagnosis.md b/04-ISSUES/300-a-new-module-is-not-built-at-its-merge/01-diagnosis.md new file mode 100644 index 00000000..38491e27 --- /dev/null +++ b/04-ISSUES/300-a-new-module-is-not-built-at-its-merge/01-diagnosis.md @@ -0,0 +1,45 @@ +# 300 — diagnosis + +## 2026-10-07: where the merge decides what to build + +The text "changed nothing any module the mesh holds is built from" is printed in one place: the +controller's merge handler (`SourceMoved`), when the merge moves no held module. The handler takes the +catalogue modules built from the merged repository and branch (`mergeCandidates`). It narrows them to +the ones whose directory a changed file is in (`touchedBy`). A plan is opened only for those and for +whatever stands on them. + +`touchedBy` is the one place the mesh maps a changed file onto a module (ADR 0238). It returns three +answers: the touched modules, the `added` directories (a module that no module of the mesh is known +from) and the `unread` files. The change plan and the pull request's check take `added` as "new" and say +it is built. The merge handler went through `whatTheMergeTouched`, which keeps only the first answer. + +Ruled out: + +- **The forge's announcement.** The plan for the same pull request found the new directory, from the same + paths and module directories the merge carries (issue 278). `added` was computed correctly; it was just + not used. +- **The take-in.** A hand build of the directory was taken in and registered as usual. Nothing between a + build's outcome and registration needed changing. + +## The fix + +At a merge into the branch its repository's modules follow, the handler asks the build seat for every +added directory: at that branch, from the repository spelled and found on the seat as the modules +already built from it are, and without waiting (`askNewModules`). The build's take-in registers the +module, as it does for a hand `build`. Nothing is sent: no machine is assigned a new module, and +assigning one stays a person's act. + +The new module is outside the merge's plan. A plan walks modules the catalogue holds, tier by tier, and +sends each built one to the machines running it. A new module has no machine and nothing standing on it, +and the plan's ask for a module the catalogue does not hold fails the plan. + +A merge read as history, or of a branch that no module follows, builds no new module either. The new +module's build follows the same candidates as everything else. + +## What it does not cover + +- **A repository the mesh holds nothing from.** There is no module whose source spelling and seat the new + module could use, so nothing is built. That is unchanged: "nothing the mesh holds reads it". +- **The catch-up of merges the bus did not hand over** (issue 266) judges a missed merge by the held + modules it would move. A merge that only adds a module, and that the bus skipped, is still not built + by the catch-up. Counting `added` there would ask again on every pass until the build landed.