From a725d34b1967a5045070e4bfe94a7aa184cd9b30 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 29 May 2026 19:28:42 +0200 Subject: [PATCH] harness: emit needs_login_idle on every wait_for_login entry (closes #563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mara on #563: 'we fixed the agent to not start turns in that state (it fell back to online before), but this does not show on dashboard properly'. Root cause: the per-agent harness flips LoginState::NeedsLogin in memory on three entry paths (cold-boot without a session, 401 mid-turn, /api/logout) and parks in wait_for_login. But wait_for_login itself never called bus.emit_status('needs_login_idle') at entry — only the /api/logout handler does that today. So: - Cold boot: agent has no session, harness shows 'needs login' on its own web UI (via LoginState mutex), but the dashboard's needs_login field stays false because the {state_dir}/hyperhive-needs-login sentinel was never written. - 401 mid-turn: same — the 'after a turn failed' path in hive-ag3nt.rs / hive-m1nd.rs flips LoginState directly without emitting status, then calls wait_for_login, which now waits silently with no sentinel write. Fix: hoist the emit_status('needs_login_idle') call into wait_for_login itself. All three entry paths get the sentinel write for free; the /api/logout handler's explicit call (web_ui.rs line 966) becomes redundant but idempotent — no behaviour change there. The 'online' clear at session refresh stays exactly where it was at the loop's exit. Both hive-ag3nt and hive-m1nd binaries share wait_for_login, so the manager harness benefits without a separate change. --- hive-ag3nt/src/turn.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 {