Skip to main content

eval_magic/workspace/
mod.rs

1//! Baseline management and workspace cleanup.
2//!
3//! The whole workspace-artifact lifecycle — snapshot, promote, teardown —
4//! lives in one module.
5
6pub mod promote;
7pub mod snapshot;
8pub mod teardown;
9
10pub use promote::{NotesStatus, PromoteOptions, PromoteResult, promote_baseline};
11pub use snapshot::snapshot;
12pub use teardown::{
13    KeptIteration, PROMOTED_MARKER, SNAPSHOT_META, WorkspaceCleanupSummary, cleanup_workspace,
14};
15
16use std::fs;
17use std::path::Path;
18use std::time::{SystemTime, UNIX_EPOCH};
19
20use chrono::{DateTime, SecondsFormat};
21use serde::Serialize;
22
23/// A recoverable failure while managing workspace artifacts. Library-side
24/// convention (mirrors `pipeline::PipelineError`); the CLI boundary maps it to
25/// `anyhow`.
26#[derive(Debug, thiserror::Error)]
27pub enum WorkspaceError {
28    /// A user-facing failure with a ready-to-display message.
29    #[error("{0}")]
30    Message(String),
31    /// Filesystem IO failure.
32    #[error(transparent)]
33    Io(#[from] std::io::Error),
34    /// JSON parse/serialize failure.
35    #[error(transparent)]
36    Json(#[from] serde_json::Error),
37}
38
39/// The current wall clock as `2026-06-08T12:00:00.000Z` (the `promoted_at`
40/// stamp). `chrono` ships without its `clock` feature, so the instant comes
41/// from `std::time`. Mirrors the per-module precedent (`sandbox::now_ms`,
42/// `pipeline::io::now_iso8601`).
43pub(crate) fn now_iso8601() -> String {
44    let ms = SystemTime::now()
45        .duration_since(UNIX_EPOCH)
46        .unwrap_or_default()
47        .as_millis() as i64;
48    DateTime::from_timestamp_millis(ms)
49        .unwrap_or_default()
50        .to_rfc3339_opts(SecondsFormat::Millis, true)
51}
52
53/// Write `value` to `path` as 2-space-pretty JSON with a trailing newline —
54/// the stable on-disk format for every artifact this binary writes.
55pub(crate) fn write_json<T: Serialize>(path: &Path, value: &T) -> Result<(), WorkspaceError> {
56    let mut text = serde_json::to_string_pretty(value)?;
57    text.push('\n');
58    fs::write(path, text)?;
59    Ok(())
60}