story-kit: done 199_story_web_ui_submits_all_queued_items_at_once

This commit is contained in:
Dave
2026-02-26 12:16:07 +00:00
parent f5f2716a3a
commit 2dbfd42c6e
5 changed files with 258 additions and 17 deletions

View File

@@ -9,6 +9,41 @@ pub struct ProjectConfig {
pub component: Vec<ComponentConfig>,
#[serde(default)]
pub agent: Vec<AgentConfig>,
#[serde(default)]
pub watcher: WatcherConfig,
}
/// Configuration for the filesystem watcher's sweep behaviour.
///
/// Controls how often the watcher checks `5_done/` for items to promote to
/// `6_archived/`, and how long items must remain in `5_done/` before promotion.
#[derive(Debug, Clone, Deserialize, PartialEq)]
pub struct WatcherConfig {
/// How often (in seconds) to check `5_done/` for items to archive.
/// Default: 60 seconds.
#[serde(default = "default_sweep_interval_secs")]
pub sweep_interval_secs: u64,
/// How long (in seconds) an item must remain in `5_done/` before being
/// moved to `6_archived/`. Default: 14400 (4 hours).
#[serde(default = "default_done_retention_secs")]
pub done_retention_secs: u64,
}
impl Default for WatcherConfig {
fn default() -> Self {
Self {
sweep_interval_secs: default_sweep_interval_secs(),
done_retention_secs: default_done_retention_secs(),
}
}
}
fn default_sweep_interval_secs() -> u64 {
60
}
fn default_done_retention_secs() -> u64 {
4 * 60 * 60 // 4 hours
}
#[derive(Debug, Clone, Deserialize)]
@@ -87,6 +122,8 @@ struct LegacyProjectConfig {
#[serde(default)]
component: Vec<ComponentConfig>,
agent: Option<AgentConfig>,
#[serde(default)]
watcher: WatcherConfig,
}
impl Default for ProjectConfig {
@@ -107,6 +144,7 @@ impl Default for ProjectConfig {
stage: None,
inactivity_timeout_secs: default_inactivity_timeout_secs(),
}],
watcher: WatcherConfig::default(),
}
}
}
@@ -147,6 +185,7 @@ impl ProjectConfig {
let config = ProjectConfig {
component: legacy.component,
agent: vec![agent],
watcher: legacy.watcher,
};
validate_agents(&config.agent)?;
return Ok(config);
@@ -166,6 +205,7 @@ impl ProjectConfig {
let config = ProjectConfig {
component: legacy.component,
agent: vec![agent],
watcher: legacy.watcher,
};
validate_agents(&config.agent)?;
Ok(config)
@@ -173,6 +213,7 @@ impl ProjectConfig {
Ok(ProjectConfig {
component: legacy.component,
agent: Vec::new(),
watcher: legacy.watcher,
})
}
}
@@ -517,4 +558,93 @@ model = "sonnet"
assert_eq!(config.agent[0].name, "main");
assert_eq!(config.agent[0].model, Some("sonnet".to_string()));
}
// ── WatcherConfig ──────────────────────────────────────────────────────
#[test]
fn watcher_config_defaults_when_omitted() {
let toml_str = r#"
[[agent]]
name = "coder"
"#;
let config = ProjectConfig::parse(toml_str).unwrap();
assert_eq!(config.watcher.sweep_interval_secs, 60);
assert_eq!(config.watcher.done_retention_secs, 4 * 60 * 60);
}
#[test]
fn watcher_config_custom_values() {
let toml_str = r#"
[watcher]
sweep_interval_secs = 30
done_retention_secs = 7200
[[agent]]
name = "coder"
"#;
let config = ProjectConfig::parse(toml_str).unwrap();
assert_eq!(config.watcher.sweep_interval_secs, 30);
assert_eq!(config.watcher.done_retention_secs, 7200);
}
#[test]
fn watcher_config_partial_override() {
let toml_str = r#"
[watcher]
sweep_interval_secs = 10
[[agent]]
name = "coder"
"#;
let config = ProjectConfig::parse(toml_str).unwrap();
assert_eq!(config.watcher.sweep_interval_secs, 10);
// done_retention_secs should fall back to the default (4 hours).
assert_eq!(config.watcher.done_retention_secs, 4 * 60 * 60);
}
#[test]
fn watcher_config_from_file() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".story_kit");
fs::create_dir_all(&sk).unwrap();
fs::write(
sk.join("project.toml"),
r#"
[watcher]
sweep_interval_secs = 120
done_retention_secs = 3600
[[agent]]
name = "coder"
"#,
)
.unwrap();
let config = ProjectConfig::load(tmp.path()).unwrap();
assert_eq!(config.watcher.sweep_interval_secs, 120);
assert_eq!(config.watcher.done_retention_secs, 3600);
}
#[test]
fn watcher_config_default_when_no_file() {
let tmp = tempfile::tempdir().unwrap();
let config = ProjectConfig::load(tmp.path()).unwrap();
assert_eq!(config.watcher, WatcherConfig::default());
}
#[test]
fn watcher_config_preserved_in_legacy_format() {
let toml_str = r#"
[watcher]
sweep_interval_secs = 15
done_retention_secs = 900
[agent]
command = "claude"
"#;
let config = ProjectConfig::parse(toml_str).unwrap();
assert_eq!(config.watcher.sweep_interval_secs, 15);
assert_eq!(config.watcher.done_retention_secs, 900);
assert_eq!(config.agent.len(), 1);
}
}