Issue 324 and ADR 0258: a desk click performs only an acknowledgement

Review found that ADR 0253 let any process at the operator's session release,
stop or start a delivery in the operator's name, and that its tokens, fallback
words and quiet warnings failed on the paths that go wrong.
This commit is contained in:
jochen
2026-10-08 14:40:02 +02:00
parent 1c987e0df7
commit dd149a72c0
5 changed files with 197 additions and 4 deletions
@@ -132,6 +132,13 @@ What the mesh had, on the main branches of 2026-10-08:
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.
> **The mechanism changed — 2026-10-08, by [ADR 0258](0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md).**
> What stands: a notification offers answers and the operator channel performs them. What moved: a desk click
> performs only an acknowledgement (Details, Silence for a week) until answers are authorised; release, stop,
> start and restart are said in words. The reading of *acknowledge* below, and the promise to honour a tier
> "when one exists", were enforced by nothing and are replaced there, with one-time tokens, a verdict that is
> never weakened and a change of words that is said.
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.
@@ -0,0 +1,120 @@
---
topic: the mesh
status: accepted
date: 2026-10-08
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md
---
# 258. A desk click performs only an acknowledgement, an answer is a one-time ticket, and a verdict is never weakened
## Context
ADR 0253 was merged before its review finished
([issue 324](../04-ISSUES/324-a-desk-click-could-answer-in-the-operators-name/00-report.md)). The review found
that its §6 let a desk click release, stop or start a delivery and restart a service in the operator's
name. Any process of the operator's account can choose a desktop notification's action: dunst's own
`dunstctl action` does it, and every agent that runs as the operator can run it. A click therefore proves
that something at the operator's session chose, not that the operator did. ADR 0234 §8 holds exactly that:
"a desk click alone never authorises". ADR 0253 §6 read *acknowledge* as any verb no table had tiered, and
said a tier would be honoured once one existed. No tier field exists in the code, so nothing enforced that.
The review also found that answer tokens never expired and could be reused or replayed; that words refused by
the plain rule weakened "Needs you" to "Nothing for you to do"; that the rule refused sound words; that a
quiet warning whose words came to need the operator was never said; and that the desk's answers died with a
ten-second notification or a restart of the notifier's bundle.
## Considered Options
1. **Keep §6, and ask for the operator's code at the desk** (ADR 0234's P2). That is to-be 46 phase 5: the
factor, its enrolment and recovery, and the controller's authorise verbs. Not built, and not small.
Rejected for now; it is the way the other answers come back.
2. **Trust the click, and say so in the why.** The record would still say an action was the operator's
when anything at the session could have chosen it. Rejected: failure must be loud, and a false
attribution is quiet.
3. **Offer only what is an acknowledgement in the plain sense:** Details, which performs nothing, and
silencing a condition, which ADR 0234 names as acknowledge ("silencing, announced"). Everything else is
said in words. Chosen.
## Decision
1. **A desk click performs only an acknowledgement**, until answers are authorised (to-be 46 phase 5):
- **Details**, which performs nothing;
- **Silence for a week**, through `conditions silence` with a why and the cause `operator-answer`.
The operator channel's holder keeps this as an explicit allow-list and refuses everything else. A
condition's actions outside it are never offered, and a token for one is refused without a call. The
controller offers no other action. **Release, stop, start and restart are said in words**: "Needs you:
release it, or stop it." with no button. This replaces ADR 0253 §6's reading of *acknowledge* and its
promise to honour a tier.
2. **An answer is a one-time ticket.** A token is made for one message of one condition, together with the
other tokens of that message; a newer message's tokens replace them. It is bound to the machines the
message was shown on, good for a day, and used once. It is refused for a condition that cleared or is
silenced, from another machine, used, expired or unknown. One event is acted on once, by its id. **A
refused answer is said on the desk where it was chosen**, never dropped.
3. **The why says what is known**: "chosen on the desktop notification … on `<machine>` (a desk click:
whoever was at the operator's session)". A silence chosen on the desk is a decision, not a repair, and
does not count toward a healer wanted.
4. **A verdict is never weakened.** When a producer's words break the plain rule, the scope's words are said.
If the producer needed the operator, its verdict stays "Needs you:" with "read the details to see what to
do.", and its sound answers stay.
5. **The plain rule refuses no plain words.** It refuses "agent" and, in what the operator needs, "by hand",
but not "session". It reads "e.g." and "i.e." as words. A number is a hash only with letters a to f in it.
An explanation too long is cut at a word, not refused.
6. **A change of words is said.** The controller emits `condition-changed` (change `words`) when what a
condition needs or offers changes. The holder says a warning it kept quiet when its words now need the
operator, whether it learns that from the event or from a reading of the state. It also says once, after
a day, a warning still kept quiet, so nothing stays silent for ever.
7. **The desk's answers stay and come back.** A notification that offers answers stays until it is
dismissed. When the notifier's bundle starts, it says `started`, and the holder shows the open
notifications that offer answers there again, with fresh tokens.
8. **The operator's zone is always known**: the holder carries the zone data, whatever the machine has
installed.
## Consequences
- The delivery-waiting popup past its bound reads "Needs you: start it, or stop it. …" and offers Details
only. Starting or stopping it is not possible from the desk until phase 5. That is the honest state,
and the words say where the decision lies.
- Failed services nobody manages, and data that shrank or went, still offer Silence for a week.
- ADR 0253's other rules stand: the plain words, the verdict, the quiet rule, the time zone and the one-line
clearance. Its §6 and the consequence "the verb's own record … says who chose it" are replaced here.
- An answer chosen in a notification shown before a newer message about the same condition is refused,
and the refusal is shown.
## How it is checked
- **The messenger** (`mesh-catalog`, `modules/messenger/cmd/messenger/answers_test.go`):
- only an acknowledgement is answerable: Release, Restart, Start and a silence carrying another argument
are refused by the allow-list, never offered, and a token for one put in the state is refused without a
call and said on the desk;
- at most four answers, Details included;
- the same token twice is refused the second time, and the same event is acted on once;
- a token is refused from another machine, when unknown, past its day, and for a condition silenced or
cleared;
- a verb the mesh refuses is said on the notification;
- a words-only change to "Needs you" after a quiet time is said, through the event and through a reading of
the state, and a quiet warning is said once after a day;
- a notifier that started again is offered the open answers with fresh tokens, the old ones refused.
`words_test.go` holds the real notifications of issue 320 to their exact words and answers.
- **dunst** (`modules/dunst/cmd/dunst-tools/actions_test.go`): a notification with answers is sent with no
expiry; the bundle says `started`. Checked live on the laptop on 2026-10-08: `notify-send --print-id --wait`
writes the id into a pipe at once, and "Wait timeout expired" when the notification expired. The latter is
not a token, so it says nothing.
- **The controller** (`mesh-controller`):
- `internal/conditions/plain_test.go`: the words the rule refused wrongly pass, and a long explanation is
cut; refused words keep their verdict and answers; a change of words is a `condition-changed`.
- `cmd/mesh-controller/plain_words_test.go`: the module, held-delivery and waiting conditions offer no
answer; the relogin words and the kind `relogin-needed` (ADR 0254) are held to the plain rule; a silence
with the cause `operator-answer` does not count toward a healer wanted, and one by hand still does.
## References
- [Issue 324](../04-ISSUES/324-a-desk-click-could-answer-in-the-operators-name/00-report.md),
[issue 320](../04-ISSUES/320-the-operator-could-not-read-the-meshs-notifications/00-report.md)
- [ADR 0253](0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md) (§6 replaced
here), [ADR 0234](0234-the-mesh-holds-a-conversation-with-its-operator.md) §8 (the tiers, and the desk
click)
- [To-be 46](../03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md) §11 and §13 phase 5
@@ -129,7 +129,7 @@ cleared on the first that does not. Designed in [to-be 48](48-a-module-says-how-
| severity | `urgent` (needs the operator now) or `warning` (when they can) — two levels, no more |
| 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) |
| actions | the answers a notification offers, each a label and the seat verb the operator channel calls when it is chosen (ADR 0253); a desk click performs only an acknowledgement — Details, Silence for a week — until answers are authorised (ADR 0258) |
| 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 |
@@ -304,7 +304,11 @@ section; to-be 46 §13 builds what follows from it.
"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
says the chosen one as its event), and the holder calls the action's verb with a why saying it was a desk
click. *Amended 2026-10-08, [ADR 0258](../../02-DECISIONS/0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md):*
only an acknowledgement (Details, Silence for a week) is offered or performed; each answer is a one-time
token bound to its condition and machine, good for a day; a change of words to "Needs you" is a
`condition-changed`, and a quiet warning is said once after a day. 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
@@ -452,8 +452,10 @@ needs the lost factor's code. So:
*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.
output seat's holder takes it and calls the action's verb.
*Amended 2026-10-08, [ADR 0258](../../02-DECISIONS/0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md):*
only an acknowledgement (Details, silencing) is offered so: any process of the account can choose an
action, so a click is never taken for the operator's word. Every other answer 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
@@ -0,0 +1,60 @@
---
status: located
opened: 2026-10-08
located-in: [mesh-catalog modules/messenger (the answers, the quiet rule), mesh-catalog modules/dunst (the desk's actions), mesh-controller internal/conditions (the plain rule, the change of words)]
fixed-by:
amended-design: 03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md
---
# 324. A desk click could answer in the operator's name
## Symptom
ADR 0253 (issue 320) was merged at the operator's request before its review had finished. The
controller's part began to roll out; the build of the operator channel's holder and the desktop notifier was
published and waited for its delivery's word, so it was not yet live. The review then found:
1. **A desk click could release, stop or start a delivery, or restart a service, in the operator's name.**
Any process of the operator's account can choose a desktop notification's action (`dunstctl action`), so
a click proves nothing about who chose. The holder then recorded "the operator chose Release". ADR 0234 §8
says a desk click alone never authorises. ADR 0253 §6 said these verbs were acknowledgements because none
declared a tier, and that a tier would be honoured once one existed; no tier field exists in any code, so
that promise was enforced by nothing.
2. **An answer's token lived for ever.** It could be used twice, for a condition cleared or silenced, from
another machine, and a redelivered event would be acted on again.
3. **Refused words weakened the verdict.** When a producer's words broke the plain rule, the condition was
said in its scope's words, which needed nothing: "Needs you" became "Nothing for you to do", and the
answers were dropped. The rule also refused sound words: "session" (as in "log out of every session",
the words a new login needs), "e.g.", any number of seven digits or more, and an explanation one word too
long.
4. **A warning kept quiet could stay quiet for ever.** A change of its words to "Needs you" emitted no event
(only severity, resolver and silence did), the holder's reading of the state did not look for it, and
nothing said a quiet warning however long it stayed open.
5. **The desk's answers died.** A warning's notification expired in dunst's ten seconds with its answers; a
restart of the notifier's bundle ended the waits it held, and a click after it did nothing, silently.
6. The time zone depended on the machine having zone data installed. ADR 0253 said "the verb's own record
says who chose it", which was false for a restart (it takes no why). A silence chosen on the desk would
have counted as a hand repair toward a healer wanted.
## Cause
The decision outran its review: it read ADR 0234's *acknowledge* ("what any granted principal may already
do") as covering any verb no table had tiered, without asking what a desk click proves. The tokens, the
fallback, the event for a change of words and the notifier's restart were each designed for the path that
works, not for the path that fails.
## Fix
[ADR 0258](../../02-DECISIONS/0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md)
on the branch `fix/notifications-after-review` in hq, mesh-controller and mesh-catalog.
## How it is checked
ADR 0258 §How it is checked: a test for every point above, including the same token twice, a cleared or
silenced token, a forged and another machine's answer, a refused verb, a words-only change after a quiet time
through the event and the reading of the state, the relogin words held to the plain rule, the fallback keeping
its verdict and answers, and the four-answer limit.
## Design
To-be 45 §2 and §5 and to-be 46 §11 say the desk performs only an acknowledgement.