huskies: merge 985

This commit is contained in:
dave
2026-05-13 16:47:56 +00:00
parent 580480094e
commit a078d3df7c
3 changed files with 107 additions and 1 deletions
@@ -248,3 +248,70 @@ stage = "qa"
(bug 173: lozenges must update when agents are assigned during pipeline advance)" (bug 173: lozenges must update when agents are assigned during pipeline advance)"
); );
} }
#[tokio::test]
async fn pipeline_advance_coder_gates_pass_human_qa_holds_for_review() {
use std::fs;
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
// Seed story via CRDT so qa_mode can be resolved from the store.
crate::db::ensure_content_store();
crate::db::write_item_with_content(
"9910_story_human_qa",
"2_current",
"---\nname: Human Qa Test\n---\ntest",
crate::db::ItemMeta::named("Human Qa Test"),
);
// Set qa_mode = Human on the CRDT item directly.
crate::crdt_state::set_qa_mode(
"9910_story_human_qa",
Some(crate::io::story_metadata::QaMode::Human),
);
// Write a minimal project.toml so the pool can load config.
fs::create_dir_all(root.join(".huskies")).unwrap();
fs::write(
root.join(".huskies/project.toml"),
r#"
[[agent]]
name = "coder-1"
role = "Coder"
command = "echo"
args = ["noop"]
prompt = "test"
stage = "coder"
"#,
)
.unwrap();
let pool = AgentPool::new_test(3001);
pool.run_pipeline_advance(
"9910_story_human_qa",
"coder-1",
CompletionReport {
summary: "done".to_string(),
gates_passed: true,
gate_output: String::new(),
needs_commit_recovery: false,
},
Some(root.to_path_buf()),
None,
false,
None,
)
.await;
// With qa: human the story should be held for human review (ReviewHold stage).
let item = crate::crdt_state::read_item("9910_story_human_qa")
.expect("story should exist in CRDT after advance");
assert!(
matches!(
item.stage(),
crate::pipeline_state::Stage::ReviewHold { .. }
),
"qa: human should leave story in ReviewHold, got: {:?}",
item.stage()
);
}
+2 -1
View File
@@ -1,6 +1,7 @@
//! Handler for the `show` command. //! Handler for the `show` command.
use super::CommandContext; use super::CommandContext;
use crate::io::story_metadata::QaMode;
/// Strip YAML front matter and return a summary of useful fields + the remaining body. /// Strip YAML front matter and return a summary of useful fields + the remaining body.
#[allow(clippy::string_slice)] // indices from find("\n---") on ASCII delimiter; "---" and "\n---" are ASCII-only #[allow(clippy::string_slice)] // indices from find("\n---") on ASCII delimiter; "---" and "\n---" are ASCII-only
@@ -41,7 +42,7 @@ fn strip_front_matter(text: &str) -> (String, String) {
} }
} else if line.starts_with("qa:") { } else if line.starts_with("qa:") {
let val = line.trim_start_matches("qa:").trim().trim_matches('"'); let val = line.trim_start_matches("qa:").trim().trim_matches('"');
if val == "human" { if let Some(QaMode::Human) = QaMode::from_str(val) {
parts.push("**QA:** human review required".to_string()); parts.push("**QA:** human review required".to_string());
} }
} else if line.starts_with("merge_failure:") { } else if line.starts_with("merge_failure:") {
+38
View File
@@ -472,6 +472,44 @@ fn set_qa_mode_returns_false_for_unknown_story() {
assert!(!ok, "set_qa_mode should return false for unknown story_id"); assert!(!ok, "set_qa_mode should return false for unknown story_id");
} }
#[test]
fn set_qa_mode_round_trip_all_variants() {
use crate::io::story_metadata::QaMode;
init_for_test();
write_item_str(
"985_story_qa_all_variants",
"1_backlog",
Some("Qa All Variants"),
None,
None,
None,
None,
None,
None,
);
for mode in [QaMode::Server, QaMode::Agent, QaMode::Human] {
let ok = set_qa_mode("985_story_qa_all_variants", Some(mode));
assert!(ok, "set_qa_mode({mode:?}) should succeed for known item");
let view = read_item("985_story_qa_all_variants").unwrap();
assert_eq!(
view.qa_mode,
Some(mode),
"CRDT register should round-trip {mode:?}"
);
}
// Clear → register reverts to unset (project default applies).
let ok = set_qa_mode("985_story_qa_all_variants", None);
assert!(ok, "set_qa_mode(None) should succeed");
let view = read_item("985_story_qa_all_variants").unwrap();
assert_eq!(
view.qa_mode, None,
"clearing qa_mode should leave register unset"
);
}
// ── set_retry_count / bump_retry_count tests ───────────────────────────── // ── set_retry_count / bump_retry_count tests ─────────────────────────────
#[test] #[test]