A box can stage a platform update and never ask root to apply it Issue open
Seen on barbara-tinkering (AU, i-022288023f5e7496d) during the 2026-09-21 roll of 1b48ed5.
The agent had done its whole half of the handshake:
/data/system/router/requested.json -> {"version":"1b48ed5","promotedat":"2026-09-21T15:03:09Z","seenat":"2026-09-21T15:07:45Z"} /data/system/router/1b48ed5/carlos -> staged, complete
and then never touched /data/system-update-requested, which is the file carlos-system-update.path watches. Its mtime was still 2026-09-18 15:40, from the previous roll. So root was never signalled, carlos-system-update.service never ran, and carlos system status showed the box as plain "behind" with a stale error from the previous attempt. The other three boxes rolled normally.
The path unit was active and enabled throughout. Nothing was wrong with the trigger; the agent simply stopped asking.
Writing the marker by hand (the same RFC3339Nano line the agent writes, chown carlos:carlos) made root apply immediately and cleanly.
Why this is worth its own fix
The box had a working update staged on local disk and would not ask for it. There is no log line for this state -- three days of journalctl -u carlos-edge | grep "agent: system update" on that box is empty, including on a day when an apply DID run -- so from the console it is indistinguishable from a box that is merely slow, and from the box it is invisible. It cannot be recovered without SSM, which is the shape AGENTS.md's "every carlos command is designed for a user with no access to the underlying infra" rule exists to prevent.
Where I would look
systemLeg.tick in internal/host/systemleg.go. The marker write at the end is gated behind an early return:
if now.Before(p.deadline) || now.Before(l.backoffUntil) { return }
and holdOff grows a backoff on every failed fetch or failed request. That box had spent 2026-09-07 to 2026-09-18 failing an apply on every roll (see the gate/boot issue filed alongside this), so a long-grown backoffUntil is the obvious candidate -- but requested.json was written at 15:07:45 on the day in question, and that write happens AFTER the same early return, so the deadline check evidently passed on that tick. I have not reconciled those two facts and am not claiming the cause.
Worth noting either way: lastTouch and backoffUntil are in-memory only, so the state that suppresses the request does not survive an edge restart and cannot be inspected from outside the process.
Suggested shape
- Log when a request is suppressed, with the reason and the time it will next be attempted. Silence is the worst property this has.
- Surface "staged but not requested" in
carlos system status, distinct from "behind" -- it is a different problem with a different fix. - Consider having root's hourly
carlos adoptpass notice arequested.jsonnewer than the marker and complete the handshake, so this self-heals without a person.