messenger: every message still shown keeps its own Details (issue 371) #174

Merged
mesh-admin merged 2 commits from fix/messenger-details-per-message into main 2026-10-10 11:48:50 +00:00
Contributor

Fixes issue 371 (Details refused "it belongs to no open message" on Telegram and the desk).

Cause

choices() in modules/messenger/cmd/messenger/answers.go deleted a record's earlier Details ticket on a channel kind every time a newer message about the same record was composed. Seen on 2026-10-10: seat.operator-channel.unasked was sent on Telegram as "escalated" at 12:57:49 and as "reminder" at 12:58:20, so the first message's Details was dead; the two taps at 13:30:34 and 13:30:36 were refused. takeWords also dropped every ticket when a condition's actions changed.

Fix

  • Each message gets a ticket of its own; older ones stand. Still one-time, still kind-bound, still refused for a cleared or silenced record, and Details still performs nothing (ADR 0258/0259).
  • Bounded: at most MostDetails (8) per record and kind, none older than TokenFor (24 h), pruned whenever a new one is made.
  • A ticket pushed out by the bound is refused in plain words: "... this message is older than the last few about it: tap Details on the newest one".
  • A change of the condition's actions keeps Details tickets (Details names no action).

Tests

details_test.go: three warnings in a row (one problem said three times, and three problems), Details works on each once and a second tap is refused; the bound in number and time; Details survives an actions change; Details on a message with an open ask issues no warrant and leaves the ask open. Mutation-checked: restoring the old replacement, the old reset in takeWords, removing the bound, or not marking a ticket used each fails a new test.

Acceptance: the operator receives three warnings in a row and Details works on each of them, once each.

Fixes issue 371 (Details refused "it belongs to no open message" on Telegram and the desk). ## Cause `choices()` in `modules/messenger/cmd/messenger/answers.go` deleted a record's earlier Details ticket on a channel kind every time a newer message about the same record was composed. Seen on 2026-10-10: `seat.operator-channel.unasked` was sent on Telegram as "escalated" at 12:57:49 and as "reminder" at 12:58:20, so the first message's Details was dead; the two taps at 13:30:34 and 13:30:36 were refused. `takeWords` also dropped every ticket when a condition's actions changed. ## Fix - Each message gets a ticket of its own; older ones stand. Still one-time, still kind-bound, still refused for a cleared or silenced record, and Details still performs nothing (ADR 0258/0259). - Bounded: at most `MostDetails` (8) per record and kind, none older than `TokenFor` (24 h), pruned whenever a new one is made. - A ticket pushed out by the bound is refused in plain words: "... this message is older than the last few about it: tap Details on the newest one". - A change of the condition's actions keeps Details tickets (Details names no action). ## Tests `details_test.go`: three warnings in a row (one problem said three times, and three problems), Details works on each once and a second tap is refused; the bound in number and time; Details survives an actions change; Details on a message with an open ask issues no warrant and leaves the ask open. Mutation-checked: restoring the old replacement, the old reset in `takeWords`, removing the bound, or not marking a ticket used each fails a new test. Acceptance: the operator receives three warnings in a row and Details works on each of them, once each.
mesh-admin added 1 commit 2026-10-10 11:45:09 +00:00
messenger: every message still shown keeps its own Details, bounded in number and age (issue 371)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds messenger → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
bb96f4d163
A newer message about the same problem (an escalation, a reminder, a re-send) replaced the Details
ticket of the one before it on that channel kind, so tapping Details on any but the newest was refused
"it belongs to no open message". Each message now keeps a ticket of its own, used once, at most
MostDetails per record and kind and none older than TokenFor; one pushed out is refused in words that
send the operator to the newest message. A change of the condition's actions no longer drops Details,
which names no action.
Author
Contributor

Delivery novox/mesh-catalog@6a8c718f440c — delivered since 2026-10-10T11:53:52Z

Delivery plan — builds messenger → novox; no bus step

  • tier 0: messenger
  • novox: receives messenger

Machines

  • messenger on novox: passed — healthy 3 times over 3m44s
  • messenger on the rest running it: sent

