huskies: merge 960

This commit is contained in:
dave
2026-05-13 13:17:46 +00:00
parent a47fbc4179
commit 77dc09668c
14 changed files with 138 additions and 193 deletions
+8 -45
View File
@@ -2,7 +2,6 @@
use crate::config::ProjectConfig;
use std::collections::HashMap;
use std::path::Path;
use super::super::super::{AgentStatus, PipelineStage, agent_config_stage, pipeline_stage};
use super::super::StoryAgent;
@@ -18,32 +17,14 @@ pub(in crate::agents::pool) fn is_agent_free(
})
}
pub(super) fn scan_stage_items(_project_root: &Path, stage_dir: &str) -> Vec<String> {
/// Return all story_ids in the given pipeline `Stage`, sourced from the CRDT.
pub(super) fn scan_stage_items(want: &crate::pipeline_state::Stage) -> Vec<String> {
use std::collections::BTreeSet;
let mut items = BTreeSet::new();
// Accept legacy directory-style strings (`"2_current"`, `"4_merge"`,
// etc.) at the boundary; `Stage::from_dir` itself is strict post-934
// stage 6, so we normalise here.
let normalised = match stage_dir {
"0_upcoming" => "upcoming",
"1_backlog" => "backlog",
"2_current" => "coding",
"2_blocked" => "blocked",
"3_qa" => "qa",
"4_merge" => "merge",
"4_merge_failure" => "merge_failure",
"5_done" => "done",
"6_archived" => "archived",
other => other,
};
let Some(want) = crate::pipeline_state::Stage::from_dir(normalised) else {
return Vec::new();
};
// CRDT is the only source of truth — no filesystem fallback.
for item in crate::pipeline_state::read_all_typed() {
if std::mem::discriminant(&item.stage) == std::mem::discriminant(&want) {
if std::mem::discriminant(&item.stage) == std::mem::discriminant(want) {
items.insert(item.story_id.0.clone());
}
}
@@ -181,6 +162,7 @@ mod tests {
// attempt to promote an archived story.
#[test]
fn scan_stage_items_skips_filesystem_item_known_to_crdt_at_different_stage() {
use crate::pipeline_state::Stage;
crate::db::ensure_content_store();
// Write the story into the CRDT as 6_archived.
crate::db::write_item_with_content(
@@ -190,34 +172,16 @@ mod tests {
crate::db::ItemMeta::named("Archived"),
);
// Also place a stale .md file in a temp 1_backlog/ dir.
let tmp = tempfile::tempdir().unwrap();
let backlog = tmp.path().join(".huskies/work/1_backlog");
std::fs::create_dir_all(&backlog).unwrap();
std::fs::write(
backlog.join("9970_story_archived.md"),
"---\nname: Archived\n---\n",
)
.unwrap();
let items = scan_stage_items(tmp.path(), "1_backlog");
let items = scan_stage_items(&Stage::Backlog);
assert!(
!items.contains(&"9970_story_archived".to_string()),
"archived CRDT story must not appear in 1_backlog scan via stale filesystem shadow"
"archived CRDT story must not appear in backlog scan"
);
}
#[test]
fn scan_stage_items_returns_empty_for_missing_dir() {
// Use a unique stage name that no other test writes to, so
// the global CRDT store won't contribute stale items.
let tmp = tempfile::tempdir().unwrap();
let items = scan_stage_items(tmp.path(), "9_nonexistent");
assert!(items.is_empty());
}
#[test]
fn scan_stage_items_returns_sorted_story_ids() {
use crate::pipeline_state::Stage;
// Write items via the CRDT store (the primary source of truth).
crate::db::ensure_content_store();
crate::db::write_item_with_content(
@@ -239,8 +203,7 @@ mod tests {
crate::db::ItemMeta::named("baz"),
);
let tmp = tempfile::tempdir().unwrap();
let items = scan_stage_items(tmp.path(), "2_current");
let items = scan_stage_items(&Stage::Coding);
// The global CRDT may contain items from other tests, so check
// that our three items are present and appear in sorted order.
assert!(