From 0659fab5f2c33878b615d1d6fdd25f4a8eb84b8d Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 18:31:38 +0000 Subject: [PATCH] Ember helper, two bugs from PC 1 on 6 October 2026: (A) a helper acts only on lines added after its start, so a stale quit or remove never kills a later start (commands_after + test); (B) the engine starts the task again before every task-path step and the acknowledgement is the helper's own log line for the sequence (up to 15 s), a missing line fails the step with that reason instead of a blind 4-second ack. Playbooks read the app log from \logs, where it lives Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/engine.rs | 33 +++++++++++++++++--- app/igneum-app/src/powertask.rs | 27 +++++++++++++--- relay/playbooks/ember-installed-tune-pc1.ps1 | 2 +- relay/playbooks/ember-proof-collect-pc1.ps1 | 4 +-- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 11e133f5b..dd0688023 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -2567,11 +2567,36 @@ impl Engine { } 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() }); - let _ = std::fs::write(self.helper_cmd_dir().join("cmd.txt"), cmd); - // the helper polls twice a second and nvidia-smi answers within a second or two + let dir = self.helper_cmd_dir(); + let is_task = self.sweep_helper_is_task; + let _ = std::fs::create_dir_all(&dir); + let _ = std::fs::write(dir.join("cmd.txt"), cmd); + // 6 October 2026, PC 1 18:18Z: the task's helper had exited (20 idle minutes) while the engine's flag + // still said it ran, so the commands went to nobody and a blind 4-second sleep called them + // acknowledged. Now: the task is started again before every step (Start-ScheduledTask ignores a + // start while one runs), 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 + if is_task { + if let Err(e) = crate::powertask::start() { + self.shared.log(&format!("tune helper: the task could not be started ({e})")); + } + } + let want = format!(" {seq}3 nvidia-smi"); + let log_file = dir.join("helper.log"); std::thread::spawn(move || { - std::thread::sleep(Duration::from_secs(4)); - shared.send(Cmd::TuneSet(seq, Ok("helper".into()))); + let until = Instant::now() + Duration::from_secs(if is_task { 15 } else { 6 }); + loop { + std::thread::sleep(Duration::from_millis(500)); + let text = std::fs::read_to_string(&log_file).unwrap_or_default(); + if let Some(line) = text.lines().rev().find(|l| l.contains(&want)) { + shared.send(Cmd::TuneSet(seq, Ok(format!("helper: {}", line.trim())))); + return; + } + if Instant::now() >= until { + shared.send(Cmd::TuneSet(seq, Err(format!("the helper did not run sequence {seq} within {} s (no line in {})", if is_task { 15 } else { 6 }, log_file.display())))); + return; + } + } }); } "amd" => { diff --git a/app/igneum-app/src/powertask.rs b/app/igneum-app/src/powertask.rs index 232ef5589..382f893d7 100644 --- a/app/igneum-app/src/powertask.rs +++ b/app/igneum-app/src/powertask.rs @@ -79,6 +79,13 @@ pub fn parse_line(line: &str) -> Option<(u64, HelperCmd)> { } } +/// 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)> { + let skip = if text.lines().count() < skip { 0 } else { skip }; + text.lines().skip(skip).filter_map(parse_line).collect() +} + /// The nvidia-smi arguments a command becomes (None for the verbs that run nothing). pub fn smi_args(dev: &str, c: &HelperCmd) -> Option> { match c { @@ -195,17 +202,25 @@ pub fn run_helper(dir: &Path) -> i32 { } }; log("helper started (scheduled task, elevated)"); - // a stale file from an earlier run is not a command: only lines after the start count - let mut last_seq: u64 = std::fs::read_to_string(&cmd_file).ok().and_then(|t| t.lines().filter_map(parse_line).map(|(s, _)| s).max()).unwrap_or(0); - let mut last_text = String::new(); + // a stale file from an earlier run is not a command: only lines ADDED after the start count. 6 October 2026, + // PC 1 17:55:55Z: a helper that started after an engine had written `quit` read that line and exited in the + // same second, and every later start did the same; so the lines present at the start are skipped whole + // (quit and remove included), and a file that shrinks starts the count again + let initial = std::fs::read_to_string(&cmd_file).unwrap_or_default(); + let mut last_seq: u64 = initial.lines().filter_map(parse_line).map(|(s, _)| s).max().unwrap_or(0); + let mut skip = initial.lines().count(); + let mut last_text = initial; let mut dev = "0".to_string(); let mut idle = Instant::now(); let smi = crate::platform::tool("nvidia-smi"); loop { let text = std::fs::read_to_string(&cmd_file).unwrap_or_default(); if text != last_text { + if text.lines().count() < skip { + skip = 0; + } last_text = text.clone(); - for (seq, c) in text.lines().filter_map(parse_line) { + for (seq, c) in commands_after(&text, skip) { match c { HelperCmd::Quit => { log("quit"); @@ -296,6 +311,10 @@ mod tests { assert_eq!(parse_line("quit"), Some((0, HelperCmd::Quit))); assert_eq!(parse_line("remove"), Some((0, HelperCmd::Remove))); assert_eq!(parse_line("12 reregister"), Some((12, HelperCmd::Reregister))); + // a stale quit present at the start is skipped; a quit added later counts; a rewritten (shorter) file counts whole + assert_eq!(commands_after("5 dev 0\n6 pl 460\nquit\n", 3), vec![]); + 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)]); assert_eq!(parse_line("12 reregister C:\\evil.exe"), None, "no path argument: the helper picks the install folder itself"); assert_eq!(smi_args("0", &HelperCmd::Reregister), None); assert!(readback_command().contains("Actions[0].Execute")); diff --git a/relay/playbooks/ember-installed-tune-pc1.ps1 b/relay/playbooks/ember-installed-tune-pc1.ps1 index 70a0b7c68..00da23102 100644 --- a/relay/playbooks/ember-installed-tune-pc1.ps1 +++ b/relay/playbooks/ember-installed-tune-pc1.ps1 @@ -35,7 +35,7 @@ if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Outp Start-Sleep -Seconds 2 $order = @($cards | Where-Object { $_.vendor -eq 'amd' }) + @($cards | Where-Object { $_.vendor -eq 'nvidia' }) $deadline = (Get-Date).AddMinutes(27) -$logs = Join-Path $appDir 'logs' +$logs = Join-Path $env:LOCALAPPDATA 'igneum\logs' # the app's log folder is \logs (platform::log_dir), not app\logs (6 October 2026: the first window read no log at all) function LogMark() { $l = Get-ChildItem -Path $logs -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($l) { return @{ f = $l.FullName; n = (Get-Content -LiteralPath $l.FullName).Count } } else { return @{ f = ''; n = 0 } } } function LogSince($m) { if (-not $m.f) { return @() }; $all = @(Get-Content -LiteralPath $m.f -ErrorAction SilentlyContinue); if ($all.Count -le $m.n) { return @() }; return @($all[$m.n..($all.Count - 1)]) } $rows = 0 diff --git a/relay/playbooks/ember-proof-collect-pc1.ps1 b/relay/playbooks/ember-proof-collect-pc1.ps1 index 3db3b7e83..91fb11a1e 100644 --- a/relay/playbooks/ember-proof-collect-pc1.ps1 +++ b/relay/playbooks/ember-proof-collect-pc1.ps1 @@ -3,10 +3,10 @@ # last two hours from the Security log. Never writes, quits, pauses or resumes anything. $ErrorActionPreference = 'Continue' $a = Join-Path $env:LOCALAPPDATA 'igneum\app' -$l = Get-ChildItem (Join-Path $a 'logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +$l = Get-ChildItem (Join-Path $env:LOCALAPPDATA 'igneum\logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if ($l) { Write-Output ('RESULT PROOF log=' + $l.FullName) - Get-Content -LiteralPath $l.FullName | Where-Object { $_ -match 'power cap|Power Helper|elevat|UAC|runas|tune' } | Select-Object -Last 24 | ForEach-Object { Write-Output ('RESULT PROOF applog ' + $_) } + Get-Content -LiteralPath $l.FullName | Where-Object { $_ -match 'power cap|Power Helper|elevat|UAC|runas|tune' } | Select-Object -Last 60 | ForEach-Object { Write-Output ('RESULT PROOF applog ' + $_) } } $h = Join-Path $a 'sweep\helper.log' if (Test-Path -LiteralPath $h) { Get-Content -LiteralPath $h | Select-Object -Last 15 | ForEach-Object { Write-Output ('RESULT PROOF helper ' + $_) } } else { Write-Output 'RESULT PROOF helper none' }