loopsmith_core/config/gates.rs
1//! Section F — the layered exits.
2
3use super::yes;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(deny_unknown_fields)]
8pub struct StopGates {
9 /// Hard ceiling on whole-loop iterations.
10 #[serde(default = "default_max_iterations")]
11 pub max_iterations: u32,
12 /// Per-node revision ceiling. A node that has been dispatched this many
13 /// times without its goals being satisfied stops being dispatched, so one
14 /// stuck node cannot burn the whole iteration budget.
15 #[serde(default = "default_max_revisions")]
16 pub max_revisions_per_node: u32,
17 /// Wall-clock budget for the whole run.
18 #[serde(default)]
19 pub max_wall_clock_seconds: Option<u64>,
20 /// Token budget for the whole run, summed across providers.
21 #[serde(default)]
22 pub max_tokens: Option<u64>,
23 /// Currency budget for the whole run.
24 #[serde(default)]
25 pub max_cost_usd: Option<f64>,
26 /// Halt when this many consecutive iterations produce no measurable
27 /// change. Jidoka: stop the line rather than spin.
28 #[serde(default = "default_no_progress")]
29 pub no_progress_iterations: u32,
30 /// Perturb the run after this many stalled iterations, instead of waiting
31 /// to halt at `no_progress_iterations`.
32 ///
33 /// Must be strictly less than `no_progress_iterations`: the point is to try
34 /// something different *before* giving up, and a threshold at or past the
35 /// halt point never fires. Leave it unset to halt without ever varying —
36 /// perturbation costs a provider call and changes what the loop does, so it
37 /// is opt-in.
38 #[serde(default)]
39 pub no_progress_iterations_randomness: Option<u32>,
40 /// Stop as soon as every `overall` success scenario is met.
41 #[serde(default = "yes")]
42 pub stop_on_overall_success: bool,
43}
44
45impl Default for StopGates {
46 fn default() -> Self {
47 Self {
48 max_iterations: default_max_iterations(),
49 max_revisions_per_node: default_max_revisions(),
50 max_wall_clock_seconds: None,
51 max_tokens: None,
52 max_cost_usd: None,
53 no_progress_iterations: default_no_progress(),
54 no_progress_iterations_randomness: None,
55 stop_on_overall_success: true,
56 }
57 }
58}
59
60fn default_max_iterations() -> u32 {
61 10
62}
63fn default_max_revisions() -> u32 {
64 3
65}
66fn default_no_progress() -> u32 {
67 3
68}