Related: #316 (bootstrap/carlos falls behind the fleet). Both found during Tito's v0.22.2 → v0.28.0 production roll on 2026-10-18.
Agent's in-process adopt rolls back a console update when it can't restart console.service itself ("Interactive authentication required") Issue closed
Claimed by claude~paul@keymail.dev 5 days ago
Seen on v0.25.1: Tito production IE (betty-fossicking), 2026-10-18 08:24:44 UTC, while rolling the console from 7f403a77 to 289043db with roll-console.sh <checkout> tito.
The edge agent, running as carlos, noticed the promote and adopted it in-process before the root pass did. Restarting the console's named unit needs root:
adopt: try-restart console.service: exit status 1: Failed to try-restart console.service: Interactive authentication required.
carlos.tito.io: update to 289043db failed: … — rolled the symlink back
adopt: start console.service: exit status 1: Failed to start console.service: Interactive authentication required.
carlos.tito.io: rolled back to 7f403a77 but could not start it: … — this host may be serving nothingThe old console process was never stopped, so nothing went down. One second later, the script's root adopt and its systemctl restart console put 289043db live (/opt/carlos/versions/289043db/console, no restarts since). Later root adopt passes left it alone. Rolling the console on to 26065378 (v0.28.0) at 08:41 didn't hit it.
Expected: when the agent can't restart a route's named unit itself, it hands the restart to the root pass, as it already does for "needs N staged and this process cannot write the version store — asked for a root pass". A permission error isn't a failed update. It shouldn't roll the symlink back, try to start the old build, or report "this host may be serving nothing" while the old process is still serving.
Why it matters: the rollback check, and the refusal it can record ("a plain re-promote stays refused"), exist to catch a console that won't start. A false trigger from permissions could leave a host on the old build, or refuse a good one. Here the root pass happened to follow within a second.
Related request, filed alongside: keep bootstrap/carlos in step with the fleet.
Fix pushed on branch adopt-unit-root-pass-315 (71483eae).
An agent that is not root (euid != 0, i.e. carlos-edge.service as User=carlos) now hands any route with a Unit to the root pass before it stages or repoints anything: release.RootRestartsUnits() sends it through OnStageBlocked with ErrUnitRestartNeedsRoot, the same relay as a build the agent cannot stage. The live link keeps naming the running build. Nothing is poisoned or refused, and there is no "serving nothing" line. Holds, refusals and the proof gate still run first. An agent run as root keeps restarting the unit itself. Host-writes mode is unchanged.
I chose to hand off before the repoint, not to classify the permission error after it fails. Classifying would match on systemd's message text, and it would still leave a window where the link names a build that isn't running.
Tests: release/rootunitstest.go (hand-off, no poison, root pass then installs) and host/rootunitstest.go (fast loop production options, non-root and root). Both are mutation-checked.
Merged to main as d7a5b8e (squash), main CI green. Not released or rolled. The census overlay landed at PROPOSED-315, and its ruling is still asked on #280.