use crate::config::HarnessConfig;
use crate::turn_dispatch::TurnDispatchTrackers;
#[derive(Debug)]
pub struct TurnPrologueState {
pub trackers: TurnDispatchTrackers,
pub compression_llm_failures: u32,
pub pressure_warned: bool,
pub opening_rough_tokens: Option<usize>,
pub preflight_evaluated: bool,
pub preflight_ran: bool,
}
impl TurnPrologueState {
pub fn begin(harness: &HarnessConfig) -> Self {
Self {
trackers: TurnDispatchTrackers::with_harness(3, harness),
compression_llm_failures: 0,
pressure_warned: false,
opening_rough_tokens: None,
preflight_evaluated: false,
preflight_ran: false,
}
}
pub fn note_preflight(&mut self, ran: bool) {
self.preflight_evaluated = true;
self.preflight_ran = ran;
}
pub fn observe_opening_tokens(&mut self, rough_tokens: usize) {
self.opening_rough_tokens.get_or_insert(rough_tokens);
}
pub fn reset_tool_dispatch(trackers: &mut TurnDispatchTrackers) {
trackers.tool_guardrail.reset_for_turn();
}
pub fn trace_preflight(&self, gate: bool, disposition: &'static str) {
tracing::debug!(
opening_rough_tokens = ?self.opening_rough_tokens,
preflight_evaluated = self.preflight_evaluated,
preflight_ran = self.preflight_ran,
preflight_gate = gate,
disposition,
"prologue preflight metrics"
);
}
}
pub fn should_run_preflight_estimate(
message_count: usize,
protect_first_n: usize,
protect_last_n: usize,
rough_tokens: usize,
threshold_tokens: usize,
) -> bool {
let protected = protect_first_n.saturating_add(protect_last_n);
let count_gate = message_count > protected;
let size_gate = threshold_tokens > 0 && rough_tokens >= threshold_tokens;
count_gate || size_gate
}
pub fn compression_made_progress(
orig_len: usize,
new_len: usize,
orig_tokens: usize,
new_tokens: usize,
) -> bool {
if new_len < orig_len {
return true;
}
orig_tokens > 0 && new_tokens < (orig_tokens * 95) / 100
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::HarnessConfig;
#[test]
fn ha55_prologue_initializes_trackers() {
let mut state = TurnPrologueState::begin(&HarnessConfig::default());
assert!(!state.trackers.guardrail_halt);
assert_eq!(state.compression_llm_failures, 0);
assert!(!state.pressure_warned);
assert!(!state.preflight_evaluated);
state.observe_opening_tokens(42);
state.observe_opening_tokens(99);
state.note_preflight(true);
assert_eq!(state.opening_rough_tokens, Some(42));
assert!(state.preflight_evaluated);
assert!(state.preflight_ran);
}
#[test]
fn preflight_count_gate() {
assert!(should_run_preflight_estimate(25, 0, 20, 100, 50_000));
assert!(!should_run_preflight_estimate(10, 0, 20, 100, 50_000));
}
#[test]
fn preflight_few_but_huge_size_gate() {
assert!(should_run_preflight_estimate(5, 0, 20, 80_000, 50_000));
assert!(!should_run_preflight_estimate(5, 0, 20, 1_000, 50_000));
}
#[test]
fn compression_progress_token_only() {
assert!(compression_made_progress(10, 10, 100_000, 80_000));
assert!(!compression_made_progress(10, 10, 100_000, 96_000)); assert!(compression_made_progress(12, 10, 100_000, 100_000));
}
}