fix: server-side 20s blocking in get_test_result to prevent agent poll spam
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -471,11 +471,15 @@ pub(super) async fn tool_run_tests(args: &Value, ctx: &AppContext) -> Result<Str
|
|||||||
.map_err(|e| format!("Serialization error: {e}"))
|
.map_err(|e| format!("Serialization error: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How long `get_test_result` blocks server-side before returning "running".
|
||||||
|
/// This prevents agents from burning turns polling every 2 seconds.
|
||||||
|
const TEST_POLL_BLOCK_SECS: u64 = 20;
|
||||||
|
|
||||||
/// Check on a running test job and return results if complete.
|
/// Check on a running test job and return results if complete.
|
||||||
///
|
///
|
||||||
/// Returns `{"status": "running", "elapsed_secs": N}` if still in progress,
|
/// Blocks for up to 15 seconds, checking every second. Returns immediately
|
||||||
/// or the full test result if finished. If no job exists for the worktree,
|
/// when the test finishes, or after 15s with `{"status": "running"}`.
|
||||||
/// returns an error.
|
/// This server-side blocking prevents agents from wasting turns polling.
|
||||||
pub(super) async fn tool_get_test_result(
|
pub(super) async fn tool_get_test_result(
|
||||||
args: &Value,
|
args: &Value,
|
||||||
ctx: &AppContext,
|
ctx: &AppContext,
|
||||||
@@ -489,6 +493,42 @@ pub(super) async fn tool_get_test_result(
|
|||||||
.map_err(|e| format!("Cannot canonicalize project root: {e}"))?,
|
.map_err(|e| format!("Cannot canonicalize project root: {e}"))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Block for up to TEST_POLL_BLOCK_SECS, checking once per second.
|
||||||
|
let test_jobs = ctx.test_jobs.clone();
|
||||||
|
let wd = working_dir.clone();
|
||||||
|
for _ in 0..TEST_POLL_BLOCK_SECS {
|
||||||
|
{
|
||||||
|
let mut jobs = test_jobs.lock().map_err(|e| e.to_string())?;
|
||||||
|
if let Some(job) = jobs.get_mut(&wd) {
|
||||||
|
if let Some(child) = job.child.as_mut() {
|
||||||
|
match child.try_wait() {
|
||||||
|
Ok(Some(status)) => {
|
||||||
|
let result = collect_child_result(child, status);
|
||||||
|
job.child = None;
|
||||||
|
job.result = Some(result.clone());
|
||||||
|
jobs.remove(&wd);
|
||||||
|
return format_test_result(&result);
|
||||||
|
}
|
||||||
|
Ok(None) => {} // still running, keep waiting
|
||||||
|
Err(e) => {
|
||||||
|
jobs.remove(&wd);
|
||||||
|
return Err(format!("Failed to check child status: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(result) = job.result.clone() {
|
||||||
|
jobs.remove(&wd);
|
||||||
|
return format_test_result(&result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(
|
||||||
|
"No test job running for this worktree. Call run_tests first.".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still running after blocking period — return status.
|
||||||
let mut jobs = ctx.test_jobs.lock().map_err(|e| e.to_string())?;
|
let mut jobs = ctx.test_jobs.lock().map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let job = jobs.get_mut(&working_dir).ok_or_else(|| {
|
let job = jobs.get_mut(&working_dir).ok_or_else(|| {
|
||||||
|
|||||||
Reference in New Issue
Block a user