diff --git a/00-META/glossary.md b/00-META/glossary.md index 5c2493bc..1c464740 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -176,6 +176,9 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02 **Uses:** node, machine, module, seat, setting (Module), manifest (Module). - **assignment** — a module put on a node, with the settings that node gives it. +- **pending assignment** — an assignment of a module whose build is still running, kept and made when that + build registers the module, or ended with why when it does not + ([issue 325](../04-ISSUES/325-assign-said-no-such-module-while-its-build-ran/00-report.md)). - **scope** — where a seat or a claim holds: `node`, `site` or `mesh`. `node` is also the scope's value in a manifest. - **capacity** — how many holders a seat takes. A capacity-1 seat is exclusive. A higher-capacity seat diff --git a/04-ISSUES/325-assign-said-no-such-module-while-its-build-ran/00-report.md b/04-ISSUES/325-assign-said-no-such-module-while-its-build-ran/00-report.md new file mode 100644 index 00000000..65a78eba --- /dev/null +++ b/04-ISSUES/325-assign-said-no-such-module-while-its-build-ran/00-report.md @@ -0,0 +1,92 @@ +--- +status: located +opened: 2026-10-08 +located-in: [mesh-controller cmd/mesh-controller (acts.go, the assign act; upgrades.go, the merge's build request), mesh-controller internal/inventory (the build request and pending assignment records)] +fixed-by: mesh-controller#150, mesh-lab#68 +replay: R325 +amended-design: +--- + +# 325. Assign said "no module of that name" while the module's build ran + +## What was observed + +On 2026-10-08 the catalogue's pull request adding a new module, `sensors`, merged. Since issue 300's fix, a +merge that adds a module asks the build seat for it, and the build registers the module when its outcome is +taken in. About a minute after the merge, `mesh-controller.assign {node: "g14", module: "sensors"}` +answered: + +> no module of that name: sensors + +A few minutes later the same call worked. The build had not finished the first time. + +The operator read the answer as "you forgot to register the module" and asked whether `assign` could offer +to register it when it is not found. + +## Why it is an issue + +The answer was true and misleading. The controller had itself asked for the build a minute earlier, and it +answered as if it had never heard of the module. It kept no record of a build it had asked for until the +outcome came back, so `assign` could not tell three different situations apart: + +1. a build is in flight, and the module will exist in minutes; +2. the module's directory is known (a merge or a person asked for its build) and it was never registered, + because the build failed, was not registered, never answered, or could not be asked; +3. the name is unknown: a typo, or a module in no repository the mesh builds from. + +Each needs a different act from the person, and the one answer given fitted only the third. Issue 300 +removed the second act by hand from adding a module; this answer put it back, as a wait and a retry that +nothing told the person to make. + +## Fix + +**The controller keeps every build request it makes, and `assign` reads them** (mesh-controller#150). A build request is kept when a merge asks for a new module, when a person runs `build`, and when +a plan asks for a tier. It is kept with its repository, directory, branch and the merge's commit. A merge +whose request could not be made is kept too, with the reason. Requests older than 30 days are deleted. +The outcome is the `build` record with the same id. + +An assignment of a module the catalogue does not hold now says which case it is in: + +- **A build is in flight**: "sensors is not registered yet: it is being built from + novox/mesh-catalog@3da80a4b (modules/sensors) since 15:38:17, as build build-…; it can be assigned once + that build registers it". The assignment is kept as a **pending assignment** (new glossary word). When + the build registers the module, the assignment is made, by the same act and with the same checks as by + hand. If the build fails, is not registered, or says nothing within two hours, the pending assignment + expires with the build's own words. `status` lists every pending assignment that is waiting, and every one + that ended in the last day; while one waits, or one expired or was refused, status does not call the mesh + well. `unassign` withdraws a pending assignment. +- **Known, not built**: said with what came of the last request (failed, with the builder's first line; + no outcome; could not be asked), and refused. The answer offers `build`, and `assign` with the new + argument `build: "true"`. That argument asks for the build from where the last request was made and keeps + the assignment pending on it. +- **Unknown**: refused as "no module of that name", as before. The answer adds the closest names the mesh + holds and says how a module in a repository is built. + +**Pending by default, with no argument to ask for it.** An assignment is what a person meant, and the +controller already keeps it when its machine does not resolve. Nothing is sent until a push, or a walk if +the module's first upgrade announcement finds it assigned. `unassign` undoes it. Asking for a +second act once the build lands would bring back the two acts by hand that issue 300 removed. A build is +never asked for by default: it runs on another machine, and a directory whose last build failed is a fault +to look at before it is something to retry. + +Several modules in one act are judged together (ADR 0207). So an act naming a module that is not registered +is refused whole, with each module's case said, and nothing is kept pending. + +**Not covered.** The controller cannot read the forge, so a module directory on a trunk that no merge +announced since this fix, and no person asked to build, still reads as unknown. The refusal says how to +build it. + +## How it is checked + +- `TestReplay325` replays the live case: a merge adds `modules/sensors`, `assign laptop sensors` runs + before the build's outcome, and then the outcome registers it. The assignment must say the build and be + made when the build lands. On the commit before the fix it fails with "no module of that name: sensors". + It is R325 in mesh-lab's replays register (mesh-lab#68). +- `cmd/mesh-controller/pending_test.go` has one test per case: a build in flight (the answer, the pending + record, status and its JSON, the build applying it), a failed build expiring it and then reading as known + and not built, `build: "true"` asking from the last request's source, an unknown name with the closest + names, a several-module act refused whole, `unassign` withdrawing, and a build that says nothing expiring + at the next look while a module registered meanwhile is assigned. The seat passes `build` and `unassign` + refuses it. + +No design names what `assign` answers for a module it does not hold, so no design is amended.