Counter ASIC 3.0 (hash): the PC 1 queue of 7 October 2026, prepared, nothing published: the class v4 efficiency pass (clock locks, v4 against v3, the card alone, elevated), G1 + the AMD ladder on the sub-version 3 kit, item 6's family run e with its re-kit, Ember Tune per card (5080, 9070 XT), the publish lines

tools/ca3-v4-amend: pc1-v4-efficiency.ps1 (the job id names the card; -lgc grid 2,850 to 1,400 with the four Ember caps on it, so the owed 5090 clock rows fall out; the fingerprint checked on every step; nvidia-smi sampler by pid; -rgc in finally; voltage: no nvidia-smi lever, the V/F curve follows the lock), pc1-v4-sub3-amd-g1.ps1 (tools/ca3-pc1-amd's G1 + ladder shape on fetch-ca3-v4-sub3-pc1-20261007, the 13 packs' Mac fingerprints as the table), pc1-amd-family-20261007.ps1 (run e, the new kit id only), pc1-ember-card.ps1 (the installed app tunes the one card named by the job id), pc1-publish-20261007.sh (the exact lines, the order, the kit sha256s). Kits built on build-1 with mingw from this tree. CI checks run locally: playbook-quit, copied-sources, bash-body green. The dr736 derive job is not in the queue (its packs are not in this tree).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 18:03:59 +00:00
parent 6bb52c5007
commit d5d7dc5662
5 changed files with 720 additions and 0 deletions

View file

