Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions crates/claudear-analysis/src/evaluation/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ impl EvaluationResult {
}
}

/// Whether any test tool gained new failures vs the baseline.
pub fn has_new_test_failures(&self) -> bool {
self.deltas
.iter()
.any(|d| d.after.category == EvalCategory::Test && d.new_failures > 0)
}

/// Whether the fix introduced new failures or regressions in any tool.
pub fn has_regressions(&self) -> bool {
self.deltas
.iter()
.any(|d| d.new_failures > 0 || !d.regressions.is_empty())
}

fn build_summary(deltas: &[EvalDelta]) -> String {
if deltas.is_empty() {
return "No evaluation tools ran.".to_string();
Expand Down Expand Up @@ -225,6 +239,42 @@ mod tests {
assert!(!result.summary.is_empty());
}

#[test]
fn test_has_new_test_failures() {
// A newly-added failing test (red) shows up as a new test failure.
let red = EvaluationResult::new(
1,
"org/repo".into(),
vec![EvalDelta::compute(
make_snapshot(EvalCategory::Test, "cargo test", 10, 0),
make_snapshot(EvalCategory::Test, "cargo test", 10, 1),
)],
);
assert!(red.has_new_test_failures());

// Once the fix lands, the test passes again (green) — no new test failures.
let green = EvaluationResult::new(
1,
"org/repo".into(),
vec![EvalDelta::compute(
make_snapshot(EvalCategory::Test, "cargo test", 10, 0),
make_snapshot(EvalCategory::Test, "cargo test", 11, 0),
)],
);
assert!(!green.has_new_test_failures());

// A lint regression is not a test failure.
let lint = EvaluationResult::new(
1,
"org/repo".into(),
vec![EvalDelta::compute(
make_snapshot(EvalCategory::Lint, "clippy", 10, 0),
make_snapshot(EvalCategory::Lint, "clippy", 10, 1),
)],
);
assert!(!lint.has_new_test_failures());
}

#[test]
fn test_evaluation_result_pr_comment() {
let before = make_snapshot(EvalCategory::Test, "cargo test", 10, 2);
Expand Down
9 changes: 9 additions & 0 deletions crates/claudear-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,9 @@ pub struct ReplyConfig {
pub templates: std::collections::HashMap<String, String>,
/// Timeout for verifying (reproducing) a reported bug, in seconds (default: 1800).
pub verify_timeout_secs: u64,
/// When verify can't run (timeout/error/unsupported), assume reproduced and fix
/// anyway (default: true). Set false to ask the reporter for repro steps instead.
pub verify_fail_open: bool,
}

impl Default for ReplyConfig {
Expand All @@ -815,6 +818,7 @@ impl Default for ReplyConfig {
default_template: None,
templates: std::collections::HashMap::new(),
verify_timeout_secs: 1800,
verify_fail_open: true,
}
}
}
Expand Down Expand Up @@ -1086,6 +1090,9 @@ pub struct EvaluationConfig {
pub post_pr_comment: bool,
/// Fail the fix attempt on regression.
pub fail_on_regression: bool,
/// Enforce red->green: author a failing test first (must fail on the unfixed
/// code), then fix, then require it to pass. Needs test_delta enabled.
pub require_red_green: bool,
/// Custom test command override.
pub custom_test_cmd: Option<String>,
/// Custom lint command override.
Expand All @@ -1108,6 +1115,7 @@ impl Default for EvaluationConfig {
total_timeout_secs: 900,
post_pr_comment: true,
fail_on_regression: false,
require_red_green: false,
custom_test_cmd: None,
custom_lint_cmd: None,
custom_analysis_cmd: None,
Expand Down Expand Up @@ -8120,6 +8128,7 @@ instructions_file = "my-instructions.md"
total_timeout_secs: 1800,
post_pr_comment: false,
fail_on_regression: true,
require_red_green: false,
custom_test_cmd: Some("npm test".to_string()),
custom_lint_cmd: None,
custom_analysis_cmd: Some("sonar".to_string()),
Expand Down
15 changes: 15 additions & 0 deletions crates/claudear-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3232,6 +3232,18 @@ pub enum TimelineEventStatus {
#[serde(rename = "verify_completed")]
VerifyCompleted,

/// Red-green: the failing-test (red) phase began.
#[serde(rename = "red_green_started")]
RedGreenStarted,

/// Red-green: the authored test failed on the unfixed code (red confirmed).
#[serde(rename = "red_confirmed")]
RedConfirmed,

/// Red-green: the authored test passed after the fix (green confirmed).
#[serde(rename = "green_confirmed")]
GreenConfirmed,

#[serde(rename = "reply_started")]
ReplyStarted,

Expand Down Expand Up @@ -3292,6 +3304,9 @@ impl TimelineEventStatus {
Self::FixStarted => "fix_started",
Self::VerifyStarted => "verify_started",
Self::VerifyCompleted => "verify_completed",
Self::RedGreenStarted => "red_green_started",
Self::RedConfirmed => "red_confirmed",
Self::GreenConfirmed => "green_confirmed",
Self::ReplyStarted => "reply_started",
Self::ReplySent => "reply_sent",
Self::FixSucceeded => "fix_succeeded",
Expand Down
1 change: 1 addition & 0 deletions crates/claudear-e2e/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ impl ConfigBuilder {
template.to_string(),
)]),
verify_timeout_secs,
verify_fail_open: true,
};
self
}
Expand Down
Loading