Refactor auto-trigger and recovery logic

This commit introduces a significant refactor to the auto-trigger and
recovery mechanisms for the analysis pipeline. The core change is the
introduction of a more granular `CaseAnalysisState` enum, replacing the
simpler `AutoDecision`.

The `evaluate_state` function now computes the full lifecycle state of a
case, considering factors like the presence of recordings, pending
transcripts, queued jobs, failure markers, document staleness, and an
idle threshold. This provides a richer understanding of the case's
status.

The `evaluate_case` function is retained as a backwards-compatible
adapter for existing callsites and tests. It maps the new
`CaseAnalysisState` to the old `AutoDecision` enum, simplifying the
decision to "enqueue" or "skip."

The `recovery` module is also refactored. The `scan_and_enqueue`
function is replaced by `boot_scan_state`, which is now purely
observational. It aggregates statistics about cases in different states
at boot time and logs them, but it no longer attempts to re-enqueue
jobs. The responsibility for triggering analysis now lies with the
per-render `try_enqueue_all_for_user` logic, which utilizes the new
typed state machine.

This refactoring aims to provide more clarity and control over the
analysis pipeline's state management, particularly in handling cases
that have previously failed or are waiting for further input.
This commit is contained in:
2026-05-05 14:26:05 +02:00
parent 9e8db7518d
commit 0660727faf
6 changed files with 403 additions and 252 deletions
+11 -3
View File
@@ -184,7 +184,9 @@ async fn main() {
});
}
// Analyze pipeline: channel + worker + recovery scan.
// Analyze pipeline: channel + worker. Boot-scan is observation-only —
// the per-render `try_enqueue_all_for_user` covers auto-trigger via
// the typed `CaseAnalysisState` machine.
let (analyze_tx, analyze_rx) = analyze::channel();
let analyze_busy: doctate_server::WorkerBusy =
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
@@ -196,10 +198,16 @@ async fn main() {
events_tx.clone(),
));
{
let tx = analyze_tx.clone();
let data_path = config.data_path.clone();
let idle_threshold =
std::time::Duration::from_secs(config.analysis_idle_threshold_secs);
tokio::spawn(async move {
analyze::recovery::scan_and_enqueue(&data_path, &tx).await;
analyze::recovery::boot_scan_state(
&data_path,
idle_threshold,
std::time::SystemTime::now(),
)
.await;
});
}