algocline_core/progress.rs
1use std::sync::{Arc, Mutex};
2
3use crate::metrics::SessionStatus;
4
5// ─── Progress ───────────────────────────────────────────────
6
7/// Structured progress information reported by strategies via `alc.progress()`.
8///
9/// Stored in `SessionStatus` and readable via `alc_status` MCP tool.
10/// Not all strategies report progress — this is opt-in for strategies
11/// that benefit from structured step tracking (e.g. multi-round pipelines).
12#[derive(Debug, Clone, serde::Serialize)]
13pub struct ProgressInfo {
14 /// Current step (1-based).
15 pub step: u64,
16 /// Total expected steps (0 = unknown/indeterminate).
17 pub total: u64,
18 /// Optional human-readable message for the current step.
19 pub message: Option<String>,
20}
21
22/// Cheap, cloneable handle for writing progress from the Lua bridge.
23///
24/// Wraps the shared `SessionStatus` to expose only progress-related writes.
25/// Passed to `bridge::register_progress()`.
26///
27/// # Call site and threading
28///
29/// Called exclusively from the Lua OS thread via `alc.progress()`.
30/// Acquires `std::sync::Mutex<SessionStatus>` for a few microseconds
31/// (single field assignment). See `SessionStatus` doc for full locking design.
32///
33/// # Poison policy
34///
35/// Silently skips on poison. Progress is observational (consumed by
36/// `alc_status`) — a missed update degrades monitoring but does not
37/// affect execution correctness. If you observe stale progress in
38/// `alc_status` while the session is active, mutex poison from an
39/// earlier OOM panic is a possible cause.
40#[derive(Clone)]
41pub struct ProgressHandle {
42 auto: Arc<Mutex<SessionStatus>>,
43}
44
45impl ProgressHandle {
46 pub(crate) fn new(auto: Arc<Mutex<SessionStatus>>) -> Self {
47 Self { auto }
48 }
49
50 /// Set the current progress. Called from `alc.progress(step, total, msg?)`.
51 ///
52 /// Silently skips on mutex poison (see ProgressHandle doc for rationale).
53 pub fn set(&self, step: u64, total: u64, message: Option<String>) {
54 if let Ok(mut m) = self.auto.lock() {
55 m.progress = Some(ProgressInfo {
56 step,
57 total,
58 message,
59 });
60 }
61 }
62}