From 115a7c5569c2912ab01f51efdeb177e97dc12e45 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 22:28:49 +0000 Subject: [PATCH] Ember helper (F), from the 0.3.16 window on PC 1: the tune path wrote its request index as the wire sequence ("00 dev 1", "01 pl 160") below the cap path's unix-based numbers, so the helper skipped every tune command as stale and both climbs stopped on "the helper did not run sequence 1 within 15 s"; every writer now draws from one monotonic space (powertask::wire_seq), the acknowledgement matches the wire number; test Co-Authored-By: Claude Fable 5.1 (cherry picked from commit 9e22a7a399614780deb1c53ef3c5ee75c854a821) --- app/igneum-app/src/engine.rs | 11 +++++++---- app/igneum-app/src/powertask.rs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index b94715b41..c76cbd129 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -1905,7 +1905,7 @@ impl Engine { std::thread::spawn(move || { let r = crate::powertask::start().and_then(|_| { let _ = std::fs::create_dir_all(&dir); - let mut seq = crate::platform::unix_now() % 1_000_000; + let mut seq = crate::powertask::wire_seq(); let mut text = String::new(); for (dev, w) in &pairs { seq += 1; @@ -2583,7 +2583,7 @@ impl Engine { // under a scratch IGNEUM_APP_DATA), no prompt let hdir = crate::powertask::helper_dir(); let _ = std::fs::create_dir_all(&hdir); - let _ = std::fs::write(hdir.join("cmd.txt"), format!("{} dev {}\n", crate::platform::unix_now() % 1_000_000, c.device)); + let _ = std::fs::write(hdir.join("cmd.txt"), format!("{} dev {}\n", crate::powertask::wire_seq(), c.device)); crate::powertask::start()?; self.shared.log("tune helper: the Igneum Power Helper task (no prompt)"); self.sweep_helper = true; @@ -2664,7 +2664,10 @@ impl Engine { return; } let dev = device.to_string(); - let cmd = format!("{seq}0 dev {dev}\n{seq}1 pl {w}\n{seq}2 {}\n{seq}3 {}\n", if clock > 0 { format!("lgc {clock}") } else { "rgc".to_string() }, if mem > 0 { format!("lmc {mem}") } else { "rmc".to_string() }); + // (F) the wire numbers come from the one monotonic space every writer uses (powertask::wire_seq); + // `seq` (the run's request index) stays the engine's own id for the acknowledgement + let wire = crate::powertask::wire_seq(); + let cmd = format!("{} dev {dev}\n{} pl {w}\n{} {}\n{} {}\n", wire, wire + 1, wire + 2, if clock > 0 { format!("lgc {clock}") } else { "rgc".to_string() }, wire + 3, if mem > 0 { format!("lmc {mem}") } else { "rmc".to_string() }); let dir = self.helper_cmd_dir(); let is_task = self.sweep_helper_is_task; // 6 October 2026, PC 1: (B) the task's helper had exited while the engine's flag still said it ran, @@ -2674,7 +2677,7 @@ impl Engine { // is started and a fresh heartbeat awaited BEFORE the command is written; and the acknowledgement // is the helper's own log line for this sequence (`3 nvidia-smi ...`), read for up to 15 s; // a missing line is a failure with that reason - let want = format!(" {seq}3 nvidia-smi"); + let want = format!(" {} nvidia-smi", wire + 3); let log_file = dir.join("helper.log"); std::thread::spawn(move || { if is_task { diff --git a/app/igneum-app/src/powertask.rs b/app/igneum-app/src/powertask.rs index 3f22d2378..a4f9043fd 100644 --- a/app/igneum-app/src/powertask.rs +++ b/app/igneum-app/src/powertask.rs @@ -119,6 +119,17 @@ pub fn parse_line(line: &str) -> Option<(u64, HelperCmd)> { } } +/// The sequence number a command line carries on the wire. The helper runs a line only when its number is above +/// every number it has seen (its `last_seq`, seeded from the file at its start), so every writer must draw from +/// ONE monotonic space: the unix time modulo a million (six digits, what `parse_line` accepts), plus a small +/// offset per line. (F) 6 October 2026, 22:19Z on PC 1: the tune path wrote its request index ("00 dev 1", "01 pl +/// 160", ...) while the cap path had written 305327 and up, so the helper skipped every tune command as stale and +/// both climbs stopped on "the helper did not run sequence 1 within 15 s". Wraps every 11.6 days; the helper's +/// idle exit (20 minutes) re-seeds it from the file, so a wrap costs at most one tune step. +pub fn wire_seq() -> u64 { + crate::platform::unix_now() % 999_990 +} + /// The commands a helper acts on: the lines added after its start (`skip` = the line count at the start, 0 again /// when the file shrank). A stale `quit` or `remove` from an earlier engine is never a command. pub fn commands_after(text: &str, skip: usize) -> Vec<(u64, HelperCmd)> { @@ -360,6 +371,9 @@ mod tests { assert_eq!(commands_after("5 dev 0\n6 pl 460\nquit\n7 dev 1\n", 3), vec![(7, HelperCmd::Dev("1".into()))]); assert_eq!(commands_after("quit\n", 3), vec![(0, HelperCmd::Quit)]); // the heartbeat: fresh within ALIVE_MAX_S, dead after, dead when unreadable + // (F) every wire number parses (six digits at most) and leaves room for the four lines of a tune step + let w = wire_seq(); + assert!(w + 3 <= 999_999 && parse_line(&format!("{} rmc", w + 3)).is_some()); assert!(alive_at("1791309325", 1791309325 + ALIVE_MAX_S)); assert!(!alive_at("1791309325", 1791309325 + ALIVE_MAX_S + 1)); assert!(!alive_at("", 1791309325));