@ -0,0 +1,149 @@
# 7 October 2026 copy for the PC 1 queue: tools/ca3-pc1-amd/pc1-amd-family.ps1 (run e) unchanged except the kit id, since a machine
# runs a fetch id once and the 0.3.21 host build wipes the jobs folder: fetch-ca3-pc1-amd-family-20261007 (the probe rebuilt on
# build-1 from this tree's proto-opencl/family-probe.c).
# Counter ASIC 3.0, PC 1 AMD job 2 (6 October 2026, run e: the 9070 XT ALONE): item 6's step cost of every reserve candidate family of spec
# 1.13.2 on PC 1's RX 9070 XT (machine ae432dc7, gfx1201), docs/plans/counter-asic-3-reserve.md, status file section
# 3 "Item 6" (the OWED AMD column). Run a (run-ca3-pc1-amd-family-20261006, 150 s, exit 0) measured ordinal 0, PC 1's
# integrated gfx1036 (one CU: alu 40.59 G steps/s against the 9070 XT's expected 1,000 to 2,000): the device list parse
# ran a second -match after the capturing one, which overwrote $Matches, so every device read idx 0 and an empty name,
# and the probe took a bare ordinal. Run b: the probe picks the card BY NAME (`--device-name gfx1201`, the match on the
# newest AMD platform by driver version, the kit worker's dedup rule; `RESULT device_choice name= index= platform= driver=
# cus=`), prints the device's name, CUs and platform on every RESULT line, and refuses with `RESULT error` when no
# gfx1201 is listed; the older-platform duplicate and the gfx1036 run after it by explicit ordinal, as their own labelled
# columns. Run d (17:17Z, the card by name, every variant built) showed a dependent-chain ratio does NOT survive the
# card's own miner: the alu chain read 226 then 195 G steps/s and the ratios swung from 5.9 to 11.8x to 0.17 to 1.3x
# between runs (the loaded card's scheduler, not the ops), while the Mac and 5090 columns were taken with the card alone.
# Run e: the 9070 XT is switched OFF by the RUNNER (`--cards-off amd:gfx1201` at publish, the app's own card path, put
# back on ANY exit with its own enabled flag, identities and cap: rule of 6 October 2026, a script never posts to
# /api/cards) for its three runs; this script only confirms by the process list that the card's opencl worker is gone;
# the 5090 and the 4070 keep mining (NOT --stop-miners). The gfx1036 and the old-platform columns run
# AFTER the restore, beside the miners, as they are not the card under test. mm8 stays exact=unverified: the gfx12 WMMA
# iu8 16x16x16 fragment layout is in no source at hand, and a guessed CPU layout would turn a wrong guess into exact=no. The probe is the family kit's
# family-probe-cl.exe (proto-opencl/family-probe.c of branch ca3-pc1-amd, cross-compiled with mingw, OpenCL.dll at run
# time; the Mac ran the same source bit-exact on Apple OpenCL), three runs per device, each best of 3 with a fresh seed
# per repetition, device event time, bit-exact against the CPU reference on two whole 32-lane groups. Every family is
# tried through every form AMD's OpenCL C offers; a form that does not compile prints build=failed with the first line
# of the build log and the run goes on (run a's finding: AMD's OpenCL C compiles no amd_perm, no sudot4, no WMMA
# builtin; ds_bpermute does compile). Never quits, pauses, resumes or updates the installed app; never writes
# settings.json. Lines: the probe's own `RESULT FAMILY name= ms= gsteps= ratio= exact= path= variant= ... device= cus=
# platform=` and `RESULT FAMILYBEST ...`, each prefixed `RESULT run=<n> dev=<label>`; a `SUMMARY {json}` line at the end.
# Read back with `node tools/jobs.mjs <job id>`.
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-amd-family'
$kitId = 'fetch-ca3-pc1-amd-family-20261007'
$started = Get-Date
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
function Summary([string] $status, [hashtable] $extra) {
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
}
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
$jobs = Split-Path $env:IGNEUM_JOB_DIR
$kit = Join-Path $jobs $kitId
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
$probe = Join-Path $kit 'bin\family-probe-cl.exe'
$src = Join-Path $kit 'src\family-probe.c'
if (-not (Test-Path $probe)) { "RESULT error probe missing at $probe"; Summary 'failed' @{ error = 'probe missing' }; exit 2 }
"RESULT probe $probe sha256 $((Get-FileHash -Algorithm SHA256 $probe).Hash.ToLower()) bytes $((Get-Item $probe).Length)"
if (Test-Path $src) { "RESULT source family-probe.c sha256 $((Get-FileHash -Algorithm SHA256 $src).Hash.ToLower()) bytes $((Get-Item $src).Length)" }
# what the card carries during the run (read only; nothing is switched)
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
$w = @(Workers)
$amdLoaded = ($w | Where-Object { $_ -match 'igneum-worker-opencl' }).Count -gt 0
$cardState = if ($amdLoaded) { 'loaded' } else { 'quiet' }
"RESULT workers_before $(Stamp) $($w -join ' ')"
"RESULT context card_state=$(if ($amdLoaded) { 'LOADED (an igneum-worker-opencl process mines on an AMD card: absolutes are shared-card figures, ratios stand)' } else { 'quiet (no igneum-worker-opencl process)' })"
# ---- the installed app and the card's settings.json entries (READ ONLY), for the card-alone runs ----
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
$urlFile = Join-Path $appDir 'app.url'
$base = $null
if (Test-Path $urlFile) { $base = (Get-Content -LiteralPath $urlFile -Raw).Trim().TrimEnd('/') }
"RESULT app install=$inst app_dir=$appDir url_present=$([bool]$base)"
# the 9070 XT's OpenCL device index as the APP numbers it (the installed worker's --list; the probe's own list below
# numbers devices differently), for the process-list check of its worker
$dev = $null
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) {
$wl = @(& (Join-Path $inst 'igneum-worker-opencl.exe') --list 2>&1 | ForEach-Object { "$_" })
foreach ($l in $wl) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
}
"RESULT app_device $(if ($null -ne $dev) { $dev } else { 'unknown (no installed worker list: the quiet check falls back to any opencl worker)' })"
# the card is switched off by the RUNNER (`--cards-off amd:gfx1201` at publish), never by this script (rule of 6 October
# 2026, tools/ci/playbook-quit-check.sh rule 2); below only the read-only confirmation that its worker is gone
function CardWorkers {
$procs = @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue)
if ($null -ne $dev) { $procs = @($procs | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" }) }
@($procs | ForEach-Object { $_.ProcessId })
}
# the probe's own device list: the 9070 XT by name first (the probe picks the newest platform itself), then the
# older-platform duplicate and the gfx1036 by explicit ordinal as their own columns
$list = @(& $probe --list 2>&1 | ForEach-Object { "$_" })
$list | ForEach-Object { "RESULT list $_" }
$extra = @()
$gfx1201Count = 0; $newest = ''
foreach ($l in $list) {
if ($l -match '^\[(\d+)\] (.*?) \| (.*?) \| driver (\S+)') {
$idx = [int] $Matches[1]; $name = $Matches[2]; $plat = $Matches[3]; $drv = $Matches[4]
if ($name -match 'gfx1201') { $gfx1201Count++; if ($drv -gt $newest) { $newest = $drv } }
if ($name -match 'gfx1201|gfx1036|Radeon' -and $name -notmatch 'NVIDIA') { $extra += [pscustomobject]@{ idx = $idx; name = $name; platform = $plat; driver = $drv } }
}
}
if ($gfx1201Count -eq 0) { "RESULT error no gfx1201 in the probe's list (the eGPU is off the bus: the 9070 XT column stays OWED); listed AMD devices: $(($extra | ForEach-Object { "[$($_.idx)] $($_.name) on $($_.platform) driver $($_.driver)" }) -join ' ; ')"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
# the by-ordinal runs after the by-name one: the gfx1201 entries NOT on the newest driver (the older platform), then the rest
$byOrdinal = @($extra | Where-Object { -not ($_.name -match 'gfx1201' -and $_.driver -eq $newest) })
"RESULT devices by_name=gfx1201 (newest driver $newest, $gfx1201Count gfx1201 entries) then by_ordinal=[$(($byOrdinal | ForEach-Object { "[$($_.idx)] $($_.name) on $($_.platform) driver $($_.driver)" }) -join ' ; ')]"
$runs = 0; $exactRows = 0; $rowsTotal = 0; $built = 0; $failedBuilds = 0; $probeErrors = 0; $mainRows = 0
function RunProbe([string] $label, [string[]] $probeArgs, [int] $r) {
# never name a parameter after the automatic variable for unbound arguments: a splat of it is then empty (run b, 17:10Z)
"RESULT run=$r dev=$label start $(Stamp) args=[$($probeArgs -join ' ')] card_state=$cardState"
$t0 = Get-Date
$lines = @(& $probe @probeArgs --reps 3 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
foreach ($l in $lines) {
if ($l -match '^RESULT ') { "RESULT run=$r dev=$label $($l.Substring(7))" } else { "RESULT run=$r dev=$label text $l" }
if ($l -match '^RESULT FAMILY name=\S+ ms=') { $script:rowsTotal++; if ($l -match ' exact=yes ') { $script:exactRows++ }; if ($label -eq '9070xt') { $script:mainRows++ } }
if ($l -match '^RESULT FAMILY .*build=failed') { $script:failedBuilds++ }
if ($l -match '^RESULT FAMILYBEST name=\S+ ms=') { $script:built++ }
if ($l -match '^RESULT error') { $script:probeErrors++ }
}
"RESULT run=$r dev=$label end $(Stamp) exit=$rc wall_s=$([int]((Get-Date) - $t0).TotalSeconds)"
if ($rc -ne 0) { "RESULT run=$r dev=$label error=probe exit $rc" }
$script:runs++
}
# ---- the 9070 XT alone: off in the app, confirmed quiet, three runs, restored whatever happens ----
$cardAlone = $false
try {
$before = @(CardWorkers)
"RESULT card_workers_before pids=[$($before -join ' ')]"
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker for the card (the runner's --cards-off took)"; $cardAlone = $true }
else {
$t = 0
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
$left = @(CardWorkers)
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: the card's igneum-worker-opencl process is gone (the card to itself)"; $cardAlone = $true }
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still run (was the job published with --cards-off amd:gfx1201?); the 9070 XT rows below are LOADED-card figures (run d's class)" }
Start-Sleep -Seconds 8
}
$cardState = if ($cardAlone) { 'alone' } else { 'loaded' }
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
for ($r = 1; $r -le 3; $r++) { RunProbe '9070xt' @('--device-name', 'gfx1201') $r; Start-Sleep -Seconds 2 }
} finally {
"RESULT runs_9070xt_done $(Stamp) (the card comes back through the RUNNER's restore on any exit, not here)"
}
# ---- the other AMD columns beside the miners (not the card under test) ----
$cardState = if ((@(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue)).Count -gt 0) { 'loaded' } else { 'quiet' }
foreach ($d in $byOrdinal) {
$label = if ($d.name -match 'gfx1201') { 'gfx1201-old-platform' } elseif ($d.name -match 'gfx1036') { 'gfx1036' } else { 'amd-' + $d.idx }
for ($r = 1; $r -le 3; $r++) { RunProbe $label @('--device', "$($d.idx)") $r; Start-Sleep -Seconds 2 }
}
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
"RESULT end $(Stamp)"
$status = if ($mainRows -gt 0) { 'done' } elseif ($rowsTotal -gt 0) { 'partial' } else { 'failed' }
Summary $status @{ runs = $runs; family_rows = $rowsTotal; rows_9070xt = $mainRows; card_alone_9070xt = $cardAlone; exact_rows = $exactRows; failed_builds = $failedBuilds; familybest_rows = $built; probe_errors = $probeErrors }
if ($status -eq 'failed') { exit 1 }
exit 0

View file

@ -0,0 +1,74 @@
# Counter ASIC 3.0, PC 1 queue (7 October 2026): Ember Tune on ONE card of the installed app, by the job id (an id carrying
# "9070" tunes the RX 9070 XT, "5080" the RTX 5080, otherwise the RTX 5090), the other cards untouched and mining on. The shape
# is relay/playbooks/ember-installed-tune-pc1.ps1 (6 October 2026): the SHIPPED app tunes itself through the registered Power
# Helper task, this script only asks (POST api/tune/goal climb on, POST api/sweep/start for the one card) and watches the TUNE
# rows in the app's own log and api/state, then puts the climb switch back. On 0.3.21 (ember.rs may_set) a step is SET on an
# AMD card only when igneum-gpu-telemetry beside the app gives the card a tune line (ADLX) and Power control is on, else the
# tune is measure-only and the TUNE rows carry the reason; this script prints the card's tune_line before and after so the
# "measure only" reading and its reason are on RESULT lines. Never quits, pauses or resumes the installed app, never posts to
# /api/cards, never writes settings.json. Not elevated (the task carries the rights).
$ErrorActionPreference = 'Continue'
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
$u = Join-Path $appDir 'app.url'
if (-not (Test-Path -LiteralPath $u)) { Write-Output 'RESULT TUNE error=no_app_url (installed app not running)'; exit 1 }
$base = (Get-Content -LiteralPath $u -Raw).Trim()
function State() { try { return ((Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json) } catch { return $null } }
function Post([string] $path, [string] $json) { try { Invoke-WebRequest -Uri ($base + $path) -Method POST -Body $json -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null; return $true } catch { Say ($path + ': ' + $_.Exception.Message); return $false } }
$s = State
if (-not $s) { Write-Output 'RESULT TUNE error=state_unreadable'; exit 1 }
$ver = [string]$s.version
Write-Output ('RESULT TUNE installed app ' + $ver + ' power_control=' + $s.settings.power_control + ' tuning_off=' + $s.settings.tuning_off + ' climb_before=' + $s.settings.tune_climb + ' goal_before=' + $s.settings.tune_goal)
$t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
Write-Output ('RESULT TUNE task ' + $(if ($t) { $t.Actions[0].Execute + ' state=' + $t.State } else { 'NOT REGISTERED' }))
if (-not $t) { Write-Output 'RESULT TUNE error=task_not_registered (the NVIDIA climbs would need a prompt; stopping)'; exit 1 }
if (-not $s.settings.power_control) { Write-Output 'RESULT TUNE error=power_control_off'; exit 1 }
$climbBefore = [bool]$s.settings.tune_climb
$jobDir = $env:IGNEUM_JOB_DIR; $jobId = if ($jobDir) { Split-Path -Leaf $jobDir } else { '' }
$want = if ($jobId -match '9070') { '9070' } elseif ($jobId -match '5080') { '5080' } else { '5090' }
Write-Output ('RESULT TUNE job_id=' + $jobId + ' card_match=' + $want)
$cards = @($s.mining.cards | Where-Object { $_.enabled -and ($_.vendor -eq 'nvidia' -or $_.vendor -eq 'amd') -and $_.name -notmatch 'Radeon\(TM\) Graphics' -and $_.name -match $want })
if ($cards.Count -eq 0) { Write-Output ('RESULT TUNE error=no enabled card matching ' + $want + ' in api/state (names: ' + (($s.mining.cards | ForEach-Object { $_.name }) -join ', ') + ')'); exit 1 }
Write-Output ('RESULT TUNE cards ' + (($cards | ForEach-Object { $_.key + ' (' + $_.name + ', ' + $_.vendor + ', sweep_state ' + $_.sweep_state + ', line "' + $_.tune_line + '")' }) -join ' | '))
foreach ($c in $cards) { Write-Output ('RESULT TUNE before key=' + $c.key + ' power_w=' + $c.power_w + ' power_pct=' + $c.power_pct + ' clock_cap=' + $c.clock_cap_mhz + ' mhs=' + $c.mhs + ' line=' + $c.tune_line) }
# the climb on (NVIDIA cards; an AMD card has no clock knob and runs its ladder regardless)
if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Output 'RESULT TUNE error=tune_goal_refused'; exit 1 }
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'
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
foreach ($c in $order) {
if ((Get-Date) -gt $deadline) { Say 'budget spent before ' + $c.key; break }
$m = LogMark
$body = '{"key":"' + ($c.key -replace '"', '\"') + '"}'
if (-not (Post 'api/sweep/start' $body)) { Write-Output ('RESULT TUNE error=sweep_start_refused key=' + $c.key); continue }
Say ('tune asked for ' + $c.key)
$seen = 0; $done = $false; $started = Get-Date
while (-not $done -and (Get-Date) -lt $deadline) {
Start-Sleep -Seconds 10
$new = @(LogSince $m)
while ($seen -lt $new.Count) { $l = [string]$new[$seen]; $seen++; if ($l -match 'TUNE (card=|chosen|start|aborted|progress)') { if ($l -notmatch 'TUNE progress') { Write-Output ('RESULT ' + ($l -replace '^.*?(TUNE )', 'TUNE ')) }; if ($l -match 'TUNE card=') { $rows++ } } }
$st = State
if ($st) {
$cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1
if ($cc) {
$running = ($cc.sweep_state -eq 'running')
if (-not $running -and ((Get-Date) - $started).TotalSeconds -gt 180) { $done = $true }
if ($running -and $cc.tune_steps -gt 0 -and ($seen % 6 -eq 0)) { Say ($c.key + ': step ' + $cc.tune_step + ' of ' + $cc.tune_steps + ', ' + $cc.sweep_note) }
}
}
}
$st = State
if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1; if ($cc) { Write-Output ('RESULT TUNE after key=' + $c.key + ' power_w=' + $cc.power_w + ' power_pct=' + $cc.power_pct + ' clock_cap=' + $cc.clock_cap_mhz + ' line=' + $cc.tune_line + ' source=' + $cc.tune_source + ' note=' + $cc.sweep_note); if ($cc.tune_curve) { Write-Output ('RESULT TUNE curve key=' + $c.key + ' ' + (($cc.tune_curve | ForEach-Object { ($_.clock_mhz) + 'MHz/' + ($_.mem_mhz) + 'mem/' + ($_.power_pct) + '%:' + $_.mhs + 'MH/s ' + $_.watts + 'W ' + $_.eff + ' ' + $_.mark }) -join ' | ')) } } }
}
# the climb switch back to what it was
Post 'api/tune/goal' ('{"climb":' + $(if ($climbBefore) { 'true' } else { 'false' }) + '}') | Out-Null
Write-Output ('RESULT TUNE climb_restored=' + $climbBefore + ' rows=' + $rows)
$smi = Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'
if (-not (Test-Path $smi)) { $smi = Join-Path $env:SystemRoot 'System32\nvidia-smi.exe' }
if (Test-Path $smi) { Write-Output ('RESULT TUNE nvidia_now ' + ((& $smi --query-gpu=index,name,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader 2>&1 | Out-String) -replace "`r?`n", ' | ')) }
if ($rows -eq 0) { Write-Output 'RESULT TUNE error=no_rows'; exit 1 }
exit 0

View file

@ -0,0 +1,33 @@
#!/usr/bin/env bash
# The PC 1 queue of 7 October 2026 (Counter ASIC 3.0, the hash lane; main's orders through the coordinator): the exact publish
# lines, one job at a time under the PC 1 rule (each job's closing report read before the next; nothing published before the
# coordinator relays the shipper's "PC 1 released" line). PC 1 = machine ae432dc7 (RTX 5090, RTX 5080 on the dock, RTX 4070,
# RX 9070 XT amd:gfx1201). Every measured card alone through the RUNNER's --cards-off (the app's own card path, restored on any
# exit); the other cards mining on; the installed app never quit, paused, resumed or updated; the desk usable.
# tools/ca3-v4-amend/pc1-publish-20261007.sh <step> [--deploy] steps: kit family-kit eff-5090 eff-5080 amd-g1 family tune-5080 tune-9070
# tools/ca3-v4-amend/pc1-publish-20261007.sh read <job id> the closing report (node tools/jobs.mjs <id> --all)
# Kits (built 7 October 2026, 17:5x UTC; the exes cross-compiled on build-1 with mingw from this tree):
# igneum-ca3-v4-sub3-pc1-kit.zip sha256 1d441f5a075d72b1b902d23941e57b0fa4bee08f91a14b10a7a545aaac58c413 (896,071 bytes, 167 files)
# igneum-ca3-pc1-amd-family-kit-20261007.zip sha256 9dd82b11ed08ff9270ac7d59f634eaf99267b1bb48a9ae9fdfc0ce7fe41e63ef (39,399 bytes, 3 files)
# Order (main, 7 October 2026 evening): eff-5090, eff-5080 (the class v4 efficiency pass; its clock grid carries the four Ember
# caps, so the owed "5090 clock rows" fall out of it), amd-g1 (G1 + the AMD ladder on the sub-version 3 kit), family (item 6
# on AMD), tune-5080 (the full Ember Tune on the 5080), tune-9070 (the 9070 XT tune pass). The dr736 derive job is NOT in this
# queue: its packs are not in this tree (the 6 October kit carried them from branch ca3-pc1-amd).
set -euo pipefail
ROOT=$(cd "$(dirname "$0")/../.." && pwd); cd "$ROOT"
KITS="${IGNEUM_PC1_KITS:-${TMPDIR:-/tmp}}"
P=packaging/ota/publish-jobs.sh
STEP="${1:-}"; shift || true
DEPLOY=""; for a in "$@"; do [ "$a" = "--deploy" ] && DEPLOY="--deploy"; done
case "$STEP" in
kit) $P add --kind fetch --target ae432dc7 --id fetch-ca3-v4-sub3-pc1-20261007 --file "$KITS/igneum-ca3-v4-sub3-pc1-kit.zip" --dir jobs --extract --title "CA3 PC 1: the sub-version 3 kit (worker, 13 packs)" --expires-hours 36 $DEPLOY ;;
family-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca3-pc1-amd-family-20261007 --file "$KITS/igneum-ca3-pc1-amd-family-kit-20261007.zip" --dir jobs --extract --title "CA3 PC 1: the family probe kit (7 Oct)" --expires-hours 36 $DEPLOY ;;
eff-5090) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-eff-5090-20261007 --elevated --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 75 --title "CA3 PC 1: class v4 efficiency pass on the RTX 5090 (clock locks, v4 against v3, the card alone)" --expires-hours 36 $DEPLOY ;;
eff-5080) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-eff-5080-20261007 --elevated --cards-off "nvidia:NVIDIA GeForce RTX 5080" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 75 --title "CA3 PC 1: class v4 efficiency pass on the RTX 5080 (clock locks, v4 against v3, the card alone)" --expires-hours 36 $DEPLOY ;;
amd-g1) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-sub3-amd-g1-20261007 --cards-off amd:gfx1201 --script tools/ca3-v4-amend/pc1-v4-sub3-amd-g1.ps1 --timeout-minutes 45 --title "CA3 PC 1: G1 on the sub-version 3 kit and the AMD ladder on the 9070 XT (the card alone)" --expires-hours 36 $DEPLOY ;;
family) $P add --kind run --target ae432dc7 --id run-ca3-pc1-amd-family-20261007-e --cards-off amd:gfx1201 --script tools/ca3-v4-amend/pc1-amd-family-20261007.ps1 --timeout-minutes 20 --title "CA3 PC 1: item 6 family step costs on the 9070 XT (run e, the card alone)" --expires-hours 36 $DEPLOY ;;
tune-5080) $P add --kind run --target ae432dc7 --id run-ca3-pc1-ember-5080-20261007 --script tools/ca3-v4-amend/pc1-ember-card.ps1 --timeout-minutes 35 --title "CA3 PC 1: Ember Tune on the RTX 5080 (the installed app tunes the one card)" --expires-hours 36 $DEPLOY ;;
tune-9070) $P add --kind run --target ae432dc7 --id run-ca3-pc1-ember-9070-20261007 --script tools/ca3-v4-amend/pc1-ember-card.ps1 --timeout-minutes 35 --title "CA3 PC 1: Ember Tune pass on the RX 9070 XT (set or measure-only by the probe)" --expires-hours 36 $DEPLOY ;;
read) node tools/jobs.mjs "$1" --all ;;
*) sed -n 2,12p "$0"; exit 2 ;;
esac

