From 07456c27f3ff9836c8684797c75889c2ea1f2125 Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 16 Oct 2026 14:11:19 +0200 Subject: [PATCH] start: log an unreadable applied-rev marker instead of silently treating it as stale start_many mapped every read error on the marker to None via .ok(), so the agent went down the rebuild path while the log blamed a stale rev. A missing marker stays silent; any other error now logs the path and error. Control flow is unchanged. --- hive-c0re/src/job_queue/power.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/hive-c0re/src/job_queue/power.rs b/hive-c0re/src/job_queue/power.rs index 8e286896..fb47a602 100644 --- a/hive-c0re/src/job_queue/power.rs +++ b/hive-c0re/src/job_queue/power.rs @@ -291,7 +291,7 @@ pub async fn start_many( let mut targets = Vec::with_capacity(agents.len()); for agent in agents { let running = lifecycle::is_running(agent).await; - let stored = std::fs::read_to_string(crate::paths::applied_rev_marker(agent)).ok(); + let stored = read_rev_marker(&crate::paths::applied_rev_marker(agent)); let stale = current .as_ref() .is_some_and(|rev| stored.as_deref() != Some(rev.as_str())); @@ -305,6 +305,20 @@ pub async fn start_many( Ok(ids) } +/// The rev `marker` records, or `None` when there is none. A missing file is +/// the normal never-applied case; any other read error is logged, and the +/// caller still treats the rev as stale. +fn read_rev_marker(marker: &std::path::Path) -> Option { + match std::fs::read_to_string(marker) { + Ok(rev) => Some(rev), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => { + tracing::warn!(path = %marker.display(), error = %e, "start: applied-rev marker unreadable; treating rev as stale"); + None + } + } +} + /// Stop `agents` in a **single** DAG. A running agent gets /// `SetWanted(Off) → [Signal → Drain →](graceful) Reconcile`; a down agent /// skips the pointless quiesce but keeps the `Reconcile` as the race-up @@ -354,3 +368,19 @@ pub fn pause_many(coord: &Arc, agents: &[String]) -> anyhow::Result coord.emit_rebuild_queue_snapshot(); Ok(ids) } + +#[cfg(test)] +mod tests { + use super::read_rev_marker; + + #[test] + fn a_rev_marker_reads_as_its_contents_and_none_when_absent_or_unreadable() { + let dir = tempfile::tempdir().unwrap(); + let marker = dir.path().join("rev"); + assert_eq!(read_rev_marker(&marker), None); + std::fs::write(&marker, "abc").unwrap(); + assert_eq!(read_rev_marker(&marker).as_deref(), Some("abc")); + // A directory where the file belongs fails with a non-NotFound error. + assert_eq!(read_rev_marker(dir.path()), None); + } +}