diff --git a/hive-ag3nt/src/turn.rs b/hive-ag3nt/src/turn.rs index d6b88d70..eddfc9e7 100644 --- a/hive-ag3nt/src/turn.rs +++ b/hive-ag3nt/src/turn.rs @@ -497,6 +497,17 @@ pub async fn wait_for_login( claude_dir = %claude_dir.display(), "no claude session — staying in partial-run mode (web UI only)" ); + // #563: announce `needs_login_idle` to the bus so the sentinel file + // (`{state_dir}/hyperhive-needs-login`) gets written on every entry + // path — cold-boot, 401-mid-turn, and `/api/logout`. The host's + // `auth_failed_sentinel` reads that file to surface `needs_login` on + // the dashboard; prior to this call the cold-boot + 401 paths flipped + // `LoginState::NeedsLogin` in memory but never touched the sentinel, + // so the dashboard kept rendering `online` for a parked harness. + // Idempotent — `emit_status` is a `write` on a small empty file, so + // re-entering this function after a transient operator action is a + // no-op for the on-disk state. + bus.emit_status("needs_login_idle"); let snapshot = snapshot_dir(claude_dir); let probe = Duration::from_millis(poll_ms.max(2000)); loop {