mod completion_gate;
use serde::Deserialize;
pub use completion_gate::{
CompletionGateConfig, CompletionGateConfigToml, CompletionGateDeliverableEntry,
CompletionGateMode, CompletionGateVerifyEntry, GenericGateMode, ManifestShell,
MinLinesGateConfig, VerifySource,
};
#[derive(Debug, Clone)]
pub struct AdversarialAuditConfig {
pub enabled: bool,
pub max_audit_rounds: u32,
pub max_tokens: u32,
pub mode: CompletionGateMode,
}
impl Default for AdversarialAuditConfig {
fn default() -> Self {
Self {
enabled: false,
max_audit_rounds: 1,
max_tokens: 1500,
mode: CompletionGateMode::Observe,
}
}
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct AdversarialAuditConfigToml {
#[serde(default)]
pub enabled: Option<bool>,
#[serde(default)]
pub max_audit_rounds: Option<u32>,
#[serde(default)]
pub max_tokens: Option<u32>,
#[serde(default)]
pub mode: Option<String>,
}
impl AdversarialAuditConfigToml {
#[must_use]
pub fn into_runtime(self) -> AdversarialAuditConfig {
let defaults = AdversarialAuditConfig::default();
AdversarialAuditConfig {
enabled: self.enabled.unwrap_or(defaults.enabled),
max_audit_rounds: self.max_audit_rounds.unwrap_or(defaults.max_audit_rounds),
max_tokens: self.max_tokens.unwrap_or(defaults.max_tokens),
mode: self
.mode
.as_deref()
.map(CompletionGateMode::from_str_lossy)
.unwrap_or(defaults.mode),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LhtMode {
#[default]
Auto,
Strict,
}
impl LhtMode {
#[must_use]
pub fn is_strict(self) -> bool {
matches!(self, LhtMode::Strict)
}
#[must_use]
pub fn from_optional_str(s: Option<&str>) -> Self {
match s.map(|v| v.trim().to_ascii_lowercase()).as_deref() {
Some("strict") => LhtMode::Strict,
_ => LhtMode::Auto,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AutoEnterCraft {
#[default]
UserConfirm,
OnMicroPass,
OnGraphComplete,
OnManifestExhausted,
Off,
}
impl AutoEnterCraft {
#[must_use]
pub fn from_optional_str(s: Option<&str>) -> Self {
match s.map(|v| v.trim().to_ascii_lowercase()).as_deref() {
Some("on_micro_pass" | "auto" | "immediate") => AutoEnterCraft::OnMicroPass,
Some("on_graph_complete" | "graph_complete") => AutoEnterCraft::OnGraphComplete,
Some("on_manifest_exhausted" | "manifest_exhausted") => {
AutoEnterCraft::OnManifestExhausted
}
Some("off" | "disabled" | "false") => AutoEnterCraft::Off,
_ => AutoEnterCraft::UserConfirm,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MacroPhase {
#[default]
Implement,
Craft,
Remediation,
}
impl MacroPhase {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
MacroPhase::Implement => "implement",
MacroPhase::Craft => "craft",
MacroPhase::Remediation => "remediation",
}
}
}
#[derive(Debug, Clone)]
pub struct MacroLoopConfig {
pub enabled: bool,
pub max_macro_cycles: u32,
pub max_craft_rounds_per_cycle: u32,
pub auto_enter_craft: AutoEnterCraft,
pub craft_on_small_tasks: bool,
pub min_checklist_items_for_craft: u32,
}
impl Default for MacroLoopConfig {
fn default() -> Self {
Self {
enabled: false,
max_macro_cycles: 3,
max_craft_rounds_per_cycle: 2,
auto_enter_craft: AutoEnterCraft::UserConfirm,
craft_on_small_tasks: false,
min_checklist_items_for_craft: 3,
}
}
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct MacroLoopConfigToml {
#[serde(default)]
pub enabled: Option<bool>,
#[serde(default)]
pub max_macro_cycles: Option<u32>,
#[serde(default)]
pub max_craft_rounds_per_cycle: Option<u32>,
#[serde(default)]
pub auto_enter_craft: Option<String>,
#[serde(default)]
pub craft_on_small_tasks: Option<bool>,
#[serde(default)]
pub min_checklist_items_for_craft: Option<u32>,
}
impl MacroLoopConfigToml {
#[must_use]
pub fn into_runtime(self) -> MacroLoopConfig {
let defaults = MacroLoopConfig::default();
MacroLoopConfig {
enabled: self.enabled.unwrap_or(defaults.enabled),
max_macro_cycles: self
.max_macro_cycles
.unwrap_or(defaults.max_macro_cycles)
.clamp(1, 8),
max_craft_rounds_per_cycle: self
.max_craft_rounds_per_cycle
.unwrap_or(defaults.max_craft_rounds_per_cycle)
.clamp(1, 4),
auto_enter_craft: AutoEnterCraft::from_optional_str(self.auto_enter_craft.as_deref()),
craft_on_small_tasks: self
.craft_on_small_tasks
.unwrap_or(defaults.craft_on_small_tasks),
min_checklist_items_for_craft: self
.min_checklist_items_for_craft
.unwrap_or(defaults.min_checklist_items_for_craft)
.max(1),
}
}
}
#[derive(Debug, Clone)]
pub struct LongHorizonConfig {
pub enabled: bool,
pub mode: LhtMode,
pub max_nudges_per_item: u32,
pub blocked_nudges_without_progress: u32,
pub reinject_every_steps: u32,
pub progress_via_git: bool,
pub auto_continue: bool,
pub max_auto_continue_rounds: u32,
pub completion_gate: CompletionGateConfig,
pub macro_loop: MacroLoopConfig,
pub adversarial_audit: AdversarialAuditConfig,
}
impl Default for LongHorizonConfig {
fn default() -> Self {
Self {
enabled: false,
mode: LhtMode::Auto,
max_nudges_per_item: 5,
blocked_nudges_without_progress: 3,
reinject_every_steps: 0,
progress_via_git: true,
auto_continue: false,
max_auto_continue_rounds: 16,
completion_gate: CompletionGateConfig::default(),
macro_loop: MacroLoopConfig::default(),
adversarial_audit: AdversarialAuditConfig::default(),
}
}
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct LongHorizonConfigToml {
#[serde(default)]
pub enabled: Option<bool>,
#[serde(default)]
pub mode: Option<String>,
#[serde(default)]
pub max_nudges_per_item: Option<u32>,
#[serde(default)]
pub blocked_nudges_without_progress: Option<u32>,
#[serde(default)]
pub reinject_every_steps: Option<u32>,
#[serde(default)]
pub progress_via_git: Option<bool>,
#[serde(default)]
pub auto_continue: Option<bool>,
#[serde(default)]
pub max_auto_continue_rounds: Option<u32>,
#[serde(default)]
pub completion_gate: Option<CompletionGateConfigToml>,
#[serde(default)]
pub macro_loop: Option<MacroLoopConfigToml>,
#[serde(default)]
pub adversarial_audit: Option<AdversarialAuditConfigToml>,
}
impl LongHorizonConfigToml {
#[must_use]
pub fn into_runtime(self) -> LongHorizonConfig {
let defaults = LongHorizonConfig::default();
LongHorizonConfig {
enabled: self.enabled.unwrap_or(defaults.enabled),
mode: LhtMode::from_optional_str(self.mode.as_deref()),
max_nudges_per_item: self
.max_nudges_per_item
.unwrap_or(defaults.max_nudges_per_item),
blocked_nudges_without_progress: self
.blocked_nudges_without_progress
.unwrap_or(defaults.blocked_nudges_without_progress),
reinject_every_steps: self
.reinject_every_steps
.unwrap_or(defaults.reinject_every_steps),
progress_via_git: self.progress_via_git.unwrap_or(defaults.progress_via_git),
auto_continue: self.auto_continue.unwrap_or(defaults.auto_continue),
max_auto_continue_rounds: self
.max_auto_continue_rounds
.unwrap_or(defaults.max_auto_continue_rounds),
completion_gate: self
.completion_gate
.map(CompletionGateConfigToml::into_runtime)
.unwrap_or_default(),
macro_loop: self
.macro_loop
.map(MacroLoopConfigToml::into_runtime)
.unwrap_or_default(),
adversarial_audit: self
.adversarial_audit
.map(AdversarialAuditConfigToml::into_runtime)
.unwrap_or_default(),
}
}
}