Every declared tool check (hq ADR 0240) failed with "no link to the bus is open" while the node-engine's link was up. Seen live on 2026-10-08: the condition module.sensors.g14.unhealthy said sensors.watch ... its tool sensors_health: asking it: no link to the bus is open, 296 look(s) in a row, while g14/sensors.sensors_summary answered through the mesh MCP server.
Cause
Queue.AskTool reaches the bus through a type assertion to ToolBus. Only OverNATS had Ask; the link the engine actually attaches is natsLink (dialNats), which had Health, Report, Alive, ReadKey and Ping but no Ask. The assertion failed on every live link, and the failure was reported with the same words as "no link open". The unit tests used fake links that implemented Ask, so nothing caught it. The bus grant (mesh.mod.<module>.tool.<tool>.<node>) is already composed by the controller.
Change
natsLink.Ask, through OverNATS.Ask, saying "nothing on this machine answers" for no responders and naming a refused grant as Ping does.
Compile-time assertions that natsLink is a Bus, HealthBus, ToolBus, Asker and Asked: a link that drops one does not build.
AskTool tells "no link open" apart from "the link open now cannot ask a tool".
Tests
TestAToolCheckIsAskedOnTheLinkTheEngineHolds (no server needed): fails on main with "the NATS link was not asked", passes here.
TestNatsAToolCheckIsAskedOnTheLink (with MESH_TEST_NATS): a served tool reads healthy through the queue on a real nats-server 2.11; a tool nothing serves says so. Run locally and passing.
merge-check.sh passes locally (race detector on, MESH_TEST_NATS set).
Affected live: only sensors on g14 has a tool check failing now (mesh-controller.conditions, 2026-10-08 14:36 UTC).
## Why
Every declared tool check (hq ADR 0240) failed with "no link to the bus is open" while the node-engine's link was up. Seen live on 2026-10-08: the condition `module.sensors.g14.unhealthy` said `sensors.watch ... its tool sensors_health: asking it: no link to the bus is open, 296 look(s) in a row`, while `g14/sensors.sensors_summary` answered through the mesh MCP server.
## Cause
`Queue.AskTool` reaches the bus through a type assertion to `ToolBus`. Only `OverNATS` had `Ask`; the link the engine actually attaches is `natsLink` (`dialNats`), which had `Health`, `Report`, `Alive`, `ReadKey` and `Ping` but no `Ask`. The assertion failed on every live link, and the failure was reported with the same words as "no link open". The unit tests used fake links that implemented `Ask`, so nothing caught it. The bus grant (`mesh.mod.<module>.tool.<tool>.<node>`) is already composed by the controller.
## Change
- `natsLink.Ask`, through `OverNATS.Ask`, saying "nothing on this machine answers" for no responders and naming a refused grant as `Ping` does.
- Compile-time assertions that `natsLink` is a `Bus`, `HealthBus`, `ToolBus`, `Asker` and `Asked`: a link that drops one does not build.
- `AskTool` tells "no link open" apart from "the link open now cannot ask a tool".
## Tests
- `TestAToolCheckIsAskedOnTheLinkTheEngineHolds` (no server needed): fails on main with "the NATS link was not asked", passes here.
- `TestNatsAToolCheckIsAskedOnTheLink` (with `MESH_TEST_NATS`): a served tool reads healthy through the queue on a real nats-server 2.11; a tool nothing serves says so. Run locally and passing.
- `merge-check.sh` passes locally (race detector on, `MESH_TEST_NATS` set).
Affected live: only sensors on g14 has a tool check failing now (`mesh-controller.conditions`, 2026-10-08 14:36 UTC).
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
The queue asks a tool through a ToolBus assertion that only OverNATS satisfied; the
link the engine actually holds (natsLink) had no Ask, so every tool check failed as
"no link to the bus is open" while the link was up (hq issue 331). Give natsLink Ask,
assert at build time every optional interface the queue expects of it, and tell a link
that cannot ask apart from no link.
Deliverynovox/mesh-host@5e189c2fcad2 — delivered since 2026-10-08T15:10:11Z
Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
tier 0: mesh-host
ace: receives mesh-host
g14: receives mesh-host
novox: receives mesh-host
shanks: receives mesh-host
Machines
mesh-host on ace: passed — healthy 3 times over 2m18s
mesh-host on the rest running it: sent
Transitions
2026-10-08 15:01 (new) → proposed (announced): novox/mesh-host#57's head announced
2026-10-08 15:03 proposed → checked (checked): the verdict names this commit
2026-10-08 15:03 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-08 15:07 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-08 15:07 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
2026-10-08 15:10 delivering → delivered (done): its walk is done: 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-host@5e189c2fcad2` — **delivered** since 2026-10-08T15:10:11Z
**Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step
- tier 0: mesh-host
- ace: receives mesh-host
- g14: receives mesh-host
- novox: receives mesh-host
- shanks: receives mesh-host
**Machines**
- mesh-host on ace: passed — healthy 3 times over 2m18s
- mesh-host on the rest running it: sent
**Transitions**
- 2026-10-08 15:01 (new) → proposed (announced): novox/mesh-host#57's head announced
- 2026-10-08 15:03 proposed → checked (checked): the verdict names this commit
- 2026-10-08 15:03 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-08 15:07 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-08 15:07 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
- 2026-10-08 15:10 delivering → delivered (done): its walk is done: 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/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
mesh/repo-check: PASS — its merge-check.sh passed
Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
tier 0: mesh-host
ace: receives mesh-host
g14: receives mesh-host
novox: receives mesh-host
shanks: receives mesh-host
Run by the build seat on novox as build-1791470666991761059 (builds --log build-1791470666991761059).
--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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-08T14:40:42Z 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-08T14:40:42Z
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): mesh-host
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.020s
ok github.com/novox/mesh-host/cmd/mesh-host 6.699s
ok github.com/novox/mesh-host/examples 1.023s
ok github.com/novox/mesh-host/internal/accounts 1.044s
ok github.com/novox/mesh-host/internal/apply 26.770s
ok github.com/novox/mesh-host/internal/bootstrap 2.964s
ok github.com/novox/mesh-host/internal/bundle 1.019s
ok github.com/novox/mesh-host/internal/declaration 1.066s
ok github.com/novox/mesh-host/internal/firewall 1.850s
ok github.com/novox/mesh-host/internal/identity 1.633s
ok github.com/novox/mesh-host/internal/image 1.022s
ok github.com/novox/mesh-host/internal/inventory 1.020s
ok github.com/novox/mesh-host/internal/link 10.394s
ok github.com/novox/mesh-host/internal/liveness 2.546s
ok github.com/novox/mesh-host/internal/network 1.044s
ok github.com/novox/mesh-host/internal/outward 1.039s
ok github.com/novox/mesh-host/internal/profile 1.516s
ok github.com/novox/mesh-host/internal/reachable 1.023s
ok github.com/novox/mesh-host/internal/store 2.054s
ok github.com/novox/mesh-host/internal/system 1.021s
ok github.com/novox/mesh-host/internal/tunnel 1.022s
ok github.com/novox/mesh-host/internal/units 1.014s
ok github.com/novox/mesh-host/internal/upgrade 1.906s
ok github.com/novox/mesh-host/internal/witness 33.519s
ok github.com/novox/mesh-host/packaging 1.013s
ok github.com/novox/mesh-host/validate 1.013s
**Merge check** at `809ab4cf`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step
- tier 0: mesh-host
- ace: receives mesh-host
- g14: receives mesh-host
- novox: receives mesh-host
- shanks: receives mesh-host
Run by the build seat on novox as `build-1791470666991761059` (`builds --log build-1791470666991761059`).
```
--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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-08T14:40:42Z 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-08T14:40:42Z
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): mesh-host
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.020s
ok github.com/novox/mesh-host/cmd/mesh-host 6.699s
ok github.com/novox/mesh-host/examples 1.023s
ok github.com/novox/mesh-host/internal/accounts 1.044s
ok github.com/novox/mesh-host/internal/apply 26.770s
ok github.com/novox/mesh-host/internal/bootstrap 2.964s
ok github.com/novox/mesh-host/internal/bundle 1.019s
ok github.com/novox/mesh-host/internal/declaration 1.066s
ok github.com/novox/mesh-host/internal/firewall 1.850s
ok github.com/novox/mesh-host/internal/identity 1.633s
ok github.com/novox/mesh-host/internal/image 1.022s
ok github.com/novox/mesh-host/internal/inventory 1.020s
ok github.com/novox/mesh-host/internal/link 10.394s
ok github.com/novox/mesh-host/internal/liveness 2.546s
ok github.com/novox/mesh-host/internal/network 1.044s
ok github.com/novox/mesh-host/internal/outward 1.039s
ok github.com/novox/mesh-host/internal/profile 1.516s
ok github.com/novox/mesh-host/internal/reachable 1.023s
ok github.com/novox/mesh-host/internal/store 2.054s
ok github.com/novox/mesh-host/internal/system 1.021s
ok github.com/novox/mesh-host/internal/tunnel 1.022s
ok github.com/novox/mesh-host/internal/units 1.014s
ok github.com/novox/mesh-host/internal/upgrade 1.906s
ok github.com/novox/mesh-host/internal/witness 33.519s
ok github.com/novox/mesh-host/packaging 1.013s
ok github.com/novox/mesh-host/validate 1.013s
```
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
The evidence read "asking it: asking <subject>": the words are now the link's own.
A deadline is said as the time the check gave it. A refusal is said only when the bus
refused this question, not an earlier one under a grant since widened. merge-check.sh
runs the tests that need a bus against a throwaway nats-server when the toolchain has
one, and says so when it does not (hq issue 331).
Correction to the description: on main, TestAToolCheckIsAskedOnTheLinkTheEngineHolds fails at its second step, with "on a link that cannot ask: no link to the bus is open", not with "the NATS link was not asked". That message was from a partial revert.
The real-bus test TestNatsAToolCheckIsAskedOnTheLink is skipped by the build seat, because nothing sets MESH_TEST_NATS there. merge-check.sh now starts a throwaway nats-server with JetStream and exports MESH_TEST_NATS when the toolchain has one, and prints "NOT RUN AGAINST A BUS" when it does not. Locally, with a nats-server on PATH, the real-bus tests ran and passed. The Go toolchain image (mesh-tools-go) has no nats-server, so in CI they remain skipped. This is recorded as open in hq issue 331.
The error is said once: the evidence no longer reads "asking it: asking ". A deadline is now said as "no answer within 5s" (the check's own timeout), with a test in internal/liveness.
refusal() now names a refusal only when the connection's last error changed during this question. The client keeps its last error for the life of the connection, so before this a refusal under a grant since widened was said again. The same applies to ReadKey and Ping.
Grants verified live with nats.nats_users: node.g14 may publish mesh.mod.sensors.tool.sensors_health.g14, and g14.node-tools subscribes to mesh.mod.sensors.tool.> with responses allowed. g14/sensors.sensors_health answers healthy through the mesh MCP server.
Review follow-up (5e189c2):
- **Correction to the description:** on main, `TestAToolCheckIsAskedOnTheLinkTheEngineHolds` fails at its second step, with "on a link that cannot ask: no link to the bus is open", not with "the NATS link was not asked". That message was from a partial revert.
- The real-bus test `TestNatsAToolCheckIsAskedOnTheLink` is skipped by the build seat, because nothing sets `MESH_TEST_NATS` there. `merge-check.sh` now starts a throwaway nats-server with JetStream and exports `MESH_TEST_NATS` when the toolchain has one, and prints "NOT RUN AGAINST A BUS" when it does not. Locally, with a nats-server on PATH, the real-bus tests ran and passed. The Go toolchain image (`mesh-tools-go`) has no nats-server, so in CI they remain skipped. This is recorded as open in hq issue 331.
- The error is said once: the evidence no longer reads "asking it: asking <subject>". A deadline is now said as "no answer within 5s" (the check's own timeout), with a test in `internal/liveness`.
- `refusal()` now names a refusal only when the connection's last error changed during this question. The client keeps its last error for the life of the connection, so before this a refusal under a grant since widened was said again. The same applies to `ReadKey` and `Ping`.
- Grants verified live with `nats.nats_users`: `node.g14` may publish `mesh.mod.sensors.tool.sensors_health.g14`, and `g14.node-tools` subscribes to `mesh.mod.sensors.tool.>` with responses allowed. `g14/sensors.sensors_health` answers healthy through the mesh MCP server.
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
mesh/repo-check: PASS — THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; its merge-check.sh passed
Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
tier 0: mesh-host
ace: receives mesh-host
g14: receives mesh-host
novox: receives mesh-host
shanks: receives mesh-host
Run by the build seat on shanks as build-1791471703998020066 (builds --log build-1791471703998020066).
--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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-08T15:00:23Z 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-08T15:00:23Z
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): mesh-host
--- THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.014s
ok github.com/novox/mesh-host/cmd/mesh-host 5.420s
ok github.com/novox/mesh-host/examples 1.018s
ok github.com/novox/mesh-host/internal/accounts 1.044s
ok github.com/novox/mesh-host/internal/apply 4.506s
ok github.com/novox/mesh-host/internal/bootstrap 1.647s
ok github.com/novox/mesh-host/internal/bundle 1.048s
ok github.com/novox/mesh-host/internal/declaration 1.092s
ok github.com/novox/mesh-host/internal/firewall 1.869s
ok github.com/novox/mesh-host/internal/identity 1.143s
ok github.com/novox/mesh-host/internal/image 1.039s
ok github.com/novox/mesh-host/internal/inventory 1.043s
ok github.com/novox/mesh-host/internal/link 10.350s
ok github.com/novox/mesh-host/internal/liveness 2.081s
ok github.com/novox/mesh-host/internal/network 1.072s
ok github.com/novox/mesh-host/internal/outward 1.059s
ok github.com/novox/mesh-host/internal/profile 1.539s
ok github.com/novox/mesh-host/internal/reachable 1.047s
ok github.com/novox/mesh-host/internal/store 1.244s
ok github.com/novox/mesh-host/internal/system 1.052s
ok github.com/novox/mesh-host/internal/tunnel 1.057s
ok github.com/novox/mesh-host/internal/units 1.044s
ok github.com/novox/mesh-host/internal/upgrade 1.170s
ok github.com/novox/mesh-host/internal/witness 4.778s
ok github.com/novox/mesh-host/packaging 1.049s
ok github.com/novox/mesh-host/validate 1.050s
**Merge check** at `5e189c2f`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
- `mesh/repo-check`: **PASS** — THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; its merge-check.sh passed
**Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step
- tier 0: mesh-host
- ace: receives mesh-host
- g14: receives mesh-host
- novox: receives mesh-host
- shanks: receives mesh-host
Run by the build seat on shanks as `build-1791471703998020066` (`builds --log build-1791471703998020066`).
```
--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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-08T15:00:23Z 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-08T15:00:23Z
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): mesh-host
--- THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.014s
ok github.com/novox/mesh-host/cmd/mesh-host 5.420s
ok github.com/novox/mesh-host/examples 1.018s
ok github.com/novox/mesh-host/internal/accounts 1.044s
ok github.com/novox/mesh-host/internal/apply 4.506s
ok github.com/novox/mesh-host/internal/bootstrap 1.647s
ok github.com/novox/mesh-host/internal/bundle 1.048s
ok github.com/novox/mesh-host/internal/declaration 1.092s
ok github.com/novox/mesh-host/internal/firewall 1.869s
ok github.com/novox/mesh-host/internal/identity 1.143s
ok github.com/novox/mesh-host/internal/image 1.039s
ok github.com/novox/mesh-host/internal/inventory 1.043s
ok github.com/novox/mesh-host/internal/link 10.350s
ok github.com/novox/mesh-host/internal/liveness 2.081s
ok github.com/novox/mesh-host/internal/network 1.072s
ok github.com/novox/mesh-host/internal/outward 1.059s
ok github.com/novox/mesh-host/internal/profile 1.539s
ok github.com/novox/mesh-host/internal/reachable 1.047s
ok github.com/novox/mesh-host/internal/store 1.244s
ok github.com/novox/mesh-host/internal/system 1.052s
ok github.com/novox/mesh-host/internal/tunnel 1.057s
ok github.com/novox/mesh-host/internal/units 1.044s
ok github.com/novox/mesh-host/internal/upgrade 1.170s
ok github.com/novox/mesh-host/internal/witness 4.778s
ok github.com/novox/mesh-host/packaging 1.049s
ok github.com/novox/mesh-host/validate 1.050s
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Why
Every declared tool check (hq ADR 0240) failed with "no link to the bus is open" while the node-engine's link was up. Seen live on 2026-10-08: the condition
module.sensors.g14.unhealthysaidsensors.watch ... its tool sensors_health: asking it: no link to the bus is open, 296 look(s) in a row, whileg14/sensors.sensors_summaryanswered through the mesh MCP server.Cause
Queue.AskToolreaches the bus through a type assertion toToolBus. OnlyOverNATShadAsk; the link the engine actually attaches isnatsLink(dialNats), which hadHealth,Report,Alive,ReadKeyandPingbut noAsk. The assertion failed on every live link, and the failure was reported with the same words as "no link open". The unit tests used fake links that implementedAsk, so nothing caught it. The bus grant (mesh.mod.<module>.tool.<tool>.<node>) is already composed by the controller.Change
natsLink.Ask, throughOverNATS.Ask, saying "nothing on this machine answers" for no responders and naming a refused grant asPingdoes.natsLinkis aBus,HealthBus,ToolBus,AskerandAsked: a link that drops one does not build.AskTooltells "no link open" apart from "the link open now cannot ask a tool".Tests
TestAToolCheckIsAskedOnTheLinkTheEngineHolds(no server needed): fails on main with "the NATS link was not asked", passes here.TestNatsAToolCheckIsAskedOnTheLink(withMESH_TEST_NATS): a served tool reads healthy through the queue on a real nats-server 2.11; a tool nothing serves says so. Run locally and passing.merge-check.shpasses locally (race detector on,MESH_TEST_NATSset).Affected live: only sensors on g14 has a tool check failing now (
mesh-controller.conditions, 2026-10-08 14:36 UTC).Delivery
novox/mesh-host@5e189c2fcad2— delivered since 2026-10-08T15:10:11ZDelivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
Machines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
809ab4cfmesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
Run by the build seat on novox as
build-1791470666991761059(builds --log build-1791470666991761059).Review follow-up (
5e189c2):TestAToolCheckIsAskedOnTheLinkTheEngineHoldsfails at its second step, with "on a link that cannot ask: no link to the bus is open", not with "the NATS link was not asked". That message was from a partial revert.TestNatsAToolCheckIsAskedOnTheLinkis skipped by the build seat, because nothing setsMESH_TEST_NATSthere.merge-check.shnow starts a throwaway nats-server with JetStream and exportsMESH_TEST_NATSwhen the toolchain has one, and prints "NOT RUN AGAINST A BUS" when it does not. Locally, with a nats-server on PATH, the real-bus tests ran and passed. The Go toolchain image (mesh-tools-go) has no nats-server, so in CI they remain skipped. This is recorded as open in hq issue 331.internal/liveness.refusal()now names a refusal only when the connection's last error changed during this question. The client keeps its last error for the life of the connection, so before this a refusal under a grant since widened was said again. The same applies toReadKeyandPing.nats.nats_users:node.g14may publishmesh.mod.sensors.tool.sensors_health.g14, andg14.node-toolssubscribes tomesh.mod.sensors.tool.>with responses allowed.g14/sensors.sensors_healthanswers healthy through the mesh MCP server.Merge check at
5e189c2fmesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)mesh/repo-check: PASS — THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; its merge-check.sh passedDelivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
Run by the build seat on shanks as
build-1791471703998020066(builds --log build-1791471703998020066).