diff --git a/00-META/glossary.md b/00-META/glossary.md index 543a21d5..5c2493bc 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -421,6 +421,12 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234. holder of kind `desktop`) and not *bot* (that is one service's account). - **router** — the module that holds the `operator-channel` seat, orders the channels and checks every sender ([ADR 0234](../02-DECISIONS/0234-the-mesh-holds-a-conversation-with-its-operator.md)). +- **headline / verdict / explanation / resolved line** — what a condition says to the operator in plain + words: a few words naming the thing and what is wrong; "Nothing for you to do." or "Needs you:" and the + one thing the operator can do themselves (its **needs**); one or two sentences on what happened and what + it means; the line said when it clears. Its **actions** are the answers a notification offers. Carried by + the condition beside its **summary**, which keeps the plans, commits and verbs for whoever looks closer + ([ADR 0253](../02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md)). - **ask** — a request for the operator's input, of a declared kind (yes-no, one-of, text, number, date, acknowledge). An **authorising ask** is one whose answer performs an action; the controller holds it and checks its proofs (ADR 0234). diff --git a/02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md b/02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md new file mode 100644 index 00000000..5ba0c219 --- /dev/null +++ b/02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md @@ -0,0 +1,219 @@ +--- +topic: the mesh +status: accepted +date: 2026-10-08 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0234-the-mesh-holds-a-conversation-with-its-operator.md +--- + +# 253. A condition says itself to the operator in plain words beside its summary, says whether the operator is needed, and is answered from the notification + +## Context + +The operator could not read the mesh's notifications +([issue 320](../04-ISSUES/320-the-operator-could-not-read-the-meshs-notifications/00-report.md)). A walk +waiting for its delivery's word reached the laptop's desktop as five lines: the summary, with a commit, a +plan id and two verbs' syntax in backticks; then `about:`, `since:` in UTC, `key:` and `more:`. The operator +reads a notification between other work, and has to understand it in one pass: what happened, whether +they need to act, and what to do. + +A first rewording made the same popup plain: "openrazer delivery waiting to start", then "The change to +openrazer is merged and built, and has waited 56 minutes for mesh-delivery … Nothing to do yet; it becomes +urgent after 4 hours. To start it now, have an agent start it by hand." The operator read it and asked: +*what does that notification mean, I need to do something? I need to open a session and tell it to +deliver it?* Plain words were not enough. Three more things were wrong: + +- **It did not say plainly whether the operator was needed.** "Nothing to do yet" and "to start it now" + in one sentence read as an instruction. +- **It was shown at all.** A walk waiting under its bound needs nothing from anyone; the mesh says it + again when it does. +- **What it offered could not be done from where it was read.** "Have an agent start it by hand" sends the + operator to open a session, find the verb and say why. That is the work a notification should save. + +What the mesh had, on the main branches of 2026-10-08: + +- **A condition had one line of words.** To-be 45 §2 gave it a `summary`, "one line in the mesh's words". + The producers wrote it for whoever looks closer, an agent or a person at `conditions`, and named the + plan, the commit and the verb that acts. That reader needs those, and keeps needing them. +- **Issue 277 holds every summary to the operator channel's content rule** (ADR 0234 §6): no address, + domain, path or secret. Identifiers, verbs and markup pass that rule, so they reached the operator. +- **The message added detail of its own**, and a clearance repeated the whole summary. +- **The controller raises some sixty condition kinds**, from some eighty places in its code. Several know + more than the condition's subject says: a walk knows its modules, a failed unit its name, a hand-act its + cause. +- **The verbs that answer the common questions exist**, as seat verbs any granted principal may call: + `mesh-delivery.release` and `stop` (with why), the controller's `plans` with `go` and `stop` (with why), + `conditions` with `silence` (with why), and every machine's `node-service-manager.restart`. +- **The desk could not take an answer.** `node-notifier.send` had no actions. To-be 46 §11 designs them + (phase 2 of its build): the dunst holder offers them and emits the chosen one. Its router, asks and the + controller's `authorise request` and `answer` (phases 3 to 5) are not built. No dashboard exists where an + operator could release a delivery. The one way to act was an agent session. +- **ADR 0234 §8 says a desk click alone never authorises.** It defines three tiers, and *acknowledge* + "performs only what any granted principal may already do … it is not an authorisation, and needs no + proof". No verb declares a tier yet: that is phase 5. + +## Considered Options + +### Where the words are made + +1. **A formatter in each channel**, which turns the summary into something readable. It has to guess what + a summary means from its words, breaks silently with each new producer, and is needed again for each new + channel. Rejected as the mechanism; kept as the fallback for a condition that carries no plain words. +2. **Rewrite every summary in plain words.** The reader who looks closer loses the plan, the commit and the + verb. One line cannot serve both readers. Rejected. +3. **A table of words per condition kind in the messenger.** The router would know the controller's + kinds, which inverts ADR 0234, and could not see what only the producer knows. Rejected. +4. **The condition carries plain words beside its summary, made where the condition is made.** Chosen. + +### Whether the operator is needed + +1. **Leave it to each explanation's wording.** The first rewording did, and the operator could not tell. + Rejected. +2. **A verdict every explanation opens with**, from a field the condition carries (`needs`): empty is + "Nothing for you to do.", otherwise "Needs you:" and that one thing. Chosen: it is the first thing read, + it is the same on every channel, and a router can act on the field. + +### What is shown + +1. **Everything, gently** (the desktop's fifteen-minute rhythm for warnings). The rhythm still shows a + popup that asks nothing. Rejected. +2. **Nothing that needs nothing, while it is a warning.** It stays open in the controller's conditions and + in the channel's history, and is said when it needs the operator: at its urgent bound, or when its words + change to "Needs you:". Chosen. + +### How the operator answers + +1. **Name the verb in the words**, for the operator to call. It sends them to a session. Rejected. +2. **Wait for the conversation design** (to-be 46 phases 2 to 6) and say "Needs you:" with nothing to press + until then. Honest, but leaves the common answers, which already exist as verbs, a session away for + weeks. Rejected for the answers that exist. +3. **The condition names its answers as actions; the desk offers them; the operator channel's holder + calls the verb.** It builds the part of to-be 46 phase 2 these answers need (the desk's actions, and the + chosen one said as an event), and nothing of the router or of authorising. Chosen. + +## Decision + +1. **Every condition carries, beside its summary, words for the operator:** + - **headline**: a few words naming the thing and what is wrong, at most sixty characters, such as + "openrazer not working on ``". It is the title of every message about the condition. + - **needs**: one sentence, the one thing the operator can do themselves — "release it, or stop it.", + "log out of every session on `` and log in again." — or empty when nothing is needed. + - **explanation**: opened by its **verdict**, "Nothing for you to do." when `needs` is empty and + "Needs you:" and `needs` otherwise, then one or two sentences on what happened and what it means. + - **resolved line**: the one line said when it clears. + - **actions**: the answers the operator may give from the notification, each a label and the seat verb + with its arguments (Release, Stop, Start, Restart, Silence for a week). An argument `why` left empty is + the operator channel's to fill. + + The **summary** keeps its role and its words. The events and the `conditions` verb carry all of it. +2. **They are made where the condition is made.** A producer that knows more than the subject says them + itself; otherwise the wording registered for the kind does; otherwise the scope's words do, and the + kind is reported. **A kind without words is a defect of its producer**, as a summary that carries an + address is (issue 277). +3. **The plain rule.** Headline, needs, explanation and resolved line name no hash, numbered id, dotted + name (a key, a verb, a unit's file name), duration in code, clock time, date or zone, command flag, + markup or line break, and pass the content rule. **They never send the operator elsewhere**: no + "agent", no "session", and `needs` never says "by hand". `needs` is one sentence ending in a full stop. + Words that break the rule are replaced by the scope's words and reported; a condition is never refused + for its words. +4. **Whether the operator is needed is decided per condition, by its producer.** "Nothing for you to do." + is said where the mesh handles it or it passes by itself; "Needs you:" where only the operator can do + the next thing. A condition a healer gave up on (`resolver: operator` with attempts) that needed nothing + now needs the operator: its verdict becomes "Needs you:". +5. **A warning that needs nothing is not sent.** The operator channel keeps it open, its history says it + was kept quiet, and it is said the moment it needs the operator: when it becomes urgent at its bound, or + when its words change to "Needs you:". An urgent condition is always said. At this decision these kinds + need nothing while they are warnings: + - a walk waiting for its delivery's word (under four hours), or stuck at a step; + - a machine silent, not confirming a change, waiting for a push, its tools silent or unreachable, or its + network check failing or rewritten; + - a healer wanted, a listener behind, the bus being upgraded, a check that could not run, slow status, + outdated facts or instructions, a merge or build not picked up; + - a provider failing or silent, the mesh's own software restarting, an update put back on its first + machine, data not measured, and unused data the operator chose to keep. +6. **The notification offers its answers, and the operator channel performs them.** + - The holder of `operator-channel` gives each action, and **Details**, a token, and sends them with the + message. A message that offers answers is never folded into a digest. + - **The desk:** `node-notifier.send` takes actions (token and label). The dunst holder offers them + (dunst's actions: a middle click, or its context menu) and says the chosen token as its event + `action-chosen` {id, token, machine}. It is that module's event until seats publish events of their + own (to-be 46 phase 3), when it becomes the seat's. + - **The holder calls the action's verb itself**, with a why that names the operator, the channel and the + label ("the operator chose Release on the desktop notification …"), then edits the notification to say + what came of it: done, or the refusal in words. Details shows the summary, the key and since when, on + the machine that asked; a line the content rule refuses is said withheld. + - **The tier.** None of these verbs declares an authorising tier: each is a verb any granted principal + may already call, which is ADR 0234's *acknowledge*. So a desk click performs it, as ADR 0234 allows. + When to-be 46 phase 5 declares a tier for one of them, its answer goes through `authorise request` and + `answer` instead, and the desk asks for the code. Nothing here lowers ADR 0234's bar. + - **Telegram** shows the words and no buttons until it holds the `intake` seat (to-be 46 phase 6). +7. **Times are the operator's.** The body ends with since when, in the operator's time zone (the + messenger's setting `time-zone`): a clock time today, a weekday this week, a date before. A clearance is + one line: "Resolved:", the resolved line, and how long it was open. No message carries a key, an id, a + commit, a verb or markup; those stay in the controller's `conditions`, the holder's history and Details. +8. **A condition without plain words** (a controller older than this, or a module's `notify` without a + headline) is said from its summary, with its code spans taken out, and is never kept quiet. `notify` + accepts the same words. + +## Consequences + +- **The popup of issue 320 is not shown.** A walk waiting under four hours needs nothing; it is in + `conditions` and in `operator-channel.history`, marked as kept quiet. Past four hours it is urgent and + reads "openrazer delivery waiting to start", then "Needs you: start it, or stop it." and why, then "Since + 12:40.", with the answers Start, Stop and Details. The operator chooses Start, and the notification says + "Start: done." +- A held delivery offers Release and Stop; a failed service, Restart; failed units nobody manages, and data + that shrank or went, Silence for a week; a module whose account waits for a new login says so, which no + button can do. +- **Some "Needs you:" have no button yet**, and say what to do in words: approving a retirement, deciding + where a module's data lives, undoing a catalogue change, restarting the controller, putting the bus back. + Their verbs either take what the operator must see first (`retire approve` takes the set it approves) or + need a terminal on another machine. Each is a next step: an action where a verb exists, the authorising + path of to-be 46 phase 5 where it must be proven. +- An answer is performed by the messenger's own grant. Its manifest invokes exactly the verbs its actions + name, and a new action names a new verb there. The verb's own record (a hand-act, a delivery's + transition) says who chose it, through the why. +- dunst waits for the answer in its bundle. A restart of that bundle ends the waits it held: the + notification stays and its answers do nothing then. A notification closed or expired says nothing. +- A new condition kind needs words, including its verdict. The controller's suite fails until it has them. +- The words are English, as everything else is. + +## How it is checked + +- **The controller** (`mesh-controller`): + - `internal/conditions/plain_test.go`: the plain rule refuses each shape it names; an explanation opens + with its verdict; words that send the operator to an agent, a session or "by hand" are refused; a + condition carries its producer's words, else its kind's, else its scope's, and the events carry them + all; an escalated condition turns "Nothing for you to do." into "Needs you:", once. + - `cmd/mesh-controller/plain_words_test.go`: the real notifications of issue 320, raised from the same + facts, each with its exact verdict, explanation and actions — a walk waiting under its bound (nothing) + and past it (Start, Stop, and the plan each calls), a module unhealthy (Restart, the unit and the + account's manager), relogin needed (no button), failed units (Silence), a healer wanted (nothing), a + delivery held (Release, Stop); and every registered wording plain for a subject of every scope. + - **A lint over the whole suite** (`sayable_test.go`): every condition raised in any test has words of + its producer or its kind, and they pass the rule. +- **The messenger** (`mesh-catalog`, `modules/messenger`), `words_test.go`: the same notifications decoded + from the events the controller sends: a warning that needs nothing is not shown and is kept as quiet in + the history; each shown one has its exact title, body and answers; choosing an answer calls exactly the + seat verb and arguments its condition names, with the operator's why, and edits the notification; the + clearance is one line. A quiet warning is said when it becomes urgent. Details are shown where they were + asked. The holder's tests fail a message that carries a key. +- **dunst** (`mesh-catalog`, `modules/dunst`), `actions_test.go`: `send` with actions answers the id before + the answer comes, asks `notify-send` for each action and to wait, says the chosen token as + `action-chosen` with the id and machine, says nothing for a notification closed without an answer or with + an answer it did not offer, and refuses actions it cannot carry. +- **Live**, once delivered: `operator-channel.history` shows warnings kept quiet; the next held delivery + shows Release and Stop on the desk; choosing one is in `mesh-delivery.show` with the operator's why. + +## References + +- [Issue 320](../04-ISSUES/320-the-operator-could-not-read-the-meshs-notifications/00-report.md) +- [Issue 277](../04-ISSUES/277-one-unanswered-question-was-an-urgent-alert-nobody-could-read/00-report.md) + (a summary held to the content rule; this record's lint sits beside that one) +- [ADR 0234](0234-the-mesh-holds-a-conversation-with-its-operator.md) (the channels, the content rule, the + tiers), [ADR 0227](0227-the-core-holds-nine-rules-each-checked-and-is-built-to-them-in-six-phases.md) + (rule 6: the mesh says when it is wrong), [ADR 0239](0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md) + (a walk waits for its delivery's word) +- [To-be 45](../03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md) §2 and §5; + [to-be 46](../03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md) §11 and §13 diff --git a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md index 9f1c4187..ab3df3b8 100644 --- a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +++ b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md @@ -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,9 @@ 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, needs, explanation, resolved | what the operator reads, in plain words (ADR 0253): a few words naming the thing and what is wrong; the one thing the operator can do themselves, or nothing; the explanation, opened by its verdict ("Nothing for you to do." or "Needs you:" and that thing); the line said when it clears | +| actions | the answers a notification offers (Release, Stop, Start, Restart, Silence for a week), each a label and the seat verb the operator channel calls when it is chosen (ADR 0253) | | 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 +138,18 @@ 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, and says whether the operator +is needed.** The headline, needs, explanation, resolved line and actions 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. The explanation opens with its verdict. All of it is 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; nothing that sends the operator to an agent or a session; 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 that needed +nothing now 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 +299,16 @@ 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, opened by its verdict, and since + when, in the operator's time zone (the messenger's setting `time-zone`). It offers the condition's + actions and Details; the desk takes the answer (`node-notifier.send` gains actions, and the dunst holder + says the chosen one as its event), and the holder calls the action's verb with the operator's why. It + carries no key, id, commit, verb or markup: those stay in `conditions`, the holder's history and + Details. **A warning that needs nothing is not sent**: it is kept, and said when it becomes urgent or + needs the operator. A clearance is one line, the resolved line and how long it was open. A digest lists + titles, and a message that offers answers is never in one. 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 diff --git a/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md b/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md index 578efbe0..29d5606f 100644 --- a/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md +++ b/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md @@ -2,8 +2,9 @@ layer: to-be status: designed code: [] -updated: 2026-10-06 +updated: 2026-10-08 decisions: + - 02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md - 02-DECISIONS/0234-the-mesh-holds-a-conversation-with-its-operator.md - 02-DECISIONS/0227-the-core-holds-nine-rules-each-checked-and-is-built-to-them-in-six-phases.md --- @@ -448,6 +449,11 @@ needs the lost factor's code. So: - **`node-notifier.send` gains actions** (token, label). It still answers at once; the dunst holder listens for the chosen action and emits it as an event on its node seat. + *Amended 2026-10-08, [ADR 0253](../../02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md):* + built ahead of the router for the answers a condition offers. Until seats publish events of their own + (phase 3) the chosen token is the dunst module's event `action-chosen` {id, token, machine}, and the + output seat's holder takes it and calls the action's verb. Only verbs that declare no tier are offered + so; one that declares a tier waits for phase 5. - The router's desktop adapter, holding `channel/desktop` and `intake/desktop`, turns that into a `choice` envelope. - **For `text`, `number`, `date` and codes**, the notification's single action opens the diff --git a/04-ISSUES/320-the-operator-could-not-read-the-meshs-notifications/00-report.md b/04-ISSUES/320-the-operator-could-not-read-the-meshs-notifications/00-report.md new file mode 100644 index 00000000..94e0c2c0 --- /dev/null +++ b/04-ISSUES/320-the-operator-could-not-read-the-meshs-notifications/00-report.md @@ -0,0 +1,94 @@ +--- +status: located +opened: 2026-10-08 +located-in: [mesh-controller internal/conditions (the condition's words), mesh-controller cmd/mesh-controller (every producer of a condition), mesh-catalog modules/messenger (compose, the digest, the answers), mesh-catalog modules/dunst (the desk's actions)] +fixed-by: novox/mesh-controller PR #141, novox/mesh-catalog PR #126 +amended-design: 03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +--- + +# 320. The operator could not read the mesh's notifications + +## Symptom + +On 2026-10-08 the operator said they could not read the mesh's desktop notifications. One, exactly as +it appeared on the laptop, with the plan id and the commit replaced here by placeholders: + +> CLEARED after 26 min: the walk of novox/mesh-catalog `` has waited 56m0s for mesh-delivery's +> word to start: `mesh-delivery.show` for the delivery that landed as `` says why; `plans go +> --why ...` starts it by hand +> about: plan `` (waiting) +> since: 2026-10-08 10:40 UTC +> key: plan.``.waiting +> more: conditions show plan.``.waiting + +What is wrong with it, as the operator listed it: + +- It is a wall of text, five lines and some sixty words for one fact. +- It is full of identifiers: a plan id, a commit, a condition key and a verb's syntax. +- Its time is UTC, not the operator's. +- Its backticks are markdown, which a desktop popup shows literally. +- A clearance repeats the whole problem. +- It does not say in plain words what happened, whether the operator needs to act, or what to do. + +The same shape held for every open condition on the mesh that day: a module unhealthy on the laptop +("… its unit `.service` failed in the account's own service manager (exit-code)"), three failed +units on a workstation, a cause repaired by hand thirty-five times, five deliveries held past their bound, +each with `mesh-delivery.show @` in its line. + +**A first fix was read, and still failed.** Reworded in plain words, the same popup said "… Nothing to do +yet; it becomes urgent after 4 hours. To start it now, have an agent start it by hand." The operator asked: +*what does that notification mean, I need to do something? I need to open a session and tell it to deliver +it?* It did not say plainly whether they were needed; it was shown although it needed nothing; and the only +thing it offered was a session. + +## Cause + +**The condition had one line of words, and it served two readers.** To-be 45 §2 gave a condition one +`summary`, "one line in the mesh's words", and §5 made a message out of that summary with the key, +subject, kind, since-when and the verb that shows more. The summary was written for whoever looks closer +— an agent at the mesh MCP server, a person reading `conditions` — and rightly carried what they act on: +the plan, the commit, the verb. Nothing in the condition was written for the operator who reads a popup +between other work, so the channel showed the agent's line to the operator. + +Issue 277 had already found the narrower half: a summary carried detail the content rule refuses, and the +controller now holds every summary to that rule. Identifiers, verbs and markup pass that rule — they are +not addresses, paths or secrets — so they reached the operator untouched. + +**Nothing said whether the operator was needed, or let them answer.** No field of a condition said whether +the operator was needed, so every warning was shown, and an explanation could imply an instruction. The +verbs that answer the common cases existed (release or stop a delivery, start a waiting walk, restart a +service, silence a condition), but the desk could not take an answer: `node-notifier.send` had no actions, +and the conversation design that adds them (to-be 46) was not built. The one way to act was a session. + +**The message added its own detail.** The messenger's compose added `about:`, `since:` in UTC, `key:` and +`more:` lines to every message, and a digest added each key in brackets. A clearance was the whole +summary again behind "CLEARED after …". + +## Fix + +[ADR 0253](../../02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md): +every condition carries, beside its summary, a **headline**, what it **needs** of the operator, an +**explanation** opened by its verdict ("Nothing for you to do." or "Needs you:"), a **resolved line** and +its **actions**, made where the condition is made and held to a checked plain rule. A warning that needs +nothing is not sent. The desk offers the actions, and the operator channel's holder performs the chosen one +through its seat verb. Every channel says the time in the operator's zone; a clearance is one line. + +- novox/mesh-controller: the condition's three new fields, the plain rule, a wording for every kind the + controller raises, the richer words at the producers that know more (a walk's modules, a unit's name, a + hand-act's cause, a delivery's repository), and a lint over every condition the suite raises. +- novox/mesh-catalog: the messenger shows the words, keeps quiet what needs nothing, offers and performs + the answers, says since when in the operator's zone (the new setting `time-zone`), says a clearance in + one line, names nothing but titles in a digest, and lets a module give the same words through `notify`; + dunst's `send` takes actions and says the chosen one as its event. + +## How it is checked + +ADR 0253 §How it is checked: the plain rule's own test, a test per real notification above showing the +words before and after, the suite-wide lint, and the messenger's tests of the four messages as the desktop +shows them. + +## Design + +To-be 45 §2 gains the fields, and §5's message is no longer the key, kind and verb but the headline, the +explanation with its verdict, since when and the answers; a warning that needs nothing is not sent. To-be +46 §11 notes the desk's actions built ahead of its router.