Issue 320 and ADR 0253: a condition says itself to the operator in plain words
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

The operator could not read the mesh's notifications: they were the summary meant
for an agent, with ids, commits, keys and verbs, in UTC. The condition now carries
a headline, an explanation and a resolved line beside its summary.
This commit is contained in:
jochen
2026-10-08 13:28:50 +02:00
parent 73fa97efde
commit 7c49aa0ffc
4 changed files with 251 additions and 1 deletions
@@ -4,6 +4,7 @@ status: in-progress
code: [mesh-controller, mesh-host, mesh-tools, mesh-catalog, mesh-sdk, mesh-lab]
updated: 2026-10-08
decisions:
- 02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md
- 02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md
- 02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md
- 02-DECISIONS/0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
@@ -126,7 +127,8 @@ cleared on the first that does not. Designed in [to-be 48](48-a-module-says-how-
| kind | the condition kind, from the signals table, the probe registry or an event kind |
| subject | scope, id, and the machine it concerns when there is one |
| severity | `urgent` (needs the operator now) or `warning` (when they can) — two levels, no more |
| summary | one line in the mesh's words |
| summary | one line in the mesh's words, for whoever looks closer: it may name plans, commits and verbs |
| headline, explanation, resolved | what the operator reads, in plain words (ADR 0253): a few words naming the thing and what is wrong; one or two sentences on what happened, what it means and whether to act; the line said when it clears |
| evidence | the newest observations, at most ten, each with its time |
| source | the signals-table row, probe or event that raised it |
| raised, last observed | times; and how many observations since raised |
@@ -135,6 +137,17 @@ cleared on the first that does not. Designed in [to-be 48](48-a-module-says-how-
| silenced | until when, by whom, why — empty when not silenced |
| epoch | the controller lease epoch that last wrote it |
*Added 2026-10-08, [ADR 0253](../../02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md):*
**a condition says itself to the operator in plain words, beside its summary.** The headline, explanation
and resolved line are made where the condition is made: by the producer when it knows more than the
subject (a walk's modules, a failed unit's name, a hand-act's cause), otherwise by the wording registered
for the kind, otherwise from the scope, which is reported. All three are held to the plain rule when the
controller takes an observation: no hash, numbered id, dotted name, duration in code, clock time, flag,
markup or line break, and the content rule besides. Words that break the rule are replaced by the scope's
words, never refused. A condition a healer gave up on says in its explanation that it needs the operator.
Checked by the plain rule's test and by a lint over every condition the controller's suite raises, which
fails a kind without words.
**The life of one:**
```
@@ -284,6 +297,12 @@ section; to-be 46 §13 builds what follows from it.
secrets, and this seat's holder becomes the router (to-be 46 §2, §3, §8).
- **A message** is: the condition's key, its subject (a machine's role, a module, a plan), kind,
severity, the one-line summary, since when, and the verb that shows more. **Deduplicated by the key.**
*Amended 2026-10-08, [ADR 0253](../../02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md):*
a message reads in one glance. Its title is the condition's headline, with "Urgent:", "Still open:" or
"Now urgent:" before it where that applies. Its body is the explanation and since when, in the
operator's time zone (the messenger's setting `time-zone`). It carries no key, id, commit, verb or
markup: those stay in `conditions` and in the holder's history. A clearance is one line, the resolved
line and how long it was open. A digest lists titles. Still deduplicated by the key.
- **When:** on `condition-raised`; once more if still open after 1 hour (urgent) or 12 hours
(warning); on `condition-cleared`, by editing the first message where the channel can. A silenced
condition sends nothing. Urgent goes to both channels; warning to the desktop notifier when the