fix: Preserve manual oneliner across delete and reset paths
Consolidate the sticky-Manual rule for oneliner.json into a single helper paths::delete_oneliner_unless_manual that holds the per-case lock, reads the state, keeps OnelinerState::Manual, and removes any LLM-derived variant. Three previously-direct deletion paths now route through it: handle_delete_recording, reset_case_artefacts (admin single-reset), and bulk_reset (admin bulk action). Before this fix a clinician's manual override was wiped by a recording delete, letting the LLM auto-regen path overwrite it on the next view. Tests: 4 unit tests for the wrapper contract, integration tests for the recording-delete and case-reset endpoints (Manual preserved, Ready wiped), and a static-scan invariant that fails on direct remove_file(... ONELINER_FILENAME) calls outside paths.rs.
This commit is contained in:
@@ -14,6 +14,7 @@ use crate::config::Config;
|
||||
use crate::csrf::{CsrfForm, HasCsrfToken};
|
||||
use crate::error::AppError;
|
||||
use crate::events::{self, CaseEventKind, EventSender};
|
||||
use crate::oneliner_locks::OnelinerLocks;
|
||||
use crate::paths::{CloseMarker, write_close_marker};
|
||||
use crate::routes::case_actions::{
|
||||
build_analysis_input, reset_case_artefacts, write_input_create_new,
|
||||
@@ -45,6 +46,7 @@ pub async fn handle_bulk_action(
|
||||
State(config): State<Arc<Config>>,
|
||||
State(analyze_tx): State<AnalyzeSender>,
|
||||
State(events_tx): State<EventSender>,
|
||||
State(locks): State<OnelinerLocks>,
|
||||
CsrfForm(form): CsrfForm<BulkForm>,
|
||||
) -> Result<Redirect, AppError> {
|
||||
// Bulk actions are admin-only across the board: the UI hides the
|
||||
@@ -78,7 +80,9 @@ pub async fn handle_bulk_action(
|
||||
.await
|
||||
}
|
||||
BulkAction::Close => bulk_close(&events_tx, &user.slug, &user_root, &form.case_ids).await,
|
||||
BulkAction::Reset => bulk_reset(&events_tx, &user.slug, &user_root, &form.case_ids).await,
|
||||
BulkAction::Reset => {
|
||||
bulk_reset(&events_tx, &locks, &user.slug, &user_root, &form.case_ids).await
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Redirect::to("/web/cases"))
|
||||
@@ -185,6 +189,7 @@ async fn bulk_close(
|
||||
|
||||
async fn bulk_reset(
|
||||
events_tx: &EventSender,
|
||||
locks: &OnelinerLocks,
|
||||
slug: &str,
|
||||
user_root: &std::path::Path,
|
||||
case_ids: &[String],
|
||||
@@ -202,7 +207,7 @@ async fn bulk_reset(
|
||||
skipped += 1;
|
||||
continue;
|
||||
};
|
||||
if let Err(e) = reset_case_artefacts(&case_dir).await {
|
||||
if let Err(e) = reset_case_artefacts(&case_dir, locks).await {
|
||||
warn!(slug = %slug, case_id = %case_id, error = %e, "bulk-reset: failed");
|
||||
skipped += 1;
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user