The migration did not stop anything. It ran stop + disable + mask in one
call with failed_when: false, then deleted the unit files. Masking a unit
whose file still exists conflicts, so the call failed, failed_when hid the
failure, nothing was stopped -- and the next task deleted the unit files out
from under two running processes.
That left systemctl is-enabled reporting "not-found", which looks clean,
while is-active still said "active". I reported the cutover successful on
the strength of the former.
On 2026-08-10 the result was two open3e instances issuing UDS requests on
the same CAN bus from 19:28 to 19:37, taking over each other's
open3e-edge MQTT session -- the exact failure the block's own comment said
it existed to prevent.
Fixed in three parts:
- Stop and disable FIRST, and do not swallow the result. Only units that
actually exist are touched (stat first), so a fresh host does not need
an error hidden to get through.
- No masking. It writes a symlink at the same path the next task removes,
so the two cancel out. With the unit files gone there is nothing to
start.
- Assert the invariant that actually matters -- no native process on the
CAN bus -- rather than inferring it from systemd reporting success.
pgrep, then fail loudly with the surviving command lines and why it
matters.
Verified against the live host: ok=15 changed=1 failed=0, the assert passing
with "No native CAN processes remain; safe to start the container", and the
run now idempotent (the single change is the compose up, not the teardown).
Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>