From e529e6f8f4d6372cb41fed2b05eca3d709e8c6f3 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:29:39 +0200 Subject: [PATCH] Use the system clock in the CLI and headless MCP sessions (#554) --- apps/lightcraft-cli/src/main.rs | 10 +++++----- crates/mcp/src/headless.rs | 12 ++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/lightcraft-cli/src/main.rs b/apps/lightcraft-cli/src/main.rs index 7c9d255e..592a534a 100644 --- a/apps/lightcraft-cli/src/main.rs +++ b/apps/lightcraft-cli/src/main.rs @@ -391,7 +391,7 @@ fn merge(args: &[String]) -> Result<(), String> { } i += 1; } - let mut s = Session::new().with_fs().with_default_denoise_models(); + let mut s = Session::new().with_fs().with_default_denoise_models().with_system_clock(); let paths = expand_paths(&files); let r = s.execute("library.import", &json!({"paths": paths})).map_err(|e| e.to_string())?; let mut ids: Vec = r["imported"].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default(); @@ -652,7 +652,7 @@ fn render(args: &[String]) -> Result<(), String> { } let input = input.ok_or("render: missing input file")?; let output = output.ok_or("render: missing -o OUTPUT")?; - let mut s = Session::new().with_fs().with_default_denoise_models(); + let mut s = Session::new().with_fs().with_default_denoise_models().with_system_clock(); let abs = expand_paths(std::slice::from_ref(&input)); let r = s.execute("library.import", &json!({"paths": abs})).map_err(|e| e.to_string())?; let id = r["imported"][0].as_u64().ok_or_else(|| format!("{input}: not a readable photo"))?; @@ -732,12 +732,12 @@ fn snapshot(args: &[String]) -> Result<(), String> { let t0 = Instant::now(); let mut session = match &library { Some(dir) => { - let mut s = Session::new().with_fs().with_default_denoise_models().with_default_face_models(); + let mut s = Session::new().with_fs().with_default_denoise_models().with_system_clock().with_default_face_models(); s.open_library(dir, false).map_err(|e| library_error(dir, e))?; s } - None if files.is_empty() => Session::with_demo().with_fs().with_default_denoise_models().with_default_face_models(), - None => Session::new().with_fs().with_default_denoise_models().with_default_face_models(), + None if files.is_empty() => Session::with_demo().with_fs().with_default_denoise_models().with_system_clock().with_default_face_models(), + None => Session::new().with_fs().with_default_denoise_models().with_system_clock().with_default_face_models(), }; // Apply compute policy before imports or the first scripted query can discover an adapter. session.execute("app.gpu", &json!({"enabled": false})).map_err(|e| e.to_string())?; diff --git a/crates/mcp/src/headless.rs b/crates/mcp/src/headless.rs index 974de023..ef41bde7 100644 --- a/crates/mcp/src/headless.rs +++ b/crates/mcp/src/headless.rs @@ -32,7 +32,7 @@ impl Drop for Headless { impl Default for Headless { fn default() -> Self { - Self::new(Session::new().with_fs().with_default_denoise_models().with_default_face_models()) + Self::new(Session::new().with_fs().with_default_denoise_models().with_system_clock().with_default_face_models()) } } @@ -43,7 +43,7 @@ impl Headless { /// A headless session with the procedurally generated demo library. pub fn demo() -> Self { - Self::new(Session::with_demo().with_fs().with_default_denoise_models().with_default_face_models()) + Self::new(Session::with_demo().with_fs().with_default_denoise_models().with_system_clock().with_default_face_models()) } fn photo_or_active(&self, p: &Value) -> Result { @@ -201,6 +201,14 @@ pub fn write_image(path: &Path, img: &Rgba8, quality: u8) -> Result<(), String> mod tests { use super::*; + /// A headless session stamps imports and edits with the system clock, not the engine's fixed test clock. + #[test] + fn headless_uses_the_system_clock() { + const FIXED: &str = "2026-09-30T12:00:00"; + assert_ne!((Headless::default().session.clock)(), FIXED); + assert_ne!((Headless::demo().session.clock)(), FIXED); + } + /// `app.export` without `ids` exports the selection (as the desktop app does), else the active photo. #[test] fn export_follows_the_selection() {