| 466 | } |
| 467 | |
| 468 | async fn load_loop_checkpoint(&self, run_id: &str) -> Result<Option<LoopCheckpoint>> { |
| 469 | let path = self.loop_checkpoint_path(run_id); |
| 470 | if !path.exists() { |
| 471 | return Ok(None); |
| 472 | } |
| 473 | let json = fs::read_to_string(&path) |
| 474 | .await |
| 475 | .with_context(|| format!("Failed to read loop checkpoint from {}", path.display()))?; |
| 476 | let checkpoint: LoopCheckpoint = serde_json::from_str(&json) |
| 477 | .with_context(|| format!("Failed to parse loop checkpoint from {}", path.display()))?; |
| 478 | // Refuse to hand back a checkpoint written by a future, incompatible |
| 479 | // schema version — its field semantics may differ (see the contract |
| 480 | // on LoopCheckpoint::ensure_loadable). |
| 481 | checkpoint.ensure_loadable()?; |
| 482 | Ok(Some(checkpoint)) |
| 483 | } |
| 484 | |
| 485 | async fn delete_loop_checkpoint(&self, run_id: &str) -> Result<()> { |
| 486 | let path = self.loop_checkpoint_path(run_id); |