huskies: merge 822

This commit is contained in:
dave
2026-04-28 23:06:40 +00:00
parent b698cee284
commit 6092f7efbb
2 changed files with 107 additions and 1 deletions
@@ -770,3 +770,89 @@ async fn gates_failed_with_test_evidence_and_committed_work_advances() {
"run_tests evidence must be cleared after pipeline advance consumes it"
);
}
// ── story 822: warm-resume coder on gate failure ──────────────────────────
/// Story 822 / AC 1 & 4: when a coder fails gates and a prior session ID is
/// provided, the pipeline re-spawns the coder so it can warm-resume the prior
/// conversation with the failure context injected — rather than starting from
/// scratch and re-reading the spec.
///
/// The test verifies:
/// - The coder is re-spawned (Pending/Running) rather than blocked.
/// - The retry counter is incremented (AC 3).
#[tokio::test]
async fn warm_resume_coder_on_gate_failure_with_session_id() {
use std::fs;
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
// Set up a project with a fast no-op coder agent.
fs::create_dir_all(root.join(".huskies")).unwrap();
fs::write(
root.join(".huskies/project.toml"),
r#"
max_retries = 3
[[agent]]
name = "coder-1"
role = "Coder"
command = "echo"
args = ["noop"]
prompt = "test prompt"
stage = "coder"
"#,
)
.unwrap();
crate::db::ensure_content_store();
crate::db::write_item_with_content(
"9950_story_warm_resume",
"2_current",
"---\nname: Warm Resume Test\n---\n",
);
let pool = AgentPool::new_test(3001);
// Simulate a coder failing gates. A prior session ID is provided to
// trigger the warm-resume path (--resume <session_id>).
pool.run_pipeline_advance(
"9950_story_warm_resume",
"coder-1",
CompletionReport {
summary: "Tests failed".to_string(),
gates_passed: false,
gate_output: "error[E0308]: mismatched types\n --> src/lib.rs:5:10".to_string(),
},
Some(root.to_path_buf()),
None,
false,
Some("prior-session-abc123".to_string()),
)
.await;
// The coder must be re-spawned — Pending or Running.
let agents = pool.agents.lock().unwrap();
let coder_restarted = agents.values().any(|a| {
a.agent_name == "coder-1" && matches!(a.status, AgentStatus::Pending | AgentStatus::Running)
});
assert!(
coder_restarted,
"Coder must be re-spawned (warm-resumed) when gates fail and prior session ID provided. \
Pool: {:?}",
agents
.iter()
.map(|(k, a)| format!("{k}: {} ({})", a.agent_name, a.status))
.collect::<Vec<_>>()
);
drop(agents);
// Retry counter must have been incremented (AC 3).
let content = crate::db::read_content("9950_story_warm_resume")
.expect("story must exist in content store");
assert!(
content.contains("retry_count"),
"retry_count must be incremented after warm-resume: {content}"
);
}