huskies: merge 534_refactor_unify_timer_tick_watchdog_and_watcher_sweep_into_a_single_1_second_tick_loop

This commit is contained in:
dave
2026-04-10 17:34:41 +00:00
parent 808935b446
commit 91be0ac47f
5 changed files with 88 additions and 125 deletions
+4 -35
View File
@@ -1,8 +1,8 @@
//! Deferred agent start via one-shot timers.
//!
//! Provides [`TimerStore`] for persisting timers to `.huskies/timers.json`,
//! a 30-second tick loop ([`spawn_timer_tick_loop`]) that fires due timers,
//! Provides [`TimerStore`] for persisting timers to `.huskies/timers.json`
//! and command parsing / handling for the `timer` bot command.
//! Due timers are fired by the unified background tick loop in `main`.
use chrono::{DateTime, Duration, Local, NaiveTime, TimeZone, Utc};
use chrono_tz::Tz;
@@ -134,43 +134,12 @@ impl TimerStore {
// ── Tick loop ──────────────────────────────────────────────────────────────
/// Spawn a background tokio task that fires due timers every 1 second.
///
/// Same pattern as the watchdog in `agents::pool::auto_assign`.
/// When a timer fires, `start_agent` is called for the story. If all coders
/// are busy the story remains in `2_current/` and auto-assign will pick it up.
///
/// The loop body is wrapped in `catch_unwind` so a panic on any single tick
/// does not silently kill the background task.
pub fn spawn_timer_tick_loop(
store: Arc<TimerStore>,
agents: Arc<crate::agents::AgentPool>,
project_root: PathBuf,
) {
let pending_count = store.list().len();
crate::slog!(
"[timer] Tick loop started; {pending_count} pending timer(s) loaded"
);
tokio::spawn(async move {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(1));
loop {
interval.tick().await;
// Wrap the tick body so a panic doesn't kill the loop.
let tick_result = tick_once(&store, &agents, &project_root).await;
if let Err(msg) = tick_result {
crate::slog_error!("[timer] Tick panicked: {msg}");
}
}
});
}
/// Execute one tick of the timer loop.
///
/// Called by the unified background tick loop every second.
/// Separated from the loop so we can catch panics at the call-site.
/// Returns `Err` only when the tick panicked (the panic message is returned).
async fn tick_once(
pub(crate) async fn tick_once(
store: &Arc<TimerStore>,
agents: &Arc<crate::agents::AgentPool>,
project_root: &Path,