huskies: merge 484_story_story_dependencies_in_pipeline_auto_assign

This commit is contained in:
dave
2026-04-04 21:43:29 +00:00
parent 26de009259
commit 5413a26406
6 changed files with 665 additions and 3 deletions
+87 -2
View File
@@ -200,6 +200,18 @@ pub(super) fn build_pipeline_status(project_root: &std::path::Path, agents: &Age
let agent = active_map.get(story_id);
let throttled = agent.map(|a| a.throttled).unwrap_or(false);
let dot = traffic_light_dot(blocked, throttled, agent.is_some());
// Check for unmet dependencies and append a note when present.
let unmet = crate::io::story_metadata::check_unmet_deps(
project_root,
dir,
story_id,
);
let dep_suffix = if unmet.is_empty() {
String::new()
} else {
let nums: Vec<String> = unmet.iter().map(|n| n.to_string()).collect();
format!(" *(waiting on: {})*", nums.join(", "))
};
if let Some(agent) = agent {
let model_str = config
.as_ref()
@@ -207,11 +219,11 @@ pub(super) fn build_pipeline_status(project_root: &std::path::Path, agents: &Age
.and_then(|ac| ac.model.as_deref())
.unwrap_or("?");
out.push_str(&format!(
" {dot}{display}{cost_suffix}{} ({model_str})\n",
" {dot}{display}{cost_suffix}{dep_suffix}{} ({model_str})\n",
agent.agent_name
));
} else {
out.push_str(&format!(" {dot}{display}{cost_suffix}\n"));
out.push_str(&format!(" {dot}{display}{cost_suffix}{dep_suffix}\n"));
}
}
}
@@ -470,6 +482,79 @@ mod tests {
);
}
// -- dependency display in status output --------------------------------
#[test]
fn status_shows_waiting_on_for_story_with_unmet_deps() {
use std::io::Write;
use tempfile::TempDir;
let tmp = TempDir::new().unwrap();
let stage_dir = tmp.path().join(".huskies/work/2_current");
std::fs::create_dir_all(&stage_dir).unwrap();
// Dep 999 is NOT done — no 5_done directory at all.
let story_path = stage_dir.join("10_story_waiting.md");
let mut f = std::fs::File::create(&story_path).unwrap();
writeln!(f, "---\nname: Waiting Story\ndepends_on: [999]\n---\n").unwrap();
let agents = AgentPool::new_test(3000);
let output = build_pipeline_status(tmp.path(), &agents);
assert!(
output.contains("waiting on: 999"),
"status should show waiting-on info for unmet deps: {output}"
);
}
#[test]
fn status_does_not_show_waiting_on_when_dep_is_done() {
use std::io::Write;
use tempfile::TempDir;
let tmp = TempDir::new().unwrap();
let stage_dir = tmp.path().join(".huskies/work/2_current");
let done_dir = tmp.path().join(".huskies/work/5_done");
std::fs::create_dir_all(&stage_dir).unwrap();
std::fs::create_dir_all(&done_dir).unwrap();
// Dep 999 is done.
std::fs::write(done_dir.join("999_story_dep.md"), "---\nname: Dep\n---\n").unwrap();
let story_path = stage_dir.join("10_story_unblocked.md");
let mut f = std::fs::File::create(&story_path).unwrap();
writeln!(f, "---\nname: Unblocked Story\ndepends_on: [999]\n---\n").unwrap();
let agents = AgentPool::new_test(3000);
let output = build_pipeline_status(tmp.path(), &agents);
assert!(
!output.contains("waiting on"),
"status should not show waiting-on when all deps are done: {output}"
);
}
#[test]
fn status_shows_no_waiting_info_when_no_deps() {
use std::io::Write;
use tempfile::TempDir;
let tmp = TempDir::new().unwrap();
let stage_dir = tmp.path().join(".huskies/work/2_current");
std::fs::create_dir_all(&stage_dir).unwrap();
let story_path = stage_dir.join("42_story_nodeps.md");
let mut f = std::fs::File::create(&story_path).unwrap();
writeln!(f, "---\nname: No Deps Story\n---\n").unwrap();
let agents = AgentPool::new_test(3000);
let output = build_pipeline_status(tmp.path(), &agents);
assert!(
!output.contains("waiting on"),
"status should not show waiting-on for stories without deps: {output}"
);
}
// -- traffic_light_dot --------------------------------------------------
// -- build_pipeline_status_html (colored dots) --------------------------