huskies: merge 987
This commit is contained in:
@@ -105,22 +105,35 @@ impl AgentPool {
|
||||
return;
|
||||
}
|
||||
|
||||
let success = matches!(&report, Ok(r) if r.success);
|
||||
let success = matches!(
|
||||
&report,
|
||||
Ok(r) if matches!(r.result, crate::agents::merge::MergeResult::Success { .. })
|
||||
);
|
||||
|
||||
let finished_at = unix_now();
|
||||
|
||||
// On any failure: record merge_failure in CRDT and emit notification.
|
||||
if !success {
|
||||
let kind = match &report {
|
||||
Ok(r) if r.no_commits => crate::pipeline_state::MergeFailureKind::NoCommits,
|
||||
Ok(r) if r.had_conflicts => {
|
||||
crate::pipeline_state::MergeFailureKind::ConflictDetected(
|
||||
r.conflict_details.clone(),
|
||||
)
|
||||
}
|
||||
Ok(r) => {
|
||||
crate::pipeline_state::MergeFailureKind::GatesFailed(r.gate_output.clone())
|
||||
}
|
||||
Ok(r) => match &r.result {
|
||||
crate::agents::merge::MergeResult::NoCommits { .. } => {
|
||||
crate::pipeline_state::MergeFailureKind::NoCommits
|
||||
}
|
||||
crate::agents::merge::MergeResult::Conflict { details, .. } => {
|
||||
crate::pipeline_state::MergeFailureKind::ConflictDetected(
|
||||
details.clone(),
|
||||
)
|
||||
}
|
||||
crate::agents::merge::MergeResult::GateFailure { output, .. } => {
|
||||
crate::pipeline_state::MergeFailureKind::GatesFailed(output.clone())
|
||||
}
|
||||
crate::agents::merge::MergeResult::Other { output, .. } => {
|
||||
crate::pipeline_state::MergeFailureKind::Other(output.clone())
|
||||
}
|
||||
crate::agents::merge::MergeResult::Success { .. } => {
|
||||
unreachable!("success branch is guarded by !success above")
|
||||
}
|
||||
},
|
||||
Err(e) => crate::pipeline_state::MergeFailureKind::Other(e.clone()),
|
||||
};
|
||||
let is_no_commits =
|
||||
@@ -131,7 +144,17 @@ impl AgentPool {
|
||||
&& report
|
||||
.as_ref()
|
||||
.ok()
|
||||
.and_then(|r| r.gate_failure_kind.as_ref())
|
||||
.and_then(|r| {
|
||||
if let crate::agents::merge::MergeResult::GateFailure {
|
||||
failure_kind: Some(k),
|
||||
..
|
||||
} = &r.result
|
||||
{
|
||||
Some(k)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|k| k.is_self_evident_fix())
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -271,17 +294,13 @@ impl AgentPool {
|
||||
.await
|
||||
.map_err(|e| format!("Merge task panicked: {e}"))??;
|
||||
|
||||
if !merge_result.success {
|
||||
if !matches!(
|
||||
merge_result,
|
||||
crate::agents::merge::MergeResult::Success { .. }
|
||||
) {
|
||||
return Ok(crate::agents::merge::MergeReport {
|
||||
story_id: story_id.to_string(),
|
||||
success: false,
|
||||
had_conflicts: merge_result.had_conflicts,
|
||||
conflicts_resolved: merge_result.conflicts_resolved,
|
||||
conflict_details: merge_result.conflict_details,
|
||||
gates_passed: merge_result.gates_passed,
|
||||
gate_output: merge_result.output,
|
||||
gate_failure_kind: merge_result.gate_failure_kind,
|
||||
no_commits: merge_result.no_commits,
|
||||
result: merge_result,
|
||||
worktree_cleaned_up: false,
|
||||
story_archived: false,
|
||||
});
|
||||
@@ -305,14 +324,7 @@ impl AgentPool {
|
||||
|
||||
Ok(crate::agents::merge::MergeReport {
|
||||
story_id: story_id.to_string(),
|
||||
success: true,
|
||||
had_conflicts: merge_result.had_conflicts,
|
||||
conflicts_resolved: merge_result.conflicts_resolved,
|
||||
conflict_details: merge_result.conflict_details,
|
||||
gates_passed: true,
|
||||
gate_output: merge_result.output,
|
||||
gate_failure_kind: None,
|
||||
no_commits: false,
|
||||
result: merge_result,
|
||||
worktree_cleaned_up,
|
||||
story_archived,
|
||||
})
|
||||
|
||||
@@ -23,14 +23,10 @@ impl AgentPool {
|
||||
.and_then(|e| serde_json::from_str::<crate::agents::merge::MergeReport>(e).ok())
|
||||
.unwrap_or_else(|| crate::agents::merge::MergeReport {
|
||||
story_id: story_id.to_string(),
|
||||
success: false,
|
||||
had_conflicts: false,
|
||||
conflicts_resolved: false,
|
||||
conflict_details: None,
|
||||
gates_passed: false,
|
||||
gate_output: String::new(),
|
||||
gate_failure_kind: None,
|
||||
no_commits: false,
|
||||
result: crate::agents::merge::MergeResult::Other {
|
||||
output: String::new(),
|
||||
conflict_details: None,
|
||||
},
|
||||
worktree_cleaned_up: false,
|
||||
story_archived: false,
|
||||
});
|
||||
|
||||
@@ -237,7 +237,13 @@ async fn merge_agent_work_returns_error_when_branch_not_found() {
|
||||
let job = run_merge_to_completion(&pool, repo, "99_nonexistent").await;
|
||||
match &job.status {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
assert!(!report.success, "should fail when branch missing");
|
||||
assert!(
|
||||
!matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Success { .. }
|
||||
),
|
||||
"should fail when branch missing"
|
||||
);
|
||||
}
|
||||
MergeJobStatus::Failed(_) => {
|
||||
// Also acceptable — the pipeline errored out
|
||||
@@ -305,11 +311,23 @@ async fn merge_agent_work_succeeds_on_clean_branch() {
|
||||
|
||||
match &job.status {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
assert!(!report.had_conflicts, "should have no conflicts");
|
||||
assert!(
|
||||
report.success
|
||||
|| report.gate_output.contains("Failed to run")
|
||||
|| !report.gates_passed,
|
||||
!matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Conflict { .. }
|
||||
),
|
||||
"should have no conflicts"
|
||||
);
|
||||
let is_success = matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Success { .. }
|
||||
);
|
||||
let is_gate_failure = matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::GateFailure { .. }
|
||||
);
|
||||
assert!(
|
||||
is_success || is_gate_failure || report.result.output().contains("Failed to run"),
|
||||
"report should be coherent: {report:?}"
|
||||
);
|
||||
if report.story_archived {
|
||||
@@ -418,8 +436,8 @@ fn quality_gates_run_before_fast_forward_to_master() {
|
||||
|
||||
// Gates must have failed (script/test exits 1) so master should be untouched.
|
||||
assert!(
|
||||
!result.success,
|
||||
"run_squash_merge must report failure when gates fail"
|
||||
!matches!(result, crate::agents::merge::MergeResult::Success { .. }),
|
||||
"run_squash_merge must report failure when gates fail; got: {result:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
head_before, head_after,
|
||||
@@ -531,7 +549,13 @@ async fn merge_agent_work_conflict_does_not_break_master() {
|
||||
// The report should accurately reflect what happened.
|
||||
match &job.status {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
assert!(report.had_conflicts, "should report conflicts");
|
||||
assert!(
|
||||
matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Conflict { .. }
|
||||
),
|
||||
"should report conflicts"
|
||||
);
|
||||
}
|
||||
MergeJobStatus::Failed(_) => {
|
||||
// Acceptable — merge aborted due to conflicts
|
||||
@@ -596,17 +620,17 @@ async fn merge_agent_work_zero_commits_ahead_stays_in_merge_stage() {
|
||||
match &job.status {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
assert!(
|
||||
!report.success,
|
||||
"merge must not have succeeded when feature branch is empty"
|
||||
matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::NoCommits { .. }
|
||||
),
|
||||
"merge must fail with NoCommits when feature branch is empty; got: {:?}",
|
||||
report.result
|
||||
);
|
||||
assert!(
|
||||
report.no_commits,
|
||||
"report.no_commits must be true for a zero-ahead branch"
|
||||
);
|
||||
assert!(
|
||||
report.gate_output.contains("no commits to merge"),
|
||||
"gate_output must contain 'no commits to merge', got: {}",
|
||||
report.gate_output
|
||||
report.result.output().contains("no commits to merge"),
|
||||
"output must contain 'no commits to merge', got: {}",
|
||||
report.result.output()
|
||||
);
|
||||
}
|
||||
MergeJobStatus::Failed(e) => {
|
||||
@@ -701,10 +725,16 @@ async fn server_side_merge_happy_path_advances_to_done() {
|
||||
match &job.status {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
assert!(
|
||||
!report.had_conflicts,
|
||||
!matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Conflict { .. }
|
||||
),
|
||||
"clean branch should have no conflicts"
|
||||
);
|
||||
if report.success {
|
||||
if matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Success { .. }
|
||||
) {
|
||||
// story_archived may or may not be true depending on gate env,
|
||||
// but merge_failure must NOT be in the content store.
|
||||
let content = crate::db::read_content(crate::db::ContentKey::Story("757a_happy"));
|
||||
@@ -838,7 +868,8 @@ async fn server_side_merge_conflict_sets_merge_failure() {
|
||||
// The merge must fail (conflict).
|
||||
let failed = matches!(
|
||||
&job.status,
|
||||
MergeJobStatus::Completed(r) if !r.success
|
||||
MergeJobStatus::Completed(r)
|
||||
if !matches!(r.result, crate::agents::merge::MergeResult::Success { .. })
|
||||
) || matches!(&job.status, MergeJobStatus::Failed(_));
|
||||
assert!(
|
||||
failed,
|
||||
@@ -953,11 +984,17 @@ async fn server_side_merge_gate_failure_sets_merge_failure() {
|
||||
match &job.status {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
assert!(
|
||||
!report.success,
|
||||
!matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Success { .. }
|
||||
),
|
||||
"gates should have failed; report: {report:?}"
|
||||
);
|
||||
assert!(
|
||||
!report.had_conflicts,
|
||||
!matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Conflict { .. }
|
||||
),
|
||||
"should be a gate failure, not a conflict"
|
||||
);
|
||||
}
|
||||
@@ -1052,11 +1089,18 @@ async fn merge_agent_work_one_commit_ahead_merges_successfully() {
|
||||
MergeJobStatus::Completed(report) => {
|
||||
// Success or gate failure — both acceptable; the key invariant is
|
||||
// that we didn't fail with the zero-commits early-exit.
|
||||
let is_success = matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::Success { .. }
|
||||
);
|
||||
let is_gate_failure = matches!(
|
||||
report.result,
|
||||
crate::agents::merge::MergeResult::GateFailure { .. }
|
||||
);
|
||||
assert!(
|
||||
report.success || !report.gates_passed,
|
||||
"unexpected state: success={} gates_passed={}",
|
||||
report.success,
|
||||
report.gates_passed
|
||||
is_success || is_gate_failure,
|
||||
"unexpected result variant: {:?}",
|
||||
report.result
|
||||
);
|
||||
}
|
||||
MergeJobStatus::Running => panic!("should not still be running"),
|
||||
|
||||
Reference in New Issue
Block a user