story-kit: merge 307_story_configurable_coder_pool_size_and_default_model_in_project_toml
This commit is contained in:
@@ -15,6 +15,17 @@ pub struct ProjectConfig {
|
||||
/// Per-story `qa` front matter overrides this. Default: "server".
|
||||
#[serde(default = "default_qa")]
|
||||
pub default_qa: String,
|
||||
/// Default model for coder-stage agents (e.g. "sonnet").
|
||||
/// When set, `find_free_agent_for_stage` only considers coder agents whose
|
||||
/// model matches this value, so opus agents are only used when explicitly
|
||||
/// requested via story front matter `agent:` field.
|
||||
#[serde(default)]
|
||||
pub default_coder_model: Option<String>,
|
||||
/// Maximum number of concurrent coder-stage agents.
|
||||
/// When set, `auto_assign_available_work` will not start more than this many
|
||||
/// coder agents at once. Stories wait in `2_current/` until a slot frees up.
|
||||
#[serde(default)]
|
||||
pub max_coders: Option<usize>,
|
||||
}
|
||||
|
||||
/// Configuration for the filesystem watcher's sweep behaviour.
|
||||
@@ -134,6 +145,10 @@ struct LegacyProjectConfig {
|
||||
watcher: WatcherConfig,
|
||||
#[serde(default = "default_qa")]
|
||||
default_qa: String,
|
||||
#[serde(default)]
|
||||
default_coder_model: Option<String>,
|
||||
#[serde(default)]
|
||||
max_coders: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for ProjectConfig {
|
||||
@@ -156,6 +171,8 @@ impl Default for ProjectConfig {
|
||||
}],
|
||||
watcher: WatcherConfig::default(),
|
||||
default_qa: default_qa(),
|
||||
default_coder_model: None,
|
||||
max_coders: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,6 +215,8 @@ impl ProjectConfig {
|
||||
agent: vec![agent],
|
||||
watcher: legacy.watcher,
|
||||
default_qa: legacy.default_qa,
|
||||
default_coder_model: legacy.default_coder_model,
|
||||
max_coders: legacy.max_coders,
|
||||
};
|
||||
validate_agents(&config.agent)?;
|
||||
return Ok(config);
|
||||
@@ -219,6 +238,8 @@ impl ProjectConfig {
|
||||
agent: vec![agent],
|
||||
watcher: legacy.watcher,
|
||||
default_qa: legacy.default_qa,
|
||||
default_coder_model: legacy.default_coder_model,
|
||||
max_coders: legacy.max_coders,
|
||||
};
|
||||
validate_agents(&config.agent)?;
|
||||
Ok(config)
|
||||
@@ -228,6 +249,8 @@ impl ProjectConfig {
|
||||
agent: Vec::new(),
|
||||
watcher: legacy.watcher,
|
||||
default_qa: legacy.default_qa,
|
||||
default_coder_model: legacy.default_coder_model,
|
||||
max_coders: legacy.max_coders,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -710,4 +733,68 @@ command = "claude"
|
||||
assert_eq!(config.watcher.done_retention_secs, 900);
|
||||
assert_eq!(config.agent.len(), 1);
|
||||
}
|
||||
|
||||
// ── default_coder_model & max_coders ─────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn parse_default_coder_model_and_max_coders() {
|
||||
let toml_str = r#"
|
||||
default_coder_model = "sonnet"
|
||||
max_coders = 3
|
||||
|
||||
[[agent]]
|
||||
name = "coder-1"
|
||||
stage = "coder"
|
||||
model = "sonnet"
|
||||
|
||||
[[agent]]
|
||||
name = "coder-opus"
|
||||
stage = "coder"
|
||||
model = "opus"
|
||||
"#;
|
||||
let config = ProjectConfig::parse(toml_str).unwrap();
|
||||
assert_eq!(config.default_coder_model, Some("sonnet".to_string()));
|
||||
assert_eq!(config.max_coders, Some(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_coder_model_and_max_coders_default_to_none() {
|
||||
let toml_str = r#"
|
||||
[[agent]]
|
||||
name = "coder-1"
|
||||
"#;
|
||||
let config = ProjectConfig::parse(toml_str).unwrap();
|
||||
assert_eq!(config.default_coder_model, None);
|
||||
assert_eq!(config.max_coders, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_toml_has_default_coder_model_and_max_coders() {
|
||||
// Verify the actual project.toml has the new settings.
|
||||
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();
|
||||
assert_eq!(config.default_coder_model, Some("sonnet".to_string()));
|
||||
assert_eq!(config.max_coders, Some(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_toml_has_three_sonnet_coders() {
|
||||
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 sonnet_coders: Vec<_> = config
|
||||
.agent
|
||||
.iter()
|
||||
.filter(|a| a.stage.as_deref() == Some("coder") && a.model.as_deref() == Some("sonnet"))
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
sonnet_coders.len(),
|
||||
3,
|
||||
"Expected 3 sonnet coders (coder-1, coder-2, coder-3), found {}",
|
||||
sonnet_coders.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user