C35: every quit names its source (Cmd::Quit carries it: the window host's stdin, the host gone, POST /api/quit, the --sweep run's end); the --sweep job never counts as Power control and sets no cap at start (it raised a UAC prompt on PC 1 at 22:30 UTC); the tune playbook's budget quit goes only to its own scratch URL file and says so

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-05 22:53:32 +00:00
parent f76fca96e2
commit d69e1c9fae
4 changed files with 37 additions and 13 deletions

View file

@ -75,7 +75,8 @@ pub enum Cmd {
/// restart the node with the verifier decided again (src/verifier.rs): the trust setting changed, or the
/// prover found a host that was not there when the node started
RestartNode(String),
Quit,
/// quit, with its source (the log names it: C35, 5 October 2026, two unexplained quits)
Quit(&'static str),
}
pub struct Shared {
@ -463,6 +464,8 @@ pub struct Engine {
last_error_event: Instant,
// the efficiency sweep (src/sweep.rs): one card at a time
sweep: Option<crate::ember::Run>,
/// who asked for the quit (the log's `quit:` line names it)
quit_source: &'static str,
/// the request number the vendor tool last carried out (the run's acknowledgement)
tune_acked: Option<u64>,
/// cards whose confirm check found a better neighbour: the full plan runs next
@ -559,6 +562,7 @@ impl Engine {
last_settings_save: now,
last_error_event: now - Duration::from_secs(600),
sweep: None,
quit_source: "unknown",
tune_acked: None,
tune_full_due: std::collections::HashSet::new(),
sweep_pending: None,
@ -702,7 +706,7 @@ impl Engine {
if self.shared.runtime.sweep_only {
if supported.is_empty() {
self.sweep_say("SWEEP none reason=no_supported_card");
self.shared.send(Cmd::Quit);
self.shared.send(Cmd::Quit("the --sweep run (every card done)"));
} else {
self.sweep_queue = supported;
}
@ -1021,7 +1025,9 @@ impl Engine {
}
}
},
Cmd::Quit => {
Cmd::Quit(source) => {
self.shared.log(&format!("quit requested by {source}"));
self.quit_source = source;
self.quitting = true;
self.st().quitting = true;
}
@ -1472,6 +1478,10 @@ impl Engine {
if self.power_busy {
return;
}
if self.shared.runtime.sweep_only {
self.shared.log(&format!("power cap ({why}): not touched under --sweep; the tune sets every limit itself"));
return;
}
if self.sweep.is_some() || self.sweep_pending.is_some() {
// the sweep owns the caps until it ends; it applies the chosen one itself
self.shared.log(&format!("power cap ({why}): deferred, a sweep is running"));
@ -1856,7 +1866,7 @@ impl Engine {
if let Some((idx, forced)) = pick {
self.sweep_begin(idx, forced);
} else if self.shared.runtime.sweep_only && self.sweep_queue.is_empty() && self.sweep_pending.is_none() {
self.shared.send(Cmd::Quit);
self.shared.send(Cmd::Quit("the --sweep run (every card done)"));
}
}
@ -2304,7 +2314,7 @@ impl Engine {
}
self.upload_logs(false);
if self.shared.runtime.sweep_only && self.sweep_queue.is_empty() {
self.shared.send(Cmd::Quit);
self.shared.send(Cmd::Quit("the --sweep run (every card done)"));
}
}
@ -2356,7 +2366,7 @@ impl Engine {
} else {
self.sweep_retry.insert(idx, Instant::now() + Duration::from_secs(3600));
if self.shared.runtime.sweep_only && self.sweep_queue.is_empty() {
self.shared.send(Cmd::Quit);
self.shared.send(Cmd::Quit("the --sweep run (every card done)"));
}
}
}
@ -3371,7 +3381,7 @@ impl Engine {
}
fn shutdown(&mut self) {
self.shared.log("quit: stopping the miners, then the node");
self.shared.log(&format!("quit: stopping the miners, then the node (source: {})", self.quit_source));
self.jobs.abort(&self.shared, "the app is quitting");
if self.sweep.is_some() || self.sweep_pending.is_some() {
self.sweep_abort("the app is quitting");
@ -3426,7 +3436,11 @@ impl Engine {
/// administrator rights (one UAC prompt on Windows, pkexec on Linux); the engine builds an elevated command only when
/// Power control is on in Settings, or when it is itself the elevated PC sweep job (--sweep).
fn elevation_allowed(power_control: bool, sweep_only: bool) -> bool {
power_control || sweep_only
// C35 (5 October 2026, 22:30 UTC): the unattended --sweep job on PC 1 counted as allowed and raised the one
// administrator prompt nobody was there to answer; an elevated job sets limits directly without asking (the tune
// probe's `direct`), so the flag adds nothing and Power control alone decides
let _ = sweep_only;
power_control
}
/// The notice when the one prompt was refused, cancelled or not answered: Power control goes back off, no retries.
@ -3634,7 +3648,7 @@ mod tests {
// the decision (the project lead, 5 October 2026): off = the app never asks; the elevated PC sweep job is the exception
assert!(!super::elevation_allowed(false, false));
assert!(super::elevation_allowed(true, false));
assert!(super::elevation_allowed(false, true));
assert!(!super::elevation_allowed(false, true), "the --sweep job alone never asks (C35)");
let mut cards = vec![
super::CardState { vendor: "nvidia".into(), enabled: true, device: "0".into(), name: "RTX 5090".into(), power_default_w: 575.0, power_limit_w: 575.0, power_pct: 80, ..Default::default() },
super::CardState { vendor: "amd".into(), enabled: true, device: "1".into(), name: "RX 9070 XT".into(), power_default_w: 300.0, power_limit_w: 300.0, power_pct: 80, ..Default::default() },

View file

@ -133,7 +133,7 @@ fn main() {
let Ok(l) = line else { break };
let t = l.trim();
match t {
"quit" => shared.send(engine::Cmd::Quit),
"quit" => shared.send(engine::Cmd::Quit("the window host (quit on stdin: the tray menu or the installer)")),
"pause" => shared.send(engine::Cmd::Pause),
"resume" => shared.send(engine::Cmd::Resume),
"elevated ok" => shared.send(engine::Cmd::ElevatedDone(Ok(()))),
@ -142,7 +142,7 @@ fn main() {
}
}
if wrapper {
shared.send(engine::Cmd::Quit);
shared.send(engine::Cmd::Quit("the window host went away (stdin closed)"));
}
});
}

View file

@ -339,7 +339,8 @@ fn api_post(shared: &Arc<Shared>, path: &str, body: Value) -> Result<Value, Stri
Ok(json!({ "ok": true }))
}
"/api/quit" => {
shared.send(Cmd::Quit);
// the caller is on 127.0.0.1 and holds the token: the installer, the OTA apply, a script that read app.url
shared.send(Cmd::Quit("POST /api/quit (a local caller with the token: the installer, the OTA apply, or a script that read app.url)"));
Ok(json!({ "ok": true }))
}
_ => Err("unknown api".into()),

View file

@ -15,6 +15,7 @@
# therefore measure only tonight unless the engine finds itself elevated.
$ErrorActionPreference = 'Continue'
$budgetMinutes = 35
if (-not ($budgetMinutes -is [int]) -or $budgetMinutes -lt 5) { $budgetMinutes = 35 } # a budget under 5 minutes is a bug, not a budget (C35)
$started = Get-Date
$deadline = $started.AddMinutes($budgetMinutes)
function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) }
@ -123,8 +124,16 @@ while (-not $p.HasExited) {
}
if ((Get-Date) -gt $deadline) {
Say ("budget of " + $budgetMinutes + " min spent; asking the tune engine to quit")
# C35 (5 October 2026): the only quit this script may send goes to the TUNE engine's own URL file in the scratch
# root, never to a file under the installed app's folder; the RESULT line names the file it used
$u = Join-Path $sApp 'app.url'
if (Test-Path $u) { try { Invoke-WebRequest -Uri ((Get-Content -LiteralPath $u -Raw).Trim() + 'api/quit') -Method POST -Body '{}' -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null } catch { } }
$installedUrl = Join-Path $appDir 'app.url'
if ((Resolve-Path -LiteralPath $u -ErrorAction SilentlyContinue).Path -eq (Resolve-Path -LiteralPath $installedUrl -ErrorAction SilentlyContinue).Path -or $u -like '*\igneum\app\*') {
Write-Output ('RESULT TUNE quit refused: ' + $u + ' is the installed app''s URL file')
} elseif (Test-Path -LiteralPath $u) {
Write-Output ('RESULT TUNE quit asked of the tune engine through ' + $u + ' (pid ' + $p.Id + ')')
try { Invoke-WebRequest -Uri ((Get-Content -LiteralPath $u -Raw).Trim() + 'api/quit') -Method POST -Body '{}' -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null } catch { }
} else { Write-Output ('RESULT TUNE quit not sent: no URL file at ' + $u + '; killing pid ' + $p.Id) }
Start-Sleep -Seconds 20
if (-not $p.HasExited) { $p.Kill() }
Write-Output 'RESULT TUNE error=budget_exceeded'