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:
dave
2026-05-01 15:41:52 +00:00
committed by Timmy
parent 5b48f0d051
commit fac4442969
3 changed files with 71 additions and 8 deletions
+9
View File
@@ -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());
+50
View File
@@ -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
);
}
}