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
+20 -49
View File
@@ -19,7 +19,6 @@
//! via exit-code inspection and silently skips the commit while still broadcasting
//! the event so connected clients stay in sync.
use crate::config::{ProjectConfig, WatcherConfig};
use crate::slog;
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
use serde::Serialize;
@@ -328,7 +327,7 @@ fn flush_pending(
/// All state is read from and written to CRDT — no filesystem access.
/// Worktree pruning is handled separately by the CRDT event subscriber.
pub(crate) fn sweep_done_to_archived(done_retention: Duration) {
use crate::pipeline_state::{Stage, read_all_typed};
use crate::pipeline_state::{PipelineEvent, Stage, stage_dir_name, transition, read_all_typed};
for item in read_all_typed() {
if let Stage::Done { merged_at, .. } = &item.stage {
@@ -337,9 +336,24 @@ pub(crate) fn sweep_done_to_archived(done_retention: Duration) {
.to_std()
.unwrap_or_default();
if age >= done_retention {
let story_id = &item.story_id.0;
crate::db::move_item_stage(story_id, "6_archived", None);
slog!("[watcher] sweep: promoted {story_id} → 6_archived/");
let story_id = item.story_id.0.clone();
match transition(item.stage.clone(), PipelineEvent::Accepted) {
Ok(new_stage) => {
crate::crdt_state::write_item(
&story_id,
stage_dir_name(&new_stage),
None,
None,
None,
Some(false),
None,
);
slog!("[watcher] sweep: promoted {story_id} → 6_archived/");
}
Err(e) => {
slog!("[watcher] sweep: transition error for {story_id}: {e}");
}
}
}
}
}
@@ -360,7 +374,6 @@ pub(crate) fn sweep_done_to_archived(done_retention: Duration) {
pub fn start_watcher(
git_root: PathBuf,
event_tx: broadcast::Sender<WatcherEvent>,
watcher_config: WatcherConfig,
) {
std::thread::spawn(move || {
let (notify_tx, notify_rx) = mpsc::channel::<notify::Result<notify::Event>>();
@@ -389,27 +402,13 @@ pub fn start_watcher(
}
}
slog!("[watcher] watching config files and running sweep timer");
slog!("[watcher] watching config files for hot-reload");
const DEBOUNCE: Duration = Duration::from_millis(300);
// Mutable sweep config — hot-reloaded when project.toml changes.
let mut sweep_interval = Duration::from_secs(watcher_config.sweep_interval_secs);
let mut done_retention = Duration::from_secs(watcher_config.done_retention_secs);
slog!(
"[watcher] sweep_interval={}s done_retention={}s",
watcher_config.sweep_interval_secs,
watcher_config.done_retention_secs
);
// Whether a config file change is pending in the current debounce window.
let mut config_changed_pending = false;
let mut deadline: Option<Instant> = None;
// Track when we last swept 5_done/ → 6_archived/.
// Initialise to "now minus interval" so the first sweep runs on startup.
let mut last_sweep = Instant::now()
.checked_sub(sweep_interval)
.unwrap_or_else(Instant::now);
loop {
// How long until the debounce window closes (or wait for next event).
@@ -454,37 +453,9 @@ pub fn start_watcher(
slog!("[watcher] broadcasting agent_config_changed");
let _ = event_tx.send(WatcherEvent::ConfigChanged);
// Hot-reload sweep config from project.toml.
match ProjectConfig::load(&git_root) {
Ok(cfg) => {
let new_sweep = Duration::from_secs(cfg.watcher.sweep_interval_secs);
let new_retention =
Duration::from_secs(cfg.watcher.done_retention_secs);
if new_sweep != sweep_interval || new_retention != done_retention {
slog!(
"[watcher] hot-reload: sweep_interval={}s done_retention={}s",
cfg.watcher.sweep_interval_secs,
cfg.watcher.done_retention_secs
);
sweep_interval = new_sweep;
done_retention = new_retention;
}
}
Err(e) => {
slog!("[watcher] hot-reload: failed to parse config: {e}");
}
}
config_changed_pending = false;
}
deadline = None;
// Periodically promote old items from 5_done/ to 6_archived/.
let now = Instant::now();
if now.duration_since(last_sweep) >= sweep_interval {
last_sweep = now;
sweep_done_to_archived(done_retention);
}
}
}
});