diff --git a/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/00-report.md b/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/00-report.md index fbe2734..9665d66 100644 --- a/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/00-report.md +++ b/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/00-report.md @@ -1,7 +1,9 @@ --- -status: open +status: resolved opened: 2026-09-26 -located-in: [mesh-host internal/apply, mesh-controller] +located-in: [mesh-host internal/link (the apply report line), mesh-controller cmd/mesh-controller (status and its all-well condition)] +fixed-by: mesh-host cbf5018, mesh-controller bfd983e +amended-design: --- # 125 — a hold is not a line in the apply report, and an operator flew blind into an outage diff --git a/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/01-resolution.md b/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/01-resolution.md new file mode 100644 index 0000000..5f7e104 --- /dev/null +++ b/04-ISSUES/125-a-hold-is-not-a-line-in-the-apply-report/01-resolution.md @@ -0,0 +1,70 @@ +# 125 — resolved: a hold is a line in the report, and it stops the mesh reading as well + +*2026-09-30.* + +## What the four surfaces say now + +The report named four surfaces, none of which carried the one sentence that mattered. Two of them +already did by the time this was picked up, and two did not. + +**1. The apply report says what it held** — this was missing, and is the line the operator was reading +when the count did not add up: + +``` +applied 330 resource(s), 16 held until their module is taken (route-proxy: 13, mailu: 3) +``` + +Grouped by module and ordered by name, because `take` acts on a module and that is the sentence an +operator needs. An apply that held nothing says nothing extra — a line reporting `0 held` on every +converged apply is one that stops being read. + +**2. `status` counts holds, and a hold breaks "all well"** — this was missing. Status now says: + +``` +15 resource(s) are held as found, because their module was assigned and never taken — so it is +running none of what it declares: + novox route-proxy (13), mailu (2) + + `take ` compares what runs against what it declares, and runs it +``` + +**And the sentence that was the fault no longer prints.** "all doing what they were told, all heard +from, running what the mesh would send them" was *true* for the whole outage, and acting on it stopped +the predecessor's proxy. A hold now suppresses it; being adopted still does not, and the difference is +deliberate — adopted is a mode somebody chose and can leave alone, a module assigned and never taken is +a half-finished action with nothing left to finish it. + +**3. `node show ` shows the node's own held list** — already true, and recorded here as checked +rather than assumed. It reads `held` from what the machine last reported, with an `as of` beside it, and +names each hold's kind, target, id, module, whether something other than the mesh has changed it, and +where an original was kept. + +**4. The push's count** is unchanged and now interpretable, which was the ask: `sent 346` against +`applied 330, 16 held until their module is taken (…)` is a pair a reader can resolve without opening a +file on the machine. + +## Where the data came from + +**The host already reported it.** `Held` has been on the wire since [ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md), +and the controller already recorded it and showed it in `node show`. Nothing needed a new field, a new +message or a migration — which is why this is additive, and why the report's framing (*"the semantics +are consistent and right; the reporting is what let them be forgotten"*) was exactly right. + +What was missing was that two surfaces never asked. Status read the mesh's take-time listing, so a +module assigned after that listing showed nothing at all; the apply line counted what it applied and +said nothing about the difference. + +## One thing deliberately not done + +**Status does not call a hold a fault.** It is correct behaviour, and a reader trained to see red for +something the mesh did right will stop reading. It is reported as work outstanding, with the command +that finishes it — and it withholds the all-well sentence, which is the part that carries the weight. + +## How it is checked + +- The apply line names the count and the module, and is empty when nothing is held (mesh-host). +- Status finds a hold from what the machine reported, end to end through the store. +- **A held module makes the all-well condition false**, asserted against the production condition + rather than a copy of it — that condition is now one named function for this reason. +- The JSON form carries a row per machine and module, and omits the field entirely when nothing is + held.