huskies: merge 491_story_watcher_fires_on_crdt_state_transitions_instead_of_filesystem_events

This commit is contained in:
dave
2026-04-08 01:14:55 +00:00
parent dbdcf334aa
commit 5c2769dd7d
6 changed files with 272 additions and 58 deletions
+31 -18
View File
@@ -302,25 +302,11 @@ async fn main() -> Result<(), std::io::Error> {
}
}
// Initialise the CRDT state layer backed by SQLite.
// Uses the same pipeline.db file — the crdt_ops table lives alongside
// the legacy pipeline_items table.
let crdt_db_path = app_state
.project_root
.lock()
.unwrap()
.as_ref()
.map(|root| root.join(".huskies").join("pipeline.db"));
// TODO(491): Initialise CRDT state layer once the watcher story lands.
// if let Some(db_path) = crdt_db_path
// && let Err(e) = db::crdt::init(&db_path).await
// {
// slog!("[crdt] Failed to initialise CRDT state layer: {e}");
// }
// (CRDT state layer is initialised above alongside the legacy pipeline.db.)
let workflow = Arc::new(std::sync::Mutex::new(WorkflowState::default()));
// Filesystem watcher: broadcast channel for work/ pipeline changes.
// Event bus: broadcast channel for pipeline lifecycle events.
// Created before AgentPool so the pool can emit AgentStateChanged events.
let (watcher_tx, _) = broadcast::channel::<io::watcher::WatcherEvent>(1024);
let agents = Arc::new(AgentPool::new(port, watcher_tx.clone()));
@@ -329,6 +315,10 @@ async fn main() -> Result<(), std::io::Error> {
// When orphans are found, auto-assign is triggered to reassign free agents.
let watchdog_root: Option<PathBuf> = app_state.project_root.lock().unwrap().clone();
AgentPool::spawn_watchdog(Arc::clone(&agents), watchdog_root);
// Filesystem watcher: only watches config files (project.toml, agents.toml) and
// handles the sweep of done→archived. Work-item pipeline events are now driven
// by CRDT state transitions via crdt_state::subscribe().
if let Some(ref root) = *app_state.project_root.lock().unwrap() {
let work_dir = root.join(".huskies").join("work");
if work_dir.is_dir() {
@@ -339,8 +329,31 @@ async fn main() -> Result<(), std::io::Error> {
}
}
// Bridge CRDT state-transition events to the watcher broadcast channel.
// This replaces the filesystem watcher as the source of WorkItem events.
{
let crdt_watcher_tx = watcher_tx.clone();
if let Some(mut crdt_rx) = crdt_state::subscribe() {
tokio::spawn(async move {
while let Ok(evt) = crdt_rx.recv().await {
let (action, commit_msg) =
io::watcher::stage_metadata(&evt.to_stage, &evt.story_id)
.unwrap_or(("update", format!("huskies: update {}", evt.story_id)));
let watcher_evt = io::watcher::WatcherEvent::WorkItem {
stage: evt.to_stage,
item_id: evt.story_id,
action: action.to_string(),
commit_msg,
from_stage: evt.from_stage,
};
let _ = crdt_watcher_tx.send(watcher_evt);
}
});
}
}
// Subscribe to watcher events so that auto-assign triggers when a work item
// file is moved into an active pipeline stage (2_current/, 3_qa/, 4_merge/).
// enters an active pipeline stage (2_current/, 3_qa/, 4_merge/).
{
let watcher_auto_rx = watcher_tx.subscribe();
let watcher_auto_agents = Arc::clone(&agents);
@@ -353,7 +366,7 @@ async fn main() -> Result<(), std::io::Error> {
&& matches!(stage.as_str(), "2_current" | "3_qa" | "4_merge")
{
slog!(
"[auto-assign] Watcher detected work item in {stage}/; \
"[auto-assign] CRDT transition detected in {stage}/; \
triggering auto-assign."
);
watcher_auto_agents.auto_assign_available_work(&root).await;