huskies: merge 985
This commit is contained in:
@@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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:") {
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user