huskies: merge 1019

This commit is contained in:
dave
2026-05-14 08:48:11 +00:00
parent ebf58ef224
commit e3f5875b8e
6 changed files with 157 additions and 61 deletions
-1
View File
@@ -13,7 +13,6 @@ pub use create::install_pre_commit_hook;
pub(crate) use git::detect_base_branch;
pub use git::migrate_slug_paths;
pub use remove::remove_worktree_by_story_id;
pub use sweep::sweep_orphaned_worktrees;
#[derive(Debug, Clone)]
/// Details about a newly created worktree: path, branch, and base branch.
+16 -23
View File
@@ -1,14 +1,20 @@
//! Periodic orphan sweep — removes worktrees whose stories are done, archived,
//! or absent from the CRDT.
//! Worktree orphan sweep helpers.
//!
//! `worktree_should_be_swept` and `sweep_with_lookup` are test helpers used
//! to verify that the reactive worktree-cleanup subscriber (`spawn_worktree_cleanup_subscriber`)
//! correctly identifies which stages warrant worktree removal. Production
//! worktree cleanup is driven by `TransitionFired` events in
//! `agents::pool::worktree_lifecycle`, not by periodic scanning.
#[cfg(test)]
use super::{list_worktrees, remove_worktree_by_story_id};
#[cfg(test)]
use crate::config::ProjectConfig;
use crate::pipeline_state::{Stage, read_typed};
use crate::pipeline_state::Stage;
#[cfg(test)]
use std::path::Path;
use super::{list_worktrees, remove_worktree_by_story_id};
/// Returns `true` if a worktree for the given pipeline stage should be removed
/// by the orphan sweep.
/// Returns `true` if a worktree for the given pipeline stage should be removed.
///
/// A worktree is swept when its story is `Done`, `Archived`, or not present in
/// the CRDT at all (i.e. `stage` is `None`). Active stages (`Backlog`,
@@ -27,26 +33,13 @@ pub fn worktree_should_be_swept(stage: Option<&Stage>) -> bool {
}
}
/// Remove orphaned worktrees whose stories are done, archived, or absent from
/// the CRDT.
///
/// Walks `.huskies/worktrees/`, checks each story's stage via `lookup`, and
/// calls [`remove_worktree_by_story_id`] for any that should be swept.
/// Failures are logged individually; the sweep continues regardless.
///
/// Returns the number of worktrees successfully removed.
pub async fn sweep_orphaned_worktrees(project_root: &Path, config: &ProjectConfig) -> usize {
sweep_with_lookup(project_root, config, |story_id| {
read_typed(story_id).ok().flatten().map(|item| item.stage)
})
.await
}
/// Internal sweep implementation that accepts a custom CRDT lookup function.
/// Internal sweep implementation for tests: walks worktrees and removes those
/// whose stories are in a terminal stage according to the provided `lookup`.
///
/// Accepts a `lookup` closure `fn(&str) -> Option<Stage>` that returns the
/// stage for a given story ID, or `None` if the story is not in the CRDT.
/// This indirection makes the sweep testable without a real CRDT.
#[cfg(test)]
pub(crate) async fn sweep_with_lookup<F>(
project_root: &Path,
config: &ProjectConfig,