diff --git a/04-ISSUES/359-a-secret-given-reached-the-operator-as-needs-a-look/00-report.md b/04-ISSUES/359-a-secret-given-reached-the-operator-as-needs-a-look/00-report.md new file mode 100644 index 00000000..985cda29 --- /dev/null +++ b/04-ISSUES/359-a-secret-given-reached-the-operator-as-needs-a-look/00-report.md @@ -0,0 +1,44 @@ +--- +status: located +opened: 2026-10-09 +located-in: [mesh-controller (cmd/mesh-controller/desk_secret.go, internal/conditions/plain.go)] +fixed-by: +amended-design: +--- + +# 359. A secret given reached the operator as "needs a look", and nothing said why + +**Severity: medium.** The one notification meant to tell the operator that a module's own secret changed told +them nothing they could act on, and the fall back to borrowed words was silent everywhere. + +## Symptom + +On 2026-10-09 at 23:32 (local) the operator gave a chat module's bot token at the controller's terminal, with +the controller's `secret accept` and the value on standard input. The urgent desktop notification that followed +read, in full: + +> Novox needs a look — Needs you: read the details to see what to do. The mesh noticed a problem it calls +> "secret given" with novox. + +That is the scope's fallback wording ([ADR 0253](../../02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md)), +with the verdict kept as [ADR 0258](../../02-DECISIONS/0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md) +requires. It names neither the module nor what was given, and "read the details" sends the operator elsewhere +for the one thing the condition exists to say: a secret changed, and silencing it is the answer when they gave +it themselves. The condition was raised on every channel on purpose — the security review of 2026-10-09 wanted +a bot token changed by somebody else to be heard of — so the words are the whole of its value. + +## Two faults + +1. **The condition's own words broke the plain rule.** Its producer wrote a headline, an explanation, a needs + line and a resolved line of its own; the explanation carried the clock time of the giving. The plain rule + refuses a clock time (the channel says when, in the operator's time), so the keeper replaced all of them with + the scope's words. +2. **The replacement was silent.** The keeper tells a hook of every observation said in borrowed words. A test + suite sets that hook and fails on it; the serving controller sets none, so nothing — no log line, no + condition — said that a producer's words had been refused, or why. The condition's own unit test built the + observation without passing it through a keeper, so the suite's check never saw it either. + +## What is asked + +- The secret-given words pass the plain rule, with a test that holds them to it through a keeper. +- Words refused at run time are never silent: the keeper logs the kind and the reason, once per process. diff --git a/04-ISSUES/359-a-secret-given-reached-the-operator-as-needs-a-look/01-diagnosis.md b/04-ISSUES/359-a-secret-given-reached-the-operator-as-needs-a-look/01-diagnosis.md new file mode 100644 index 00000000..a6b79b8d --- /dev/null +++ b/04-ISSUES/359-a-secret-given-reached-the-operator-as-needs-a-look/01-diagnosis.md @@ -0,0 +1,36 @@ +# Diagnosis — issue 359 + +## 2026-10-09 — the cause, confirmed by a test + +A test in the controller's repository built the secret-given observation as the terminal does (the module, the +secret's name, "at the controller's terminal", 23:32) and held its words to the plain rule. The answer was +`explanation: a clock time or date (23:32)`. With the time removed and nothing else changed, the same words +passed, from the terminal and from a desk. The secret's name, written with a hyphen, is not refused by the rule; +the clock time alone was the cause. + +The keeper's fallback (`plainly` in the controller's conditions package) then took the scope's words: "… needs a +look", and "a problem it calls" the kind with its hyphens as spaces. Since the producer had a needs line, the +verdict stayed "Needs you:" with the fallback needs, "read the details to see what to do." + +## Why nothing said so + +The hook told of borrowed words (`Unworded`) is set only by test suites. In the serving controller it is unset, +and the keeper replaced the words without a trace. The suite-wide check in the controller's command package +fails any condition a test raises through a keeper with words that are not plain; the secret-given test called +the producer directly and asserted on its fields, so that check never ran on it. + +## The fix + +In the controller's repository, on its own pull request, linked to this issue: + +- The words name the module and the machine, say the secret's name as words ("telegram token"), say how it was + given, and carry no time: "New secret given for telegram on" the machine; "If you gave it, nothing else is + needed. If you did not, somebody else now holds what telegram acts with." The needs line says silencing is the + answer when the operator just gave it. Severity (urgent) and the one answer (silence for a week) are unchanged. +- A test holds the words to the plain rule and observes them through a keeper, so the suite's check covers them; + it fails against the old words with the cause above. +- With no hook set, the keeper logs which kind is said in borrowed words and why, once per kind and reason per + process, so a refusal in the running controller is visible in its journal. + +Ruled out: the bound on lengths (the old needs line was 102 characters of 120), the outward content rule (no +address or secret shape in the words), and the machine name.