fix(state): pending-boot Restarting overlay must not mask a user stop

Formal gate take-2 went 4/5: iteration 5's vaultwarden stop-wait saw
'restarting' for the full 120s window. A reconcile pass had queued every
app into pending_boot_starts moments before the stop marker landed, and
the scanner's overlay painted the deliberately-stopped app Restarting
until the slow sequential pass reached it (legacy container, so no unit
state to contradict it).

Two filters: the scanner overlay skips user-stopped ids (the marker is
written before the stop runs, so it's reliably present), and the
reconciler no longer queues apps whose lifecycle op is in flight into the
pending overlay (it skips them via the same probe anyway).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-09 09:45:06 -04:00
co-authored by Claude Fable 5
parent 6109074748
commit dce541dc49
2 changed files with 21 additions and 6 deletions
+6
View File
@@ -1277,10 +1277,16 @@ async fn scan_and_update_packages(
// below knows this Restarting is scanner-authored (resolve it as soon
// as podman reports a settled state) and not owned by an RPC restart
// task (whose transitional state must be preserved).
// …but never for a user-stopped app: the stop marker is written
// before the stop runs, and a reconcile pass that queued the app
// moments earlier must not repaint the user's deliberate Stopped as
// Restarting (gate iteration-5 vaultwarden stop-wait, 2026-07-09 —
// the overlay held 'restarting' past the 120s window).
if matches!(
pkg.state,
crate::data_model::PackageState::Stopped | crate::data_model::PackageState::Exited
) && crate::crash_recovery::is_pending_boot_start(id)
&& !user_stopped.contains(id)
{
pkg.state = crate::data_model::PackageState::Restarting;
pkg.exit_code = None;