Issue 325: assign said no module of that name while the module's build ran

Register the symptom and the fix in mesh-controller#150 (replay R325 in
mesh-lab#68), and name the pending assignment in the glossary.
This commit is contained in:
jochen
2026-10-08 16:46:40 +02:00
parent 1782f1ad74
commit 63754ca874
2 changed files with 95 additions and 0 deletions
+3
View File
@@ -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
@@ -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.