huskies: merge 1176 bug base_branch fallback hardcodes master instead of auto-detecting

This commit is contained in:
Huskies Agent
2026-07-16 16:22:46 +00:00
parent a4e2d4bc25
commit efd0097f76
11 changed files with 301 additions and 50 deletions
+138 -4
View File
@@ -185,10 +185,16 @@ fn validate_criterion_check(
workflow: &WorkflowState,
) -> Result<(), String> {
let branch = format!("feature/story-{story_id}");
let base_branch = {
let configured = crate::config::ProjectConfig::load(project_root)
.ok()
.and_then(|c| c.base_branch);
crate::worktree::resolve_base_branch(project_root, configured.as_deref())
};
// ── A: branch has commits vs master ──────────────────────────────────────
// ── A: branch has commits vs base branch ───────────────────────────────
let commits = Command::new("git")
.args(["log", &format!("master..{branch}"), "--oneline"])
.args(["log", &format!("{base_branch}..{branch}"), "--oneline"])
.current_dir(project_root)
.output()
.ok()
@@ -204,7 +210,7 @@ fn validate_criterion_check(
// ── B: AC text mentions a file touched by the branch ─────────────────────
let changed_files: Vec<String> = Command::new("git")
.args(["diff", &format!("master...{branch}"), "--name-only"])
.args(["diff", &format!("{base_branch}...{branch}"), "--name-only"])
.current_dir(project_root)
.output()
.ok()
@@ -254,7 +260,7 @@ fn validate_criterion_check(
Err(format!(
"No corroborating evidence for criterion '{ac_text}'. \
To proceed: commit your work to '{branch}' (currently has no commits vs master), \
To proceed: commit your work to '{branch}' (currently has no commits vs {base_branch}), \
add a passing test whose name matches the criterion, \
or change a file mentioned in the criterion text."
))
@@ -652,6 +658,134 @@ mod tests {
);
}
#[test]
fn tool_check_criterion_succeeds_on_main_based_repo_with_base_branch_unset() {
let tmp = tempfile::tempdir().unwrap();
// Repo whose default branch is `main` — no `master` branch exists at
// all, and no `.huskies/project.toml` sets `base_branch`. The evidence
// gate must diff against the auto-detected `main`, not a hardcoded
// `master` (bug 1176).
std::process::Command::new("git")
.args(["init", "-b", "main"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["config", "user.email", "test@test.com"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["config", "user.name", "Test"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["commit", "--allow-empty", "-m", "init"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["checkout", "-b", "feature/story-9998_main_branch"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["commit", "--allow-empty", "-m", "feature work"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["checkout", "main"])
.current_dir(tmp.path())
.output()
.unwrap();
crate::db::ensure_content_store();
crate::db::write_item_with_content(
"9998_main_branch",
"2_current",
"---\nname: Main Branch Test\n---\n## AC\n- [ ] Implement the feature\n",
crate::db::ItemMeta::named("Main Branch Test"),
);
let ctx = test_ctx(tmp.path());
let result = tool_check_criterion(
&json!({"story_id": "9998_main_branch", "criterion_index": 0}),
&ctx,
);
assert!(
result.is_ok(),
"Expected ok on main-based repo with commits ahead of main: {result:?}"
);
}
#[test]
fn tool_check_criterion_succeeds_on_main_based_repo() {
let tmp = tempfile::tempdir().unwrap();
// Repo whose default branch is `main` — no `master` branch exists at
// all. The evidence-gate diff must resolve against `main`, not a
// hardcoded `master` (bug 1176), or `git log master..branch` fails as
// an invalid revision range and blocks every check_criterion call.
std::process::Command::new("git")
.args(["init", "-b", "main"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["config", "user.email", "test@test.com"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["config", "user.name", "Test"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["commit", "--allow-empty", "-m", "init"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["checkout", "-b", "feature/story-9996_main_based"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["commit", "--allow-empty", "-m", "feature work"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["checkout", "main"])
.current_dir(tmp.path())
.output()
.unwrap();
crate::db::ensure_content_store();
crate::db::write_item_with_content(
"9996_main_based",
"2_current",
"---\nname: Main Based\n---\n## AC\n- [ ] Implement the feature\n",
crate::db::ItemMeta::named("Main Based"),
);
let ctx = test_ctx(tmp.path());
let result = tool_check_criterion(
&json!({"story_id": "9996_main_based", "criterion_index": 0}),
&ctx,
);
assert!(
result.is_ok(),
"Expected check_criterion to succeed on a main-based repo: {result:?}"
);
}
#[test]
fn tool_check_criterion_missing_story_id() {
let tmp = tempfile::tempdir().unwrap();