ADR 0261: an assignment made while its module's build runs is kept pending

The review of mesh-controller#150 found the controller now makes an
assignment after the person's act, which no record decided. Decide it,
cite it from the glossary and to-be 45, and correct issue 325's account
of what is settled where and what is sent.
This commit is contained in:
jochen
2026-10-08 16:46:40 +02:00
parent 63754ca874
commit 3a09387c8e
4 changed files with 135 additions and 38 deletions
@@ -0,0 +1,90 @@
---
topic: what runs on it
status: accepted
date: 2026-10-08
deciders: jochen
reconstructed: false
---
# 261. An assignment made while its module's build runs is kept pending, and the controller makes it
## Context
Since issue 300's fix, a merge that adds a module asks the build seat for it, and the module is registered
when the build's outcome is taken in, minutes later. On 2026-10-08 `assign` was called in that window and
answered "no module of that name"
([issue 325](../04-ISSUES/325-assign-said-no-such-module-while-its-build-ran/00-report.md)). The controller
kept no record of a build it had asked for until the outcome came back, so it could not tell a build in
flight from a typo.
Until now an assignment was always made at the person's act, and refused there if it could not be made.
Keeping it for later means the controller makes an assignment after the act, without the person present.
That is a new kind of act, and it needs a rule.
## Considered Options
1. **Refuse, and say the build is running.** The person waits and calls `assign` again. Rejected: it brings
back the second act by hand that issue 300 removed, and nothing reminds the person to make it.
2. **Keep the assignment only when asked** (an argument such as `wait`). Rejected: a person who knows to ask
for it already knows the build is running; the one who does not is the one this is for.
3. **Keep the assignment by default while a build is in flight, and have the controller make it when the
build registers the module.** Chosen.
## Decision
1. **The controller keeps every build request it makes**, once the ask is made and never before, with who
asked it (a merge, a plan, a person's `build`, `assign`), the repository, the directory, the branch and
the merge's commit. A request whose ask could not be made, or whose asker stopped waiting, is kept with
why. A request is kept for 30 days.
2. **`assign` of a module the catalogue does not hold says which case it is in:**
- *a build is in flight*: a kept request for the module's directory has no outcome yet (within two
hours of asking), or its build succeeded within the last five minutes and is being registered;
- *known, not built*: there is a kept request, and its build failed, was not registered, gave no outcome,
or could not be asked;
- *unknown*: no module registered and no kept request by that name. The answer claims only that, and
names the closest names.
3. **In the first case the assignment is kept as a pending assignment**, with no argument needed. In the
second, `assign` refuses, and its `build` argument asks for the build from where the last request came
from and keeps the assignment pending on it. If a pending assignment of that module to that machine is
already waiting, it is made to wait for the new build; no build is asked that nothing waits on.
4. **The controller makes a pending assignment by the same act as a person's `assign`**, with the same
checks, when the build registers the module. It first takes the machine's hold and claims the pending
assignment, so a withdrawal cannot land in between. A refusal then ends it as refused. Making it sends
nothing, as a person's `assign` sends nothing.
5. **A pending assignment never waits silently.** It expires with the builder's own words when its build
fails or is not registered, and with a sentence when no outcome comes within two hours. The controller
settles pending assignments on a tick of its own, every minute, and on each build's outcome. A read,
such as `status`, settles nothing.
6. **An assignment that was not made is a condition** (`assignment-not-made`, a warning the operator
resolves). It stays open until it is answered: the module is assigned to that machine later, it is
assigned again and pending on another build, or a person takes it back with `unassign`.
7. **`unassign` withdraws a waiting pending assignment.** It is refused, and says so, while the controller
is making one. Ended pending assignments are deleted after 30 days, and a machine's go with it.
## Consequences
- One kind of assignment is now made by the controller, not at the person's act. The person's intention is
still the cause: nothing is assigned that a person did not ask for.
- `status` lists open pending assignments (and is not well while one is open) and those ended in the last
day; the condition carries an assignment not made until it is answered.
- The controller cannot read the forge. A module directory that no merge announced since this record and no
person asked to build still reads as unknown; the refusal says how to build it.
- A build being registered reads as in flight for five minutes after its outcome. A build recorded and never
registered reads so only after that.
## How it is checked
- mesh-controller `cmd/mesh-controller/pending_test.go`: each case of point 2; the claim under the hold and
the refused withdrawal (point 4, 7); a failed ask that never reads as in flight and the asker kept
(point 1); `build` re-pointing a waiting assignment (point 3); a status read that settles nothing, the
tick that does, and a silent build expiring (point 5); the condition raised, and cleared by `unassign` or
a later assignment (point 6); ended rows deleted after 30 days.
- mesh-controller `internal/inventory/pending_test.go`: a pending assignment goes with its machine.
- `TestReplay325`, R325 in mesh-lab's replays register: the live case of issue 325.
## References
- [Issue 325](../04-ISSUES/325-assign-said-no-such-module-while-its-build-ran/00-report.md); issue 300.
- novox/mesh-controller#150, novox/mesh-lab#68.
- [ADR 0207](0207-a-module-depends-on-the-node-seats-that-apply-its-resources.md): modules assigned together
are judged together, so an act naming a module not registered is refused whole.