hq issue 315: a failed systemd unit belonging to a mesh module was never raised, and a degraded service manager was said by nothing but the profile.
What changes
New internal/units: on every liveness look the engine reads list-units --state=failed of the machine's manager and of every account manager the declaration names (user-scoped services, user resources), on the two-look rule (ADR 0241 §2).
Each failed unit's owner: a declared service or process (by unit and manager), a file the declaration writes (the unit's FragmentPath), or a package the declaration installs (FragmentPath asked of pacman -Qqo, cached per path until the next apply). A unit liveness already judges (service stated running, long-running process) is left to liveness, so it is said once.
A module's failed unit is said among health.resources as an unhealthy resource of kind unit, target the unit, reason failed [in the account's own service manager] (<result>): the controller's existing module.<m>.<machine>.unhealthy raises it.
Every other failed unit is the machine's own, in the statement's new health.units (state running/degraded/unknown, failed[], unread[]). Absent from an older engine: not known.
Nothing read before the first declaration is set (else every unit reads as the machine's for a moment). Reads only: list-units, show, pacman -Q/-Qqo (a test holds it).
Tests: internal/units (shanks's four failures as found on 2026-10-08: openrazer's packaged user unit is openrazer's via its package; the two fstab mounts and greenclip's leftover are the machine's; a declared stateless service and a written unit file are their modules'; two looks each way; an unread manager; reader parsing incl. the ● mark), and cmd/mesh-hostwithUnits. merge-check.sh passes locally.
Pairs with novox/mesh-controller fix/315-a-failed-unit-is-a-condition, which raises the machine's own finding and keeps units. Either can merge first: an older controller ignores units and already raises the unit resources as module conditions; an older engine says no units, which the new controller reads as not known.
hq issue 315: a failed systemd unit belonging to a mesh module was never raised, and a degraded service manager was said by nothing but the profile.
**What changes**
- New `internal/units`: on every liveness look the engine reads `list-units --state=failed` of the machine's manager and of every account manager the declaration names (user-scoped services, `user` resources), on the two-look rule (ADR 0241 §2).
- Each failed unit's owner: a declared service or process (by unit and manager), a file the declaration writes (the unit's FragmentPath), or a package the declaration installs (FragmentPath asked of `pacman -Qqo`, cached per path until the next apply). A unit liveness already judges (service stated running, long-running process) is left to liveness, so it is said once.
- A module's failed unit is said among `health.resources` as an unhealthy resource of kind `unit`, target the unit, reason `failed [in the account's own service manager] (<result>)`: the controller's existing `module.<m>.<machine>.unhealthy` raises it.
- Every other failed unit is the machine's own, in the statement's new `health.units` (`state` running/degraded/unknown, `failed[]`, `unread[]`). Absent from an older engine: not known.
- Nothing read before the first declaration is set (else every unit reads as the machine's for a moment). Reads only: `list-units`, `show`, `pacman -Q`/`-Qqo` (a test holds it).
**Tests**: `internal/units` (shanks's four failures as found on 2026-10-08: openrazer's packaged user unit is openrazer's via its package; the two fstab mounts and greenclip's leftover are the machine's; a declared stateless service and a written unit file are their modules'; two looks each way; an unread manager; reader parsing incl. the `●` mark), and `cmd/mesh-host` `withUnits`. `merge-check.sh` passes locally.
Pairs with novox/mesh-controller fix/315-a-failed-unit-is-a-condition, which raises the machine's own finding and keeps `units`. Either can merge first: an older controller ignores `units` and already raises the `unit` resources as module conditions; an older engine says no `units`, which the new controller reads as not known.
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)
Liveness judged only what a module runs long-lived, so a module whose
daemon is a package's unit started by D-Bus activation failed at every
start while its machine read healthy, and a degraded service manager was
said by nothing but the profile.
The engine now reads the failed units of the machine's manager and of
every account manager the declaration names, on the two-look rule, and
says whose each is: a declared service or process, a unit file the mesh
writes, or a package the mesh installs makes it that module's, said as
an unhealthy resource of kind unit; anything else is the machine's own,
said in the statement's new units field. It reads; it never acts.
composed together: pass — every machine composes with the change as it did without (4 of 4 compose)
Machines
mesh-host on ace: passed — healthy 3 times over 2m28s
mesh-host on novox: judging — carried by release-1791452493787741117
mesh-host on the rest running it: sent
Transitions
2026-10-08 09:29 (new) → proposed (announced): novox/mesh-host#53's head announced
2026-10-08 09:31 proposed → checked (checked): the verdict names this commit
2026-10-08 09:31 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-08 09:40 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 09:40 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 09:44 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@14e5d91c9a2e` — **delivered** since 2026-10-08T09:44:35Z
**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
**Group** `fix/315-a-failed-unit-is-a-condition`, in order: novox/mesh-controller@6c98e7ea5103 → novox/mesh-host@14e5d91c9a2e
- novox/mesh-controller@6c98e7ea5103 before novox/mesh-host@14e5d91c9a2e: built by, over engine before controller
- composed together: pass — every machine composes with the change as it did without (4 of 4 compose)
**Machines**
- mesh-host on ace: passed — healthy 3 times over 2m28s
- mesh-host on novox: judging — carried by release-1791452493787741117
- mesh-host on the rest running it: sent
**Transitions**
- 2026-10-08 09:29 (new) → proposed (announced): novox/mesh-host#53's head announced
- 2026-10-08 09:31 proposed → checked (checked): the verdict names this commit
- 2026-10-08 09:31 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-08 09:40 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 09:40 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 09:44 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 shanks as build-1791451784890556297 (builds --log build-1791451784890556297).
--- 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-08T09:29:30Z 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-08T09:29:30Z
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.012s
ok github.com/novox/mesh-host/cmd/mesh-host 5.971s
ok github.com/novox/mesh-host/examples 1.017s
ok github.com/novox/mesh-host/internal/apply 11.736s
ok github.com/novox/mesh-host/internal/bootstrap 1.664s
ok github.com/novox/mesh-host/internal/bundle 1.014s
ok github.com/novox/mesh-host/internal/declaration 1.076s
ok github.com/novox/mesh-host/internal/firewall 1.873s
ok github.com/novox/mesh-host/internal/identity 1.182s
ok github.com/novox/mesh-host/internal/image 1.048s
ok github.com/novox/mesh-host/internal/inventory 1.075s
ok github.com/novox/mesh-host/internal/link 10.348s
ok github.com/novox/mesh-host/internal/liveness 2.140s
ok github.com/novox/mesh-host/internal/network 1.104s
ok github.com/novox/mesh-host/internal/outward 1.055s
ok github.com/novox/mesh-host/internal/profile 1.516s
ok github.com/novox/mesh-host/internal/reachable 1.050s
ok github.com/novox/mesh-host/internal/store 1.292s
ok github.com/novox/mesh-host/internal/system 1.051s
ok github.com/novox/mesh-host/internal/tunnel 1.041s
ok github.com/novox/mesh-host/internal/units 1.030s
ok github.com/novox/mesh-host/internal/upgrade 1.207s
ok github.com/novox/mesh-host/internal/witness 15.414s
ok github.com/novox/mesh-host/packaging 1.036s
ok github.com/novox/mesh-host/validate 1.033s
**Merge check** at `14e5d91c`
- `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 shanks as `build-1791451784890556297` (`builds --log build-1791451784890556297`).
```
--- 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-08T09:29:30Z 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-08T09:29:30Z
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.012s
ok github.com/novox/mesh-host/cmd/mesh-host 5.971s
ok github.com/novox/mesh-host/examples 1.017s
ok github.com/novox/mesh-host/internal/apply 11.736s
ok github.com/novox/mesh-host/internal/bootstrap 1.664s
ok github.com/novox/mesh-host/internal/bundle 1.014s
ok github.com/novox/mesh-host/internal/declaration 1.076s
ok github.com/novox/mesh-host/internal/firewall 1.873s
ok github.com/novox/mesh-host/internal/identity 1.182s
ok github.com/novox/mesh-host/internal/image 1.048s
ok github.com/novox/mesh-host/internal/inventory 1.075s
ok github.com/novox/mesh-host/internal/link 10.348s
ok github.com/novox/mesh-host/internal/liveness 2.140s
ok github.com/novox/mesh-host/internal/network 1.104s
ok github.com/novox/mesh-host/internal/outward 1.055s
ok github.com/novox/mesh-host/internal/profile 1.516s
ok github.com/novox/mesh-host/internal/reachable 1.050s
ok github.com/novox/mesh-host/internal/store 1.292s
ok github.com/novox/mesh-host/internal/system 1.051s
ok github.com/novox/mesh-host/internal/tunnel 1.041s
ok github.com/novox/mesh-host/internal/units 1.030s
ok github.com/novox/mesh-host/internal/upgrade 1.207s
ok github.com/novox/mesh-host/internal/witness 15.414s
ok github.com/novox/mesh-host/packaging 1.036s
ok github.com/novox/mesh-host/validate 1.033s
```
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.
hq issue 315: a failed systemd unit belonging to a mesh module was never raised, and a degraded service manager was said by nothing but the profile.
What changes
internal/units: on every liveness look the engine readslist-units --state=failedof the machine's manager and of every account manager the declaration names (user-scoped services,userresources), on the two-look rule (ADR 0241 §2).pacman -Qqo, cached per path until the next apply). A unit liveness already judges (service stated running, long-running process) is left to liveness, so it is said once.health.resourcesas an unhealthy resource of kindunit, target the unit, reasonfailed [in the account's own service manager] (<result>): the controller's existingmodule.<m>.<machine>.unhealthyraises it.health.units(staterunning/degraded/unknown,failed[],unread[]). Absent from an older engine: not known.list-units,show,pacman -Q/-Qqo(a test holds it).Tests:
internal/units(shanks's four failures as found on 2026-10-08: openrazer's packaged user unit is openrazer's via its package; the two fstab mounts and greenclip's leftover are the machine's; a declared stateless service and a written unit file are their modules'; two looks each way; an unread manager; reader parsing incl. the●mark), andcmd/mesh-hostwithUnits.merge-check.shpasses locally.Pairs with novox/mesh-controller fix/315-a-failed-unit-is-a-condition, which raises the machine's own finding and keeps
units. Either can merge first: an older controller ignoresunitsand already raises theunitresources as module conditions; an older engine says nounits, which the new controller reads as not known.Delivery
novox/mesh-host@14e5d91c9a2e— delivered since 2026-10-08T09:44:35ZDelivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
Group
fix/315-a-failed-unit-is-a-condition, in order: novox/mesh-controller@6c98e7ea51 → novox/mesh-host@14e5d91c9aMachines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
14e5d91cmesh/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 shanks as
build-1791451784890556297(builds --log build-1791451784890556297).