huskies: merge 482_refactor_split_agent_definitions_from_project_toml_into_agents_toml

This commit is contained in:
dave
2026-04-04 21:20:36 +00:00
parent f63ed664eb
commit 470e7a5fd5
5 changed files with 455 additions and 354 deletions
+33 -20
View File
@@ -103,7 +103,7 @@ const STORY_KIT_CLAUDE_SETTINGS: &str = r#"{
}
"#;
const DEFAULT_PROJECT_AGENTS_TOML: &str = r#"# Project-wide default QA mode: "server", "agent", or "human".
const DEFAULT_PROJECT_SETTINGS_TOML: &str = r#"# Project-wide default QA mode: "server", "agent", or "human".
# Per-story `qa` front matter overrides this setting.
default_qa = "server"
@@ -114,8 +114,9 @@ default_qa = "server"
# IANA timezone for timer scheduling (e.g. "Europe/London", "America/New_York").
# Timer HH:MM inputs are interpreted in this timezone.
# timezone = "America/New_York"
"#;
[[agent]]
const DEFAULT_AGENTS_TOML: &str = r#"[[agent]]
name = "coder-1"
stage = "coder"
role = "Full-stack engineer. Implements features across all components."
@@ -248,13 +249,14 @@ pub fn detect_script_test(root: &Path) -> String {
script
}
/// Generate a complete `project.toml` for a new project at `root`.
/// Generate a `project.toml` for a new project at `root`.
///
/// Detects the tech stack via [`detect_components_toml`] and prepends the
/// resulting `[[component]]` entries before the default `[[agent]]` sections.
/// Detects the tech stack via [`detect_components_toml`] and combines the
/// resulting `[[component]]` entries with the default project settings.
/// Agent definitions are written to `agents.toml` separately.
fn generate_project_toml(root: &Path) -> String {
let components = detect_components_toml(root);
format!("{components}\n{DEFAULT_PROJECT_AGENTS_TOML}")
format!("{components}\n{DEFAULT_PROJECT_SETTINGS_TOML}")
}
fn write_file_if_missing(path: &Path, content: &str) -> Result<(), String> {
@@ -413,6 +415,7 @@ pub(crate) fn scaffold_story_kit(root: &Path, port: u16) -> Result<(), String> {
write_file_if_missing(&story_kit_root.join("README.md"), STORY_KIT_README)?;
let project_toml_content = generate_project_toml(root);
write_file_if_missing(&story_kit_root.join("project.toml"), &project_toml_content)?;
write_file_if_missing(&story_kit_root.join("agents.toml"), DEFAULT_AGENTS_TOML)?;
write_file_if_missing(&specs_root.join("00_CONTEXT.md"), STORY_KIT_CONTEXT)?;
write_file_if_missing(&tech_root.join("STACK.md"), STORY_KIT_STACK)?;
let script_test_content = detect_script_test(root);
@@ -556,16 +559,21 @@ mod tests {
}
#[test]
fn scaffold_story_kit_project_toml_has_coder_qa_mergemaster() {
fn scaffold_story_kit_agents_toml_has_coder_qa_mergemaster() {
let dir = tempdir().unwrap();
scaffold_story_kit(dir.path(), 3001).unwrap();
let content = fs::read_to_string(dir.path().join(".huskies/project.toml")).unwrap();
assert!(content.contains("[[agent]]"));
assert!(content.contains("stage = \"coder\""));
assert!(content.contains("stage = \"qa\""));
assert!(content.contains("stage = \"mergemaster\""));
assert!(content.contains("model = \"sonnet\""));
// Agent definitions go into agents.toml, not project.toml.
let agents = fs::read_to_string(dir.path().join(".huskies/agents.toml")).unwrap();
assert!(agents.contains("[[agent]]"));
assert!(agents.contains("stage = \"coder\""));
assert!(agents.contains("stage = \"qa\""));
assert!(agents.contains("stage = \"mergemaster\""));
assert!(agents.contains("model = \"sonnet\""));
// project.toml should NOT contain [[agent]] blocks.
let project = fs::read_to_string(dir.path().join(".huskies/project.toml")).unwrap();
assert!(!project.contains("[[agent]]"));
}
#[test]
@@ -1146,19 +1154,24 @@ mod tests {
// --- generate_project_toml ---
#[test]
fn generate_project_toml_includes_both_components_and_agents() {
fn generate_project_toml_includes_components_but_not_agents() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("Cargo.toml"), "[package]\nname = \"x\"\n").unwrap();
let toml = generate_project_toml(dir.path());
// Component section
// Component section should be present
assert!(toml.contains("[[component]]"));
assert!(toml.contains("name = \"server\""));
// Agent sections
assert!(toml.contains("[[agent]]"));
assert!(toml.contains("stage = \"coder\""));
assert!(toml.contains("stage = \"qa\""));
assert!(toml.contains("stage = \"mergemaster\""));
// Agent sections must NOT be in project.toml — they go in agents.toml
assert!(!toml.contains("[[agent]]"));
}
#[test]
fn default_agents_toml_has_coder_qa_mergemaster() {
assert!(DEFAULT_AGENTS_TOML.contains("[[agent]]"));
assert!(DEFAULT_AGENTS_TOML.contains("stage = \"coder\""));
assert!(DEFAULT_AGENTS_TOML.contains("stage = \"qa\""));
assert!(DEFAULT_AGENTS_TOML.contains("stage = \"mergemaster\""));
}
#[test]