Transitions

  • 2026-10-10 11:46 (new) → proposed (announced): novox/mesh-catalog#174's head announced
  • 2026-10-10 11:48 proposed → checked (checked): the verdict names this commit
  • 2026-10-10 11:48 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-10 11:49 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-10 11:49 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
  • 2026-10-10 11:53 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-catalog@6a8c718f440c` — **delivered** since 2026-10-10T11:53:52Z **Delivery plan** — builds messenger → novox; no bus step - tier 0: messenger - novox: receives messenger **Machines** - messenger on novox: passed — healthy 3 times over 3m44s - messenger on the rest running it: sent **Transitions** - 2026-10-10 11:46 (new) → proposed (announced): novox/mesh-catalog#174's head announced - 2026-10-10 11:48 proposed → checked (checked): the verdict names this commit - 2026-10-10 11:48 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-10 11:49 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-10 11:49 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit - 2026-10-10 11:53 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
jschoubben added 1 commit 2026-10-10 11:46:37 +00:00
messenger: ask whether a channel is ready before composing its messages, so failed tries make no Details tickets (issue 371)
mesh/merge-gate pass: builds messenger → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
6a8c718f44
The review found that each try at an unready channel composed its held messages and so made a Details
ticket for each, and enough tries pushed out the tickets of messages already shown.
Author
Contributor

Merge check at bb96f4d1

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: messenger)
  • mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)

Delivery plan — builds messenger → novox; no bus step

  • tier 0: messenger
  • novox: receives messenger

Run by the build seat on g14 as build-1791632735293304621 (builds --log build-1791632735293304621).

--- the gate: messenger
messenger: ok, 6 tool(s), invokes seat:mesh-controller.conditions, seat:mesh-controller.root-free, keeps state open, sent, asks, identities; WARNING: settings ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339)
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-10T11:45:18Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-10T11:45:18Z

machines:
  - a machine (f35): composes
  - holds mesh-dns-resolver (idt): composes
  - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
  - a machine (yvbpsm): composes

a merge would rebuild 1 module(s) in 1 tier(s): messenger
--- its merge-check.sh (go toolchain)
ok  	git.novox.be/novox/mesh-catalog/checks/words	0.003s
words: 12 retired words and 70 daemons' lines, none in the 37423 strings of 637 files
ok  	git.novox.be/novox/mesh-catalog/checks/agenthome	0.009s
agent-home: no archive and no file written into under the agent's home
ok  	git.novox.be/novox/mesh-catalog/checks/paths	0.005s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/messenger
ok  	messenger/cmd/messenger	1.760s
--- its merge-check-typescript.sh (typescript toolchain)
**Merge check** at `bb96f4d1` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: messenger) - `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript) **Delivery plan** — builds messenger → novox; no bus step - tier 0: messenger - novox: receives messenger Run by the build seat on g14 as `build-1791632735293304621` (`builds --log build-1791632735293304621`). ``` --- the gate: messenger messenger: ok, 6 tool(s), invokes seat:mesh-controller.conditions, seat:mesh-controller.root-free, keeps state open, sent, asks, identities; WARNING: settings ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339) long-running resources without health: 0 files asking for a setting without saying whether it is trusted: 1 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-10T11:45:18Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-10T11:45:18Z machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 1 module(s) in 1 tier(s): messenger --- its merge-check.sh (go toolchain) ok git.novox.be/novox/mesh-catalog/checks/words 0.003s words: 12 retired words and 70 daemons' lines, none in the 37423 strings of 637 files ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.009s agent-home: no archive and no file written into under the agent's home ok git.novox.be/novox/mesh-catalog/checks/paths 0.005s paths: no module names another's state on its machine (3 known, listed to go) long-running resources without health: 70, as kept testing modules/messenger ok messenger/cmd/messenger 1.760s --- its merge-check-typescript.sh (typescript toolchain) ```
Author
Contributor

Merge check at 6a8c718f

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: messenger)
  • mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)

Delivery plan — builds messenger → novox; no bus step

  • tier 0: messenger
  • novox: receives messenger

Run by the build seat on ace as build-1791632797532113347 (builds --log build-1791632797532113347).

--- the gate: messenger
messenger: ok, 6 tool(s), invokes seat:mesh-controller.conditions, seat:mesh-controller.root-free, keeps state open, sent, asks, identities; WARNING: settings ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339)
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-10T11:45:18Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-10T11:45:18Z

machines:
  - a machine (f35): composes
  - holds mesh-dns-resolver (idt): composes
  - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
  - a machine (yvbpsm): composes

a merge would rebuild 1 module(s) in 1 tier(s): messenger
--- its merge-check.sh (go toolchain)
ok  	git.novox.be/novox/mesh-catalog/checks/words	0.002s
words: 12 retired words and 70 daemons' lines, none in the 37423 strings of 637 files
ok  	git.novox.be/novox/mesh-catalog/checks/agenthome	0.012s
agent-home: no archive and no file written into under the agent's home
ok  	git.novox.be/novox/mesh-catalog/checks/paths	0.005s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/messenger
ok  	messenger/cmd/messenger	1.612s
--- its merge-check-typescript.sh (typescript toolchain)
**Merge check** at `6a8c718f` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: messenger) - `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript) **Delivery plan** — builds messenger → novox; no bus step - tier 0: messenger - novox: receives messenger Run by the build seat on ace as `build-1791632797532113347` (`builds --log build-1791632797532113347`). ``` --- the gate: messenger messenger: ok, 6 tool(s), invokes seat:mesh-controller.conditions, seat:mesh-controller.root-free, keeps state open, sent, asks, identities; WARNING: settings ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339) long-running resources without health: 0 files asking for a setting without saying whether it is trusted: 1 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-10T11:45:18Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-10T11:45:18Z machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 1 module(s) in 1 tier(s): messenger --- its merge-check.sh (go toolchain) ok git.novox.be/novox/mesh-catalog/checks/words 0.002s words: 12 retired words and 70 daemons' lines, none in the 37423 strings of 637 files ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.012s agent-home: no archive and no file written into under the agent's home ok git.novox.be/novox/mesh-catalog/checks/paths 0.005s paths: no module names another's state on its machine (3 known, listed to go) long-running resources without health: 70, as kept testing modules/messenger ok messenger/cmd/messenger 1.612s --- its merge-check-typescript.sh (typescript toolchain) ```
mesh-admin merged commit f6c9e06cdf into main 2026-10-10 11:48:50 +00:00
mesh-admin deleted branch fix/messenger-details-per-message 2026-10-10 11:48:51 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#174