Issue 275: a machine waiting for its push was said to be urgent

This commit is contained in:
jochen
2026-10-06 18:06:43 +02:00
parent 754e0fb8e0
commit 743de7572c
@@ -0,0 +1,92 @@
---
status: located
opened: 2026-10-06
located-in: [mesh-controller]
fixed-by: novox/mesh-controller PR #89
amended-design:
---
# 275. A machine waiting for its push was said to be urgent
## Symptom
On 2026-10-06 the operator assigned the backup module to the workstation and, a minute later, pushed
it. Between the two, the self-check's D1 (to-be 45 §4) raised an **urgent** condition on the
workstation, and the operator was notified on the desktop:
> nothing can be sent to the workstation: its declaration does not compose — restic needs a secret
> called "repository" and none was made for it
The push that followed made the secret, sent the declaration, and the machine applied it. Nothing had
been wrong. In the same window D3 raised that the workstation's `node-backup` holder was silent, and
D13 that the data declared there was unmeasured — about a holder that had not been sent yet.
Three conditions, one of them urgent, for the ordinary gap between `assign` and `push`. A condition
that fires on normal work teaches the operator to ignore conditions, which is the failure to-be 45
exists to prevent.
## Cause
**D1 asked a question the push answers.** A module's own secrets — a repository password, a database
superuser — are made by the push, on the send path, and only there: a question that wrote would
contend with the machine it was asking about (that is why compositions are `Reading` everywhere but
the send). D1 composes every machine with a `Reading` composition. For a module assigned since the
last push, that composition has no value for its own secret, and the composer refuses a secret it is
given no value for. D1 read every refusal as "this machine's declaration does not compose".
The refusal was right for what it guards — a declaration must never be sent with a credential missing
— and wrong as a finding: the push would have made the secret and composed. D1 could not tell "the
push makes this" from "the push fails on this", because the refusal was one untyped error.
**D3 and D13 read the assignment, not the delivery.** Both expected a holder on every machine its
module is assigned to. Assigned is not there: the holder exists once the machine was sent a
declaration carrying it and applied it.
## Fix
The fix is novox/mesh-controller PR #89. The rule:
**A finding about a machine is about what it was sent, or what the next push would send it — never
about the gap between an assignment and its push.**
- **D1 composes as the push would, making nothing.** An own secret not made yet is judged by the
push's own rules. One the push *makes* is composed with a stand-in, and named. One the push is
*refused* on — a bus credential nobody issued
([issue 203](../203-a-fresh-assignment-is-pushed-before-its-credential-exists/00-report.md)), a
machine with no sealing key, a given secret sealed to a key the machine no longer has — is refused
in the push's words. Everything else that does not compose, or that the node-engine's validator
refuses, is still found, and is still urgent: the stand-in hides nothing.
- **A machine that composes only with stand-ins is waiting for a push, not broken.** Nothing is said
for thirty minutes from when it began waiting — the oldest assignment since its last send, or that
send. Past that, a **warning**, `awaiting-push`, names what the next push will make and the verb
that sends it. Never urgent: nothing is wrong that a push does not fix.
- **The composer's refusal is typed.** A secret given no value is its own error, carrying the module
and the name; no caller reads the words.
- **D3 and D13 expect a holder once it was delivered.** A holder is expected to answer on a machine
when the machine's last send carried its module and the machine has reported acting on that
declaration, or has had ten minutes to. A machine sent nothing is expected to hold nothing the mesh
put there. A send recorded before the mesh kept what it carried is read as before.
## How it is checked
Tests in the controller run through its real stores:
- **The window.** A machine is pushed, then a module with an own secret is assigned to it. The read
composition refuses with the typed error; the composition asked ahead of the push composes, names
the secret, and makes nothing. D1 says nothing within the bound, a warning past it, and nothing once
the machine is pushed. Without the fix this test fails with the urgent text seen live.
- **A real failure stays urgent.** A module whose bus credential was never issued, a given secret
under an old key, and a machine whose set does not resolve beside a waiting secret are each still
urgent `uncomposable`.
- **When a holder is expected.** Never sent, sent without it, sent and not yet reported, reported,
and sent long ago without a report each give the expected answer, read from the send and report
records.
## Left open
- **To-be 45's D1 row** still states only "every machine's declaration composes". It should say that
a composition waiting only on what the next push makes is not a failure, and name the
`awaiting-push` warning. An amendment via playbook 02, not made here.
- **Ports.** A module's machine port is also chosen only on the send path. A composition that reads
composes an unchosen port at the module's own number; that has raised nothing so far, but it is the
same gap, and the stand-in rule would apply to it if a port ever made D1 refuse.