fix(896): disallow ScheduleWakeup for coder agents; add run_tests retry guidance
- Add `disallowed_tools` field to `AgentConfig` and render it as `--disallowedTools` CLI flag in `render_agent_args` - Set `disallowed_tools = ["ScheduleWakeup"]` on all four coder agents (coder-1, coder-2, coder-3, coder-opus); QA and mergemaster unaffected - Append instruction to all coder `system_prompt`s: do not use ScheduleWakeup to wait for run_tests; if run_tests appears to time out, call run_tests again — it attaches to the in-flight job and blocks - Add tests: `render_agent_args_disallowed_tools` and `coder_agents_disallow_schedule_wakeup`
This commit is contained in:
@@ -239,6 +239,8 @@ pub struct AgentConfig {
|
||||
#[serde(default)]
|
||||
pub allowed_tools: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub disallowed_tools: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub max_turns: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub max_budget_usd: Option<f64>,
|
||||
@@ -321,6 +323,7 @@ impl Default for ProjectConfig {
|
||||
prompt: default_agent_prompt(),
|
||||
model: None,
|
||||
allowed_tools: None,
|
||||
disallowed_tools: None,
|
||||
max_turns: None,
|
||||
max_budget_usd: None,
|
||||
system_prompt: None,
|
||||
@@ -573,6 +576,12 @@ impl ProjectConfig {
|
||||
args.push("--allowedTools".to_string());
|
||||
args.push(tools.join(","));
|
||||
}
|
||||
if let Some(ref tools) = agent.disallowed_tools
|
||||
&& !tools.is_empty()
|
||||
{
|
||||
args.push("--disallowedTools".to_string());
|
||||
args.push(tools.join(","));
|
||||
}
|
||||
if let Some(turns) = agent.max_turns {
|
||||
args.push("--max-turns".to_string());
|
||||
args.push(turns.to_string());
|
||||
|
||||
@@ -627,3 +627,53 @@ fn project_toml_has_three_sonnet_coders() {
|
||||
sonnet_coders.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_agent_args_disallowed_tools() {
|
||||
let toml_str = r#"
|
||||
[[agent]]
|
||||
name = "coder"
|
||||
model = "sonnet"
|
||||
disallowed_tools = ["ScheduleWakeup", "SomeTool"]
|
||||
"#;
|
||||
|
||||
let config = ProjectConfig::parse(toml_str).unwrap();
|
||||
let (_, args, _) = config
|
||||
.render_agent_args("/tmp/wt", "42_foo", None, None)
|
||||
.unwrap();
|
||||
assert!(
|
||||
args.contains(&"--disallowedTools".to_string()),
|
||||
"Expected --disallowedTools flag in args"
|
||||
);
|
||||
assert!(
|
||||
args.contains(&"ScheduleWakeup,SomeTool".to_string()),
|
||||
"Expected disallowed tools joined as comma-separated string"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coder_agents_disallow_schedule_wakeup() {
|
||||
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let project_root = manifest_dir.parent().unwrap();
|
||||
let config = ProjectConfig::load(project_root).unwrap();
|
||||
|
||||
let coder_agents: Vec<_> = config
|
||||
.agent
|
||||
.iter()
|
||||
.filter(|a| a.stage.as_deref() == Some("coder"))
|
||||
.collect();
|
||||
|
||||
assert!(
|
||||
!coder_agents.is_empty(),
|
||||
"Expected at least one coder-stage agent"
|
||||
);
|
||||
|
||||
for agent in coder_agents {
|
||||
let disallowed = agent.disallowed_tools.as_deref().unwrap_or(&[]);
|
||||
assert!(
|
||||
disallowed.iter().any(|t| t == "ScheduleWakeup"),
|
||||
"Coder agent '{}' must have ScheduleWakeup in disallowed_tools",
|
||||
agent.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user