View file

@ -0,0 +1,204 @@
# Counter ASIC 3.0, PC 1: the class v4 efficiency pass (7 October 2026, main's question "profitable mining is all about
# efficiency, is there a solution?"). Thesis under test: the latency-shadow work hides in the memory wait, so the core clock
# (and with it the voltage along the driver's V/F curve) can drop until the work just fits the wait with no rate loss, and the
# 5090's +80 W for class v4 at the unlocked 2,850 MHz mostly comes back. One job per card (the job id names the card: an id
# carrying "5080" measures the RTX 5080, any other the RTX 5090), the card ALONE under the RUNNER's --cards-off (the app's own
# card path, restored on any exit; the other cards keep mining), ELEVATED so nvidia-smi -lgc needs no prompt, no restart, the
# desk usable. Per step: nvidia-smi -lgc <c>,<c> on the card, then the INSTALLED igneum-worker-cuda.exe --bench on the class v4
# pack (v4-devnet-epoch0, the sub-version 3 kit) and on the class v3 control (mx8-devnet-epoch0), about 60 s each, the 2^24
# fingerprint checked against the Mac's on every step (bit-exactness under the lock), while nvidia-smi samples power.draw,
# clocks.sm, clocks.mem and the throttle reasons at 1 Hz (a child by pid, ended in finally). Steps: unlocked, then 2,850,
# 2,781, 2,700, 2,550, 2,472, 2,400, 2,250, 2,163, 2,100, 1,950, 1,854, 1,800, 1,650, 1,500, 1,400 MHz (the four Ember caps
# 2,781 / 2,472 / 2,163 / 1,854 are on the grid, so the owed "5090 clock rows" of counter-asic-3-status.md section 7 fall out of
# this pass), then unlocked again as the drift check. The voltage lever: nvidia-smi exposes NO voltage offset (that is NVAPI,
# Afterburner's lever); the clock lock moves the card down the driver's V/F curve, so the voltage falls with the lock and that
# is the lever this pass has; stated on a RESULT line. Lines: `RESULT STEP card= pack= lock= sm_mhz= watts= mhs= mh_per_w=
# fingerprint= match=`, one per step and pack; `RESULT TABLE` lines at the end (v3 best against v4 best, the recovered watts,
# the rate lost); a `SUMMARY {json}` line. Never quits, pauses, resumes or updates the installed app; never posts to
# /api/cards; never writes settings.json; -rgc in finally. Not elevated: the lock is refused, the unlocked rows are
# measured and every lock row says why. Kit: fetch job fetch-ca3-v4-sub3-pc1-20261007 (tools/ca3-v4-amend/pc1-publish-20261007.sh).
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-v4-efficiency'
$kitId = 'fetch-ca3-v4-sub3-pc1-20261007'
$started = Get-Date
$deadline = $started.AddMinutes(66)
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
function Summary([string] $status, [hashtable] $extra) {
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
}
$jobDir = $env:IGNEUM_JOB_DIR; if (-not $jobDir) { $jobDir = Join-Path $env:TEMP 'igneum-ca3-pc1-eff' }
New-Item -ItemType Directory -Force -Path $jobDir | Out-Null
$jobId = Split-Path -Leaf $jobDir
$cardName = if ($jobId -match '5080') { 'NVIDIA GeForce RTX 5080' } else { 'NVIDIA GeForce RTX 5090' }
$expected = @{ 'mx8-devnet-epoch0' = '90f794dd556f7a3b'; 'v4-devnet-epoch0' = 'e370fb2080b7dbb1' }
$packOrder = @('v4-devnet-epoch0', 'mx8-devnet-epoch0')
$locks = @(2850, 2781, 2700, 2550, 2472, 2400, 2250, 2163, 2100, 1950, 1854, 1800, 1650, 1500, 1400)
$stepSeconds = 60
"RESULT start $(Stamp) job=$jobName job_id=$jobId card=$cardName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
'RESULT lever voltage: nvidia-smi exposes no voltage offset (NVAPI only); the clock lock (-lgc) moves the card along the driver V/F curve, so the voltage falls with the lock; the power cap (-pl) is the other driver lever and is not moved here'
$elevated = $false
try { $elevated = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch { }
"RESULT elevated=$elevated"
# ---- the kit (the wiped-jobs-folder rule: tested before use) ----
$jobs = Split-Path $jobDir
$kit = Join-Path $jobs $kitId
$packs = Join-Path $kit 'packs'
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
foreach ($pk in $packOrder) { $d = Join-Path $packs $pk; if (-not (Test-Path $d)) { "RESULT error pack missing at $d"; Summary 'failed' @{ error = "pack $pk missing" }; exit 2 }; foreach ($f in (Get-ChildItem $d -File | Sort-Object Name)) { "RESULT kitfile $pk/$($f.Name) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower())" } }
# ---- the installed worker (NVRTC, the pack's own kernel text) ----
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1
if (-not $inst) { 'RESULT error no installed igneum-worker-cuda.exe'; Summary 'failed' @{ error = 'no worker' }; exit 2 }
$exe = Join-Path $inst 'igneum-worker-cuda.exe'
"RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
$appExe = Join-Path $inst 'igneum-app.exe'
if (Test-Path $appExe) { try { "RESULT app-version $((& $appExe --version 2>&1 | Out-String).Trim())" } catch { } }
# ---- nvidia-smi and the card ----
$smi = $null
$c = Get-Command nvidia-smi -ErrorAction SilentlyContinue; if ($c) { $smi = $c.Source }
if (-not $smi) { foreach ($p in @((Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'), (Join-Path $env:SystemRoot 'System32\nvidia-smi.exe'))) { if (Test-Path $p) { $smi = $p; break } } }
if (-not $smi) { 'RESULT error nvidia-smi not found'; Summary 'failed' @{ error = 'no nvidia-smi' }; exit 2 }
$gpus = @(& $smi --query-gpu=index,name,uuid,pci.bus_id,power.limit,power.max_limit,clocks.max.sm,driver_version --format=csv,noheader 2>&1 | ForEach-Object { "$_" })
$gpus | ForEach-Object { "RESULT gpu $_" }
$row = $gpus | Where-Object { $_ -match [regex]::Escape($cardName) } | Select-Object -First 1
if (-not $row) { "RESULT error card '$cardName' is not in nvidia-smi's list"; Summary 'failed' @{ error = 'card missing' }; exit 2 }
$fld = $row -split ',\s*'
$idx = $fld[0].Trim(); $uuid = $fld[2].Trim(); $bus = $fld[3].Trim(); $maxSm = $fld[6].Trim()
"RESULT card index=$idx uuid=$uuid bus=$bus max_sm_mhz=$maxSm"
# ---- the card quiet (the RUNNER's --cards-off took): no igneum compute app on this bus id ----
function BusApps { @(& $smi --query-compute-apps=gpu_bus_id,pid,process_name --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match [regex]::Escape($bus) -and $_ -match 'igneum' }) }
$t = 0; $apps = @(BusApps)
while ($t -lt 150 -and $apps.Count -gt 0) { Start-Sleep -Seconds 5; $t += 5; $apps = @(BusApps) }
$alone = ($apps.Count -eq 0)
if ($alone) { "RESULT card $(Stamp) quiet after $t s: no igneum compute app on $bus (the runner's --cards-off took)" }
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: $($apps -join '; ') (was the job published with --cards-off?); the rows below are LOADED-card figures" }
# ---- the sampler: nvidia-smi at 1 Hz on this card, a child by pid, its file read per step ----
$samples = Join-Path $jobDir 'nv-samples.csv'
if (Test-Path $samples) { Remove-Item -LiteralPath $samples -Force }
$sampler = $null
$rows = @()
$lockState = 'unlocked'
function ReadWindow([datetime] $t0, [datetime] $t1) {
$sel = @()
if (Test-Path $samples) {
foreach ($l in (Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue)) {
$p = $l -split ',\s*'
if ($p.Count -lt 5) { continue }
$ts = $null
try { $ts = [datetime]::ParseExact($p[0].Trim(), 'yyyy/MM/dd HH:mm:ss.fff', [cultureinfo]::InvariantCulture) } catch { continue }
if ($ts -ge $t0 -and $ts -le $t1) { $sel += ,$p }
}
}
return ,$sel
}
function Mean($vals) { $v = @($vals | ForEach-Object { [double]$_ }); if ($v.Count -eq 0) { return $null }; return [math]::Round((($v | Measure-Object -Sum).Sum / $v.Count), 1) }
function SetLock([int] $mhz) {
$out = (& $smi -i $idx -lgc "$mhz,$mhz" 2>&1 | Out-String).Trim() -replace '\s+', ' '
if ($out -match 'not have permission|No permission|Insufficient|not supported|Unable|ERROR') { return "refused: $out" }
return "ok: $out"
}
function Bench([string] $pk, [int] $n) {
$d = Join-Path $packs $pk
$env:CUDA_VISIBLE_DEVICES = $uuid
$t0 = Get-Date
$lines = @(& $exe --bench --pack $d --batches $n --batch-log2 24 --block-warps 1 --device 0 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
$t1 = Get-Date
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
$fp = ''; $mhs = $null; $check = ''; $dev = ''
if ($res -and $res -match 'device=(.*?) arch=') { $dev = $Matches[1] }
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = [double]$Matches[1] }
if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
$err = ''
if (-not $res -or $rc -ne 0) { $e = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); $err = if ($e) { ($e -replace '\s+', ' ') } else { "exit $rc, no RESULT line" } }
return [pscustomobject]@{ pack = $pk; t0 = $t0; t1 = $t1; rc = $rc; fp = $fp; mhs = $mhs; check = $check; err = $err; res = $res; batches = $n; dev = $dev }
}
function Step([string] $label, [string] $lockNote) {
foreach ($pk in $packOrder) {
if ((Get-Date) -gt $deadline) { "RESULT STEP card=$cardName pack=$pk lock=$label error=budget spent"; continue }
$n = $nb[$pk]
$b = Bench $pk $n
if ($b.err) { "RESULT STEP card=$cardName pack=$pk lock=$label error=$($b.err) lock_note=$lockNote"; continue }
$w = ReadWindow $b.t0.AddSeconds(8) $b.t1
$watts = Mean ($w | ForEach-Object { $_[1] })
$sm = Mean ($w | ForEach-Object { $_[2] })
$mem = Mean ($w | ForEach-Object { $_[3] })
$tmax = ($w | ForEach-Object { [int]$_[4] } | Measure-Object -Maximum).Maximum
$thr = ($w | ForEach-Object { $_[5].Trim() } | Group-Object | Sort-Object Count -Descending | Select-Object -First 1).Name
$eff = if ($watts -and $watts -gt 0) { [math]::Round($b.mhs / $watts, 4) } else { $null }
$want = $expected[$pk]
$match = if ($want -and $b.fp -eq $want) { 'yes' } elseif ($want) { 'no' } else { 'unknown' }
$wall = [int]($b.t1 - $b.t0).TotalSeconds
"RESULT STEP card=$cardName pack=$pk lock=$label sm_mhz=$sm mem_mhz=$mem watts=$watts temp_max=$tmax throttle=$thr mhs=$($b.mhs) mh_per_w=$eff fingerprint=$($b.fp) match=$match expected=$want check=$($b.check) batches=$n wall_s=$wall samples=$($w.Count) device=$($b.dev) lock_note=$lockNote alone=$alone"
$script:rows += [pscustomobject]@{ pack = $pk; lock = $label; sm = $sm; watts = $watts; mhs = $b.mhs; eff = $eff; match = $match }
}
}
try {
$sampler = Start-Process -FilePath $smi -ArgumentList @('-i', $idx, '--query-gpu=timestamp,power.draw,clocks.sm,clocks.mem,temperature.gpu,clocks_throttle_reasons.active', '--format=csv,noheader,nounits', '-lms', '1000') -RedirectStandardOutput $samples -WindowStyle Hidden -PassThru
Start-Sleep -Seconds 6
$first = @(Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue | Select-Object -First 2)
"RESULT sampler pid=$($sampler.Id) first=[$($first -join ' | ')]"
if ($first.Count -eq 0) { 'RESULT sampler error=no samples in 6 s; watts will read empty on every row' }
# the unlocked start: -rgc so the baseline is the driver's own choice, then the batch counts from a short probe per pack
$r0 = (& $smi -i $idx -rgc 2>&1 | Out-String).Trim() -replace '\s+', ' '
"RESULT unlock start $r0"
$nb = @{}
foreach ($pk in $packOrder) {
$p = Bench $pk 5
if ($p.err) { "RESULT probe pack=$pk error=$($p.err)"; $nb[$pk] = 40 } else {
$n = [int][math]::Ceiling($stepSeconds * $p.mhs * 1e6 / 16777216.0)
$nb[$pk] = [math]::Max(20, [math]::Min(2000, $n))
"RESULT probe pack=$pk mhs=$($p.mhs) fingerprint=$($p.fp) batches_for_${stepSeconds}s=$($nb[$pk])"
}
}
Step 'unlocked' 'driver default (-rgc)'
if (-not $elevated) {
'RESULT locks skipped: the job is not elevated, nvidia-smi -lgc would be refused (the 6 October reading); the unlocked rows above are the measurement'
foreach ($c in $locks) { foreach ($pk in $packOrder) { "RESULT STEP card=$cardName pack=$pk lock=$c error=not elevated, lock not attempted" } }
} else {
foreach ($c in $locks) {
if ((Get-Date) -gt $deadline) { "RESULT STEP card=$cardName pack=* lock=$c error=budget spent"; continue }
$note = SetLock $c
$lockState = "locked $c"
if ($note -match '^refused') { foreach ($pk in $packOrder) { "RESULT STEP card=$cardName pack=$pk lock=$c error=lock $note" }; if ($c -eq $locks[0]) { 'RESULT locks refused on the first step: the remaining locks are skipped'; break } else { continue } }
Start-Sleep -Seconds 3
Step "$c" $note
}
$r1 = (& $smi -i $idx -rgc 2>&1 | Out-String).Trim() -replace '\s+', ' '
$lockState = 'unlocked'
"RESULT unlock end $r1"
Start-Sleep -Seconds 3
Step 'unlocked-end' 'driver default (-rgc), the drift check'
}
# ---- the table ----
$tab = @{}
foreach ($pk in $packOrder) {
$mine = @($rows | Where-Object { $_.pack -eq $pk -and $_.mhs })
$un = $mine | Where-Object { $_.lock -eq 'unlocked' } | Select-Object -First 1
$withW = @($mine | Where-Object { $_.eff })
$best = $withW | Sort-Object eff -Descending | Select-Object -First 1
$tab[$pk] = @{ un = $un; best = $best }
if ($un -and $best) {
$rec = [math]::Round($un.watts - $best.watts, 1)
$lost = if ($un.mhs -gt 0) { [math]::Round(100.0 * ($un.mhs - $best.mhs) / $un.mhs, 2) } else { $null }
"RESULT TABLE card=$cardName pack=$pk unlocked_sm_mhz=$($un.sm) unlocked_mhs=$($un.mhs) unlocked_w=$($un.watts) unlocked_mh_per_w=$($un.eff) best_lock=$($best.lock) best_sm_mhz=$($best.sm) best_mhs=$($best.mhs) best_w=$($best.watts) best_mh_per_w=$($best.eff) recovered_w=$rec rate_lost_pct=$lost fingerprints_all_match=$((@($mine | Where-Object { $_.match -ne 'yes' })).Count -eq 0)"
} else { "RESULT TABLE card=$cardName pack=$pk error=no unlocked row or no row with watts" }
}
$u4 = $tab['v4-devnet-epoch0'].un; $u3 = $tab['mx8-devnet-epoch0'].un; $b4 = $tab['v4-devnet-epoch0'].best; $b3 = $tab['mx8-devnet-epoch0'].best
if ($u4 -and $u3 -and $b4 -and $b3) {
"RESULT TABLE card=$cardName v3_vs_v4 v4_extra_w_unlocked=$([math]::Round($u4.watts - $u3.watts, 1)) v4_extra_w_at_best=$([math]::Round($b4.watts - $b3.watts, 1)) v4_rate_vs_v3_unlocked_pct=$([math]::Round(100.0 * ($u4.mhs - $u3.mhs) / $u3.mhs, 2)) v4_rate_vs_v3_at_best_pct=$([math]::Round(100.0 * ($b4.mhs - $b3.mhs) / $b3.mhs, 2)) v4_best_lock=$($b4.lock) v3_best_lock=$($b3.lock)"
}
Summary 'done' @{ card = $cardName; elevated = $elevated; alone = $alone; steps = $rows.Count; mismatches = (@($rows | Where-Object { $_.match -eq 'no' })).Count }
exit 0
} finally {
try { $r = (& $smi -i $idx -rgc 2>&1 | Out-String).Trim() -replace '\s+', ' '; "RESULT restore clocks reset: $r (was $lockState)" } catch { "RESULT restore clocks reset failed: $_" }
if ($sampler -and -not $sampler.HasExited) { try { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue; "RESULT restore sampler pid $($sampler.Id) stopped" } catch { } }
"RESULT gpus_after $(Stamp) $((& $smi --query-gpu=index,name,power.draw,clocks.sm,clocks.applications.graphics --format=csv,noheader 2>&1 | Out-String).Trim() -replace "`r?`n", ' | ')"
}

View file

@ -0,0 +1,260 @@
# Counter ASIC 3.0, PC 1 queue job (7 October 2026): gate G1 on the sub-version 3 kit and item 8's AMD ladder rows on PC 1's
# RX 9070 XT (machine ae432dc7, gfx1201 on the eGPU). The shape is tools/ca3-pc1-amd/pc1-amd-g1-shadow.ps1 (6 October 2026)
# with the kit of 7 October: fetch job fetch-ca3-v4-sub3-pc1-20261007 (bin/igneum-worker-opencl.exe from this tree's
# proto-opencl/host.c, cross-compiled on build-1 with mingw as proto-cuda/nvrtc/build-windows.sh builds the shipped worker;
# packs/: the class v3 control mx8-devnet-epoch0, the seven sub-version 3 class v4 packs v4-devnet-epoch0, v4-era-0 .. v4-era-5
# (program id a785001687d8688a), and the ladder packs sh256x13, sh256x27, sh256x53, sh256x88, sh64x52 re-exported under the
# amended stream; src/; SHA256SUMS). Published with the RUNNER's --cards-off amd:gfx1201 (the index-free key form; the runner
# matches the live card with or without the device index and restores it on ANY exit); the 5090, 5080 and 4070 keep mining.
# The script never touches /api/cards, never quits, pauses, resumes or updates the installed app, never writes settings.json;
# it confirms the card quiet by the process list, then runs the kit worker --bench-pack on every pack while the AMD helper
# samples board watts (the sampler by its own pid). Per pack: the self-test, the 2^24 fingerprint at base nonce 0 against the
# Mac's (G1: Metal = Apple OpenCL on 7 October 2026, 17:5x UTC), and a timed rate for the ladder row (MH/s, board watts,
# microjoules per hash). Lines: `RESULT G1 pack= fingerprint= match=`, `RESULT LADDER pack= ops= mhs= watts= uj=`, a
# `SUMMARY {json}` line. Read back with `node tools/jobs.mjs <job id>`.
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-v4-sub3-amd-g1'
$kitId = 'fetch-ca3-v4-sub3-pc1-20261007'
$started = Get-Date
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
function Summary([string] $status, [hashtable] $extra) {
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
}
# the expected 2^24 fingerprints at base nonce 0 (the Mac on Metal and Apple OpenCL, the 5090 on CUDA: status file
# section 3, docs/analysis/latency-shadow-2026-10-06.md sections 2 and 5, counter-asic-2-status.md 22:16 for the
# control; re-checked on Apple OpenCL through this kit's host.c on 6 October 2026, 15:2x UTC)
$expected = @{
'mx8-devnet-epoch0' = '90f794dd556f7a3b'
'v4-devnet-epoch0' = 'e370fb2080b7dbb1'
'v4-era-0' = 'b7237555d31fc3cf'
'v4-era-1' = 'b6b167fa15dfe2c9'
'v4-era-2' = '28bdf65eff33f2c4'
'v4-era-3' = 'e26d38c46f3f1b16'
'v4-era-4' = 'dd8fdf6ff4f59eed'
'v4-era-5' = '8bf40f5cb858d835'
'sh256x13' = 'ff8f707210659eb1'
'sh256x27' = '892b6d55a7ddcfcb'
'sh256x53' = '663c73c61f62cc54'
'sh256x88' = 'ec7ca430a061fa59'
'sh64x52' = '9dd010f79d8ca9f4'
}
# ops per hash as the status file counts them (x1.83 counted ops per shadow instruction + 930 for the base program);
# the shadow instruction count is read from each pack's program.json at run time and printed beside it
$opsPerHash = @{ 'mx8-devnet-epoch0' = 930; 'v4-devnet-epoch0' = 102100; 'v4-era-0' = 102100; 'v4-era-1' = 102100; 'v4-era-2' = 102100; 'v4-era-3' = 102100; 'v4-era-4' = 102100; 'v4-era-5' = 102100; 'sh256x13' = 49700; 'sh256x27' = 102100; 'sh256x53' = 199600; 'sh256x88' = 330700; 'sh64x52' = 49700 }
# timed dispatches of 2^24 nonces per pack: about 0.9 s each at 19 MH/s, longer where the shadow binds the card
$batches = @{ 'mx8-devnet-epoch0' = 90; 'v4-devnet-epoch0' = 90; 'v4-era-0' = 40; 'v4-era-1' = 40; 'v4-era-2' = 40; 'v4-era-3' = 40; 'v4-era-4' = 40; 'v4-era-5' = 40; 'sh256x13' = 90; 'sh256x27' = 90; 'sh256x53' = 80; 'sh256x88' = 60; 'sh64x52' = 90 }
$order = @('mx8-devnet-epoch0', 'v4-devnet-epoch0', 'v4-era-0', 'v4-era-1', 'v4-era-2', 'v4-era-3', 'v4-era-4', 'v4-era-5', 'sh256x13', 'sh256x27', 'sh256x53', 'sh256x88', 'sh64x52', 'mx8-devnet-epoch0')
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
# ---- the kit (the wiped-jobs-folder rule: tested before use; republish the fetch after any app update) ----
$jobs = Split-Path $env:IGNEUM_JOB_DIR
$kit = Join-Path $jobs $kitId
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
$exe = Join-Path $kit 'bin\igneum-worker-opencl.exe'
$packs = Join-Path $kit 'packs'
if (-not (Test-Path $exe)) { "RESULT error worker missing at $exe"; Summary 'failed' @{ error = 'worker missing' }; exit 2 }
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs"; Summary 'failed' @{ error = 'packs missing' }; exit 2 }
"RESULT worker kit $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
foreach ($f in (Get-ChildItem $kit -Recurse -File | Where-Object { $_.Name -match '\.(exe|c|json)$|SHA256SUMS' } | Sort-Object FullName)) { "RESULT kitfile $($f.FullName.Substring($kit.Length + 1).Replace('\', '/')) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower()) bytes $($f.Length)" }
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-ca3-pc1-amd' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
# ---- the installed app: install folder (the installed worker for the cross-check and the AMD helper), app dir, URL ----
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
$urlFile = Join-Path $appDir 'app.url'
$base = $null
if (Test-Path $urlFile) { $base = (Get-Content -LiteralPath $urlFile -Raw).Trim().TrimEnd('/') }
"RESULT app install=$inst app_dir=$appDir url_present=$([bool]$base)"
# ---- the OpenCL device index of the 9070 XT, from the worker's own list (the current platform; the older platform's duplicate is marked dup) ----
$listExe = $exe
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $listExe = Join-Path $inst 'igneum-worker-opencl.exe' }
$list = @(& $listExe --list 2>&1 | ForEach-Object { "$_" })
$list | ForEach-Object { "RESULT list $_" }
$dev = $null
foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
if ($null -eq $dev) { "RESULT error no gfx1201 device in --list (the eGPU is off the bus: every row OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
"RESULT device $dev gfx1201 (list from $listExe)"
# ---- the card is switched off by the RUNNER (`--cards-off amd:gfx1201` at publish), never by this script (rule of
# 6 October 2026, tools/ci/playbook-quit-check.sh rule 2); here only the read-only confirmation that its worker is gone ----
function Workers {
@(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe' OR Name = 'igneum-worker-metal.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" })
}
function CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" } | ForEach-Object { $_.ProcessId }) }
"RESULT workers_before $(Stamp) $((Workers) -join ' ')"
# ---- the AMD helper: the installed app's igneum-gpu-telemetry.exe (0.3.10+), else the newest one under an amd-kit job folder ----
$tele = $null
if ($inst -and (Test-Path (Join-Path $inst 'igneum-gpu-telemetry.exe'))) { $tele = Join-Path $inst 'igneum-gpu-telemetry.exe' }
if (-not $tele) { $found = Get-ChildItem -Path (Join-Path $appDir 'jobs') -Recurse -Filter 'igneum-gpu-telemetry.exe' -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'amd-kit' } | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($found) { $tele = $found.FullName } }
$wattsSource = 'owed'
if ($tele) {
$one = @(& $tele 2>&1 | ForEach-Object { "$_" })
$one | ForEach-Object { "RESULT tele_once $_" }
$card9070 = $one | Where-Object { $_ -match '^amd \d+ .*name "[^"]*9070[^"]*" watts (\S+) .* source (\S+)' } | Select-Object -First 1
if ($card9070 -and $card9070 -match ' watts (\S+) .* source (\S+)$') { if ($Matches[1] -ne '-') { $wattsSource = $Matches[2] } }
"RESULT tele helper=$tele sha256=$((Get-FileHash -Algorithm SHA256 $tele).Hash.ToLower()) watts_source=$wattsSource (board watts through ADLX GPUPower, GPUTotalBoardPower as the fallback; 'owed' = the helper gives no watts for the 9070 XT)"
} else { "RESULT tele none (no igneum-gpu-telemetry.exe in the install folder or under an amd-kit job folder): watts OWED" }
$samples = Join-Path $job 'amd-samples.log'
$sampler = $null
$windows = @()
$results = @()
$cardAlone = $false
try {
# ---- the card quiet? read-only: the runner switched it off before this script started; its worker must be gone ----
$before = @(CardWorkers)
"RESULT card_workers_before pids=[$($before -join ' ')]"
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker with --device $dev (the runner's --cards-off took, or --stop-miners)"; $cardAlone = $true }
else {
$t = 0
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
$left = @(CardWorkers)
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)"; $cardAlone = $true }
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still carry --device $dev (was the job published with --cards-off amd:gfx1201?); the rows below are LOADED-card figures" }
Start-Sleep -Seconds 5
}
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
# ---- the sampler: the helper at 1 Hz through a wrapper; its paths through the ENVIRONMENT (inherited by the child,
# never on a command line: Start-Process -ArgumentList in Windows PowerShell 5.1 joins arguments WITHOUT quoting,
# and "Igneum Miner" holds a space, which is why the 15:46Z run wrote no samples), the -File path quoted; ended
# by its own pid in finally. The sampler PROVES itself in the 12 idle seconds: the first three raw lines are
# printed, and without a 9070 XT line with watts the ladder is NOT run blind (the rates are on record from 15:46Z). ----
$samplerOk = $false
if ($tele -and $wattsSource -ne 'owed') {
$wrapper = Join-Path $job 'sampler.ps1'
$wrapperText = @'
$exe = $env:IGNEUM_SAMPLER_EXE
$out = $env:IGNEUM_SAMPLER_OUT
Add-Content -LiteralPath $out -Value ('wrapper start ' + (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' exe=' + $exe + ' exists=' + (Test-Path -LiteralPath $exe))
& $exe -l 1 2>&1 | ForEach-Object { Add-Content -LiteralPath $out -Value ((Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' ' + $_) }
Add-Content -LiteralPath $out -Value ('wrapper end ' + (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' exit=' + $LASTEXITCODE)
'@
[IO.File]::WriteAllText($wrapper, $wrapperText, (New-Object System.Text.UTF8Encoding $false))
Remove-Item -LiteralPath $samples -Force -ErrorAction SilentlyContinue
$env:IGNEUM_SAMPLER_EXE = $tele
$env:IGNEUM_SAMPLER_OUT = $samples
$sampler = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ('"' + $wrapper + '"')) -WindowStyle Hidden -PassThru
"RESULT sampler pid=$($sampler.Id) wrapper=$wrapper file=$samples exe_via_env=$tele"
Start-Sleep -Seconds 12
$raw = @(); if (Test-Path -LiteralPath $samples) { $raw = @(Get-Content -LiteralPath $samples) }
$raw | Select-Object -First 3 | ForEach-Object { "RESULT sampler_raw $_" }
$n9070 = @($raw | Where-Object { $_ -match '^\S+ amd \d+ .*name "[^"]*9070[^"]*" watts [\d.]+ ' }).Count
"RESULT sampler_idle lines=$($raw.Count) lines_9070_with_watts=$n9070 sampler_alive=$(-not $sampler.HasExited)"
if ($n9070 -eq 0) {
$why = if ($raw.Count -eq 0) { 'the sampler wrote nothing in 12 s (file absent or empty)' } else { 'the sampler wrote ' + $raw.Count + ' line(s) but none is a 9070 XT line with a watts number (first lines above)' }
"RESULT watts error=$why; the ladder is NOT run blind"
foreach ($pk in $order) { "RESULT LADDER pack=$pk error=$why" }
} else { $samplerOk = $true; "RESULT idle $(Stamp) sampler proven on the idle window" }
} else { "RESULT watts error=no helper or no watts in its one-shot sample: the ladder runs with watts owed"; $samplerOk = $true }
# ---- the ladder ----
$i = 0
foreach ($pk in $order) {
if (-not $samplerOk) { break }
$i++
$d = Join-Path $packs $pk
if (-not (Test-Path $d)) { "RESULT G1 pack=$pk error=pack missing at $d"; "RESULT LADDER pack=$pk error=pack missing"; continue }
$instrs = 0; $cls = ''
try { $pj = Get-Content -LiteralPath (Join-Path $d 'program.json') -Raw | ConvertFrom-Json; $cls = [string] $pj.load_class; if ($pj.shadow) { $instrs = [int] $pj.shadow.instrs_per_hash } } catch { "RESULT note program.json of $pk unreadable: $($_.Exception.Message)" }
$n = $batches[$pk]
$tag = "$pk#$i"
$t0 = (Get-Date).ToUniversalTime()
"RESULT bench $tag start $(Stamp) batches=$n class=$cls shadow_instrs_per_hash=$instrs"
$lines = @(& $exe --bench-pack --pack $d --batches $n --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
$t1 = (Get-Date).ToUniversalTime()
$lines | ForEach-Object { "RESULT bench $tag $_" }
"RESULT bench $tag end $(Stamp) exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds)"
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
$fp = ''; $mhs = ''; $check = ''
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
$buildMs = ''; if (($lines | Where-Object { $_ -match '^build ([\d.]+) ms clBuildProgram' } | Select-Object -First 1) -match '^build ([\d.]+) ms') { $buildMs = $Matches[1] }
$dsMs = ''; if (($lines | Where-Object { $_ -match '^pack .*: cache \d+ dataset (\d+) ' } | Select-Object -First 1) -match ' dataset (\d+) ') { $dsMs = $Matches[1] }
if (-not $res -or $rc -ne 0) {
$err = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); if (-not $err) { $err = "exit $rc, no RESULT line" }
"RESULT G1 pack=$pk error=$($err -replace '\s+', ' ')"
"RESULT LADDER pack=$pk error=$($err -replace '\s+', ' ')"
continue
}
$want = $expected[$pk]
$match = if ($want -and $fp -eq $want) { 'yes' } elseif ($want) { 'no' } else { 'unknown' }
"RESULT G1 pack=$pk fingerprint=$fp match=$match expected=$want check=$check card=gfx1201 harness=opencl-kit"
$results += [pscustomobject]@{ tag = $tag; pack = $pk; instrs = $instrs; ops = $opsPerHash[$pk]; mhs = $mhs; fp = $fp; match = $match; check = $check; build_ms = $buildMs; dataset_ms = $dsMs; t0 = $t0; t1 = $t1 }
Start-Sleep -Seconds 3
}
# ---- the cross-check: the INSTALLED worker on the control pack, if it carries --bench-pack ----
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) {
$iexe = Join-Path $inst 'igneum-worker-opencl.exe'
$help = (& $iexe --help 2>&1 | Out-String)
if ($help -match '--bench-pack') {
$d = Join-Path $packs 'mx8-devnet-epoch0'
$t0 = (Get-Date).ToUniversalTime()
"RESULT xcheck start $(Stamp) worker=installed sha256=$((Get-FileHash -Algorithm SHA256 $iexe).Hash.ToLower())"
$lines = @(& $iexe --bench-pack --pack $d --batches 40 --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
$t1 = (Get-Date).ToUniversalTime()
$lines | Where-Object { $_ -match '^RESULT|^pack |^warm-up|FAIL|error' } | ForEach-Object { "RESULT xcheck $_" }
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
$fp = ''; $mhs = ''
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
if ($res) { "RESULT XCHECK worker=installed pack=mx8-devnet-epoch0 fingerprint=$fp match=$(if ($fp -eq $expected['mx8-devnet-epoch0']) { 'yes' } else { 'no' }) mhs=$mhs exit=$rc (the kit worker's control rows are the LADDER lines)"; $windows += [pscustomobject]@{ tag = 'xcheck-installed'; t0 = $t0; t1 = $t1 } }
else { "RESULT XCHECK worker=installed error=exit $rc, no RESULT line" }
} else { "RESULT XCHECK worker=installed skipped: the installed igneum-worker-opencl.exe has no --bench-pack" }
}
} finally {
# ---- whatever happened: the sampler ended by its own pid (never by name: the app runs its own copy of the helper), the card back ----
if ($sampler) { try { & taskkill /T /F /PID $sampler.Id 2>&1 | Out-Null; "RESULT sampler ended pid=$($sampler.Id)" } catch { "RESULT sampler end error=$($_.Exception.Message)" } }
# the card comes back through the RUNNER (its restore on any exit), not here
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
}
# ---- the samples: board watts and clocks of the 9070 XT inside each bench window (after its first 12 s, before its last 2 s) ----
$rows = @()
if (Test-Path $samples) {
foreach ($l in (Get-Content -LiteralPath $samples)) {
if ($l -match '^(\S+) amd (\d+) bus (\S+) kind (\S+) name "([^"]*)" watts (\S+) temp_c (\S+) fan_rpm (\S+) fan_pct (\S+) mclk_mhz (\S+) gclk_mhz (\S+) util_pct (\S+) source (\S+)') {
if ($Matches[5] -notmatch '9070') { continue }
try { $ts = [datetime]::ParseExact($Matches[1], 'yyyy-MM-ddTHH:mm:ss.fff', [Globalization.CultureInfo]::InvariantCulture) } catch { continue }
$w = if ($Matches[6] -eq '-') { $null } else { [double] $Matches[6] }
$rows += [pscustomobject]@{ ts = $ts; w = $w; temp = $Matches[7]; fan = $Matches[9]; mclk = $Matches[10]; gclk = $Matches[11]; util = $Matches[12]; src = $Matches[13] }
}
}
}
"RESULT samples total $($rows.Count) file $samples (9070 XT lines)"
function Window([datetime] $t0, [datetime] $t1) { @($rows | Where-Object { $_.ts -gt $t0.AddSeconds(12) -and $_.ts -lt $t1.AddSeconds(-2) -and $null -ne $_.w }) }
$g1Match = 0; $ladderRows = 0
foreach ($r in $results) {
$in = @(Window $r.t0 $r.t1)
$watts = 'owed'; $uj = 'owed'; $gclk = 'owed'; $mclk = 'owed'; $tmax = 'owed'; $wmin = ''; $wmax = ''
if ($in.Count -gt 0) {
$m = $in | Measure-Object -Property w -Average -Minimum -Maximum
$watts = [math]::Round($m.Average, 1); $wmin = $m.Minimum; $wmax = $m.Maximum
$gclk = [math]::Round((($in | ForEach-Object { [double] $_.gclk }) | Measure-Object -Average).Average)
$mclk = [math]::Round((($in | ForEach-Object { [double] $_.mclk }) | Measure-Object -Average).Average)
$tmax = (($in | ForEach-Object { [double] $_.temp }) | Measure-Object -Maximum).Maximum
if ($r.mhs -and [double] $r.mhs -gt 0) { $uj = [math]::Round($m.Average / [double] $r.mhs, 3) }
}
if ($r.match -eq 'yes') { $g1Match++ }
$ladderRows++
"RESULT LADDER pack=$($r.pack) ops=$($r.ops) shadow_instrs=$($r.instrs) mhs=$($r.mhs) watts=$watts uj=$uj watts_min=$wmin watts_max=$wmax gclk_mhz=$gclk mclk_mhz=$mclk temp_max=$tmax samples=$($in.Count) window_s=$([int]($r.t1 - $r.t0).TotalSeconds) build_ms=$($r.build_ms) dataset_ms=$($r.dataset_ms) fingerprint=$($r.fp) match=$($r.match) card_alone=$cardAlone tag=$($r.tag)"
}
foreach ($win in $windows) { $in = @(Window $win.t0 $win.t1); if ($in.Count -gt 0) { $m = $in | Measure-Object -Property w -Average; "RESULT power $($win.tag) samples=$($in.Count) watts_mean=$([math]::Round($m.Average, 1))" } }
if ($rows.Count -gt 0) {
$idle = @($rows | Where-Object { $results.Count -gt 0 -and $_.ts -lt $results[0].t0 -and $null -ne $_.w })
if ($idle.Count -gt 0) { "RESULT idle_power samples=$($idle.Count) watts_mean=$([math]::Round(($idle | Measure-Object -Property w -Average).Average, 1)) (the card switched off in the app, before the first bench)" }
foreach ($r in $rows) { "RESULT sample $($r.ts.ToString('HH:mm:ss')) $($r.w) $($r.temp) $($r.fan) $($r.mclk) $($r.gclk) $($r.util) $($r.src)" }
}
$status = if (-not $samplerOk) { 'failed' } elseif ($results.Count -eq $order.Count) { 'done' } elseif ($results.Count -gt 0) { 'partial' } else { 'failed' }
"RESULT end $(Stamp)"
Summary $status @{ packs_run = $results.Count; packs_wanted = $order.Count; g1_match = $g1Match; ladder_rows = $ladderRows; watts = $wattsSource; sampler_proven = $samplerOk; card_alone = $cardAlone; device = $dev }
if ($status -eq 'failed') { exit 1 }
exit 0