Issue 332: add unlink-dangling and the glob refusal from the review
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered

disable cannot remove a link whose unit file is gone, so the verb that
finds it needed one that removes it.
This commit is contained in:
jochen
2026-10-08 17:40:00 +02:00
parent c3a20baece
commit fd6c452e61
@@ -41,21 +41,35 @@ Two verbs on the seat, each saying the command it replaces:
it points, whether it dangles and whose directory it is in. It replaces `systemctl list-dependencies
--reverse` and reading the `*.wants` directories by hand.
A third verb followed from the review of the first two. `systemctl disable` does not remove an enable
link once the unit's file is gone, so the link found by `wanted-by` had no verb to remove it either:
- **`unlink-dangling {unit, scope, why}`** removes the dangling enable links named for one unit, only in
the account's or the machine's own configuration, on the person's word. It never removes a package's, a
generator's or the runtime's link, and leaves any link that changed since it was read. Each link is
recorded with its target and the reason before it is removed.
The review also made every verb refuse a unit name holding `*`, `?` or `[`: the service manager expands a
glob to every unit it matches, so `reset-failed` or `stop` of `*` would have acted on all of them.
They land in the three steps of
[ADR 0246](../../02-DECISIONS/0246-a-seats-new-verb-is-promised-before-it-is-required.md):
1. The controller promises both verbs as optional (mesh-controller, this issue's first pull request).
2. The `systemd` module serves both (mesh-catalog). Its check passes only against a controller that
already promises them, so it merges after step 1 is delivered.
3. The controller requires both, in a later pull request, once every holder serves them.
1. The controller promises the verbs as optional: mesh-controller #158 (`reset-failed`, `wanted-by`) and
#160 (`unlink-dangling`).
2. The `systemd` module serves them: mesh-catalog #137 and #138. A module's check passes only against a
controller that already promises its verbs, so each merges after its step 1 is delivered.
3. The controller requires them, in a later pull request, once every holder serves them.
## How it is checked
- mesh-controller: `TestResetFailedAndWantedByAreOptionalVerbsOfTheServiceManager` (both promised,
optional, read back optional from the store's row, each naming the command it replaces), and the seat's
verb list in `TestTheServiceManagerSeatServesTheUnitVerbs`.
- mesh-controller: `TestResetFailedAndWantedByAreOptionalVerbsOfTheServiceManager` (the verbs promised,
optional, read back optional from the store's row, each naming the command it replaces, and `why`
required of `unlink-dangling`), and the seat's verb list in `TestTheServiceManagerSeatServesTheUnitVerbs`.
- mesh-catalog `modules/systemd`: the `wanted_test.go` tests (a dangling enable link in the account's own
configuration found and named, a link matched by its target, an unreadable directory and an unreachable
manager said rather than read as nothing, read-only), and the manifest test that the module serves exactly
the verbs it claims.
the verbs it claims. `TestAGlobIsRefusedAsAUnitByEveryVerb` covers every verb taking a unit. The
`unlink_test.go` tests check what is removed and what is left, that the record is written before each
removal, and that a link outside the account's home goes through `sudo -n`.
- Step 3 is not checked by anything yet, as ADR 0246 says of every optional verb.