Merge remote-tracking branch 'build/master' into class-v5

This commit is contained in:
igneum-labs 2026-10-08 13:49:16 +00:00
commit 19b1ab6c5a
10 changed files with 502 additions and 17 deletions

View file

@ -178,6 +178,18 @@ A per-block random challenge whose answer window is shorter than one DRAM round
Reading: nothing published since 2023 offers a lever outside the identity; every energy figure is a streaming pJ per bit, and no one has measured a per-random-read joule on GDDR6, GDDR7 or HBM3 at the controller, so the record's 2.0 and 1.2 nJ stand as the only model and are owed a measurement nobody can rent (the AWS F2 hour of chip-model 5.3 is still the one HBM2 instrument). The new rows change two precedents: the Ethash band's top is about 13x (claimed) not 6.8x, and the Monero chip story has a shipped-but-unverified successor to the withdrawn X9.
### 2.7 Three precedents, sourced for the 15:45 close (the research lane's ask of 14:0x UK; every figure read 8 October 2026)
A correction to the question as asked first: Monero did not go chip-free after November 2019; the Antminer X5 shipped in September 2023 at about parity to 1.5x per joule against the best measured CPU.
| Precedent (launch) | The designers' claim | The chip that shipped | Measured edge per joule against the best commodity part | Months to the first chip | Sources |
|---|---|---|---|---|---|
| Monero RandomX (fork 30 November 2019) | design.md, Introduction: a PoW "must achieve device binding by targeting specific features of existing general-purpose hardware"; section 3.4 SuperscalarHash: light-mode ASICs "will be bottlenecked by SuperscalarHash ... their efficiency will be destroyed by the high power usage"; no numeric bound anywhere in the document (claimed) | Bitmain Antminer X5, preorders 29 August 2023, delivered late September 2023: 212 kH/s at 1,350 W = 157 H/J (claimed); Antminer X9 announced 26 December 2025, 1,000 kH/s at 2,472 W = 405 H/J (claimed), USD 5,600, listed for July 2026, reported pulled on 31 July 2026 by a reseller with no Bitmain statement | Ryzen 9 9950X on xmrig 20.66 kH/s at 199 W = 104 H/J (hashrate.no, one verified submission, measured); Kryptex 27 kH/s at 170 W = 159 H/J (unlabelled); the X5 1.0x to 1.5x; the X9, if it exists, 3.9x | 46 | https://github.com/tevador/RandomX/blob/master/doc/design.md ; https://www.asicminervalue.com/miners/bitmain/antminer-x5 ; https://github.com/monero-project/monero/issues/10270 ; https://oneminers.com/blogs/news/antminer-x9-cancelled-what-bitmain-pulling-the-model-means-for-monero-mining ; https://www.hashrate.no/cpus/9950x/XMR ; https://pool.kryptex.com/device/cpu/AMD/ryzen-9-9950x |
| Ethereum Ethash (mainnet 30 July 2015; PoW ended 15 September 2022, ETC continues) | a memory-hard DAG to keep GPUs competitive; no numeric bound (claimed) | first: Antminer E3, announced 3 to 4 April 2018, 180 MH/s at 800 W = 0.23 MH/J, shipped July 2018 with most units slipping to Q4 (claimed); current: iPollo V2H, released November 2024, 3,400 MH/s at 475 W (both plus or minus 10 percent) = 7.2 MH/J (vendor page, claimed) | RTX 4090 on Etchash 127 MH/s at 249 W = 0.51 MH/J; RTX 5090 215.8 MH/s at 405 W = 0.53 (hashrate.no, community-measured); the E3 under a good 2018 GPU per joule; the V2H 14x the 4090, 13.5x the 5090 | 36 | https://cointelegraph.com/news/bitmain-releases-ethash-asic-miners ; https://www.tweaktown.com/news/61527/bitmain-pushes-back-ethereum-asic-miner-now-coming-july/index.html ; https://www.ipollo.com/products/ipollo-v2h ; https://asicminervalue.com/miners/ipollo/v2h ; https://www.hashrate.no/gpus/4090 ; https://www.hashrate.no/gpus/5090 |
| Kaspa kHeavyHash (mainnet 7 November 2021) | no resistance claimed: designed for optical PoW, described as ASIC-friendly by design (secondary sources) | IceRiver KS0, August to September 2023, 100 GH/s at 65 W = 1.54 GH/J; Antminer KS3, October 2023, 9.4 TH/s at 3,500 W = 2.7 GH/J; Antminer KS5 Pro, March 2024, 21 TH/s at 3,150 W = 6.67 GH/J (all claimed) | RTX 4090 2,080 MH/s at 226 W = 9.2 MH/J (Kryptex medium profile, GPU-reported watts); the KS0 167x, the KS5 Pro 725x | 21 to 22 | https://www.asicminervalue.com/miners/iceriver/ks0 ; https://www.asicminervalue.com/miners/bitmain/antminer-ks3 ; https://www.asicminervalue.com/miners/bitmain/antminer-ks5-pro ; https://www.kryptex.com/overclocking/nvidia-rtx-4090-micron-24gb-medium-overclock ; https://www.btc-echo.de/academy/bibliothek/kaspa-mining/ |
Reading: RandomX promised parity and got roughly it (1.0x to 1.5x after 46 months; the one chip claiming more never shipped); Ethash promised nothing numeric, bought 36 months before a chip worse than a GPU, and sits at about 14x today on a memory system without a GPU; Kaspa claimed nothing, got its chip in 21 months and went 167x to 725x in six. Every ASIC watt is nameplate and every CPU and GPU figure a community test table, so the edges carry about 20 percent either way.
## 3. This lane's own ideas
### 3.1 The second sector: W = 16 words, the whole 64-byte item (KILL at stock on measured rows; the knee row owed)
@ -272,6 +284,7 @@ Both sides are capex-dominated (the record's 16.1: 7x to 10x their electricity p
- The chip side of 3.1 is the model's own data-movement term applied twice (4.5 pJ per bit, Micron, claimed, streaming; O'Connor's 3.48 for HBM2) and lane 3's wire figure for the SRAM die; no one has measured a second-sector column read's energy on GDDR7 (section 2.6: the public record has no random-read joule on any current part).
- The tensor chip figures are vendor specifications at TDP (claimed), the M4 ANE and the H800 loop measured; the RT figures are claims and one measured RTX 2080 row; the PHY node facts are vendor IP pages read today.
- The project-cost corrections use the history's figures (a 7 nm-class startup project USD 50 M to 75 M, claimed; the 12 nm figure scaled, approximate) and the mission lane's cap model.
- The `w64` class's census under the sub-version 3 rule and its F8 row, which this lane owed before any cut, are DONE by the census lane (`docs/analysis/class-v6/census-w16-mix.md` on `class-v6-census` at d20eb04bd, 14:0x UK): 256 of 256 seeds accepted with and without an era (0.704 rejection per candidate against the control's 0.668; 0 exhaustions; 0 window-bit refusals for the instrument's reason; 189 stride-bit refusals of 1,524 candidates against the control's 218; (c''') 1.4 percent); the F8-form top-0.1-percent share 0.999 to 1.006 of the uniform control at 2^20 on 16 seeds; the verifier +0.6 percent inside a 4 percent drift on a loaded core (this file's +4 percent neither confirmed nor refuted; a quiet-box row owed). One structural row for 3.1: at 16 words the product's low bits are alignment, so the whole-item read removes the product's own bias from the address (the control carries it at bit 0 in 116 of 256 programs); the stride bit R stays until the index fold. The class is sound; it is dead on energy, not on soundness.
- Nothing ran on the Mac, on a Hetzner box or on a PC for this file; the measurements ran on four RunPod one-shot pods, all destroyed on their done (the 5090 pod when its rows are in).
## 7. Sources

View file

@ -2508,6 +2508,8 @@ F8's 64-seed gate on 017e7037 (the attack-pass lane, box 2, last seed 16:00:20 U
The tail read (8 October 2026, 09:40 to 09:46 UK, the hash lane, the M5 Max under the measure lock, one seed at a time; attack-f8 census at 2^24 nonces, window-model control, by-site attribution; tree b38b4af6 with igneum-pow at the frozen 017e7037): the four gate ratios reproduce to four places (p4 1.2169x, p8 1.3774x, p10 1.5036x, p34 1.2501x), the hot-set verdict on the windowed control is clear on all four, and every tail is attributed to one load site reading a narrow window, the site's 256-item bucket concentration carrying the excess with no saturated and no lossy source. p4: site 1 (instr 8, source r2, window 2^22 items, offset 1, last base writer mad at 4) puts 1.448 percent of its reads into the top 0.1 percent of items against 0.107 flat, index entropy 13.74 of 14 bits, largest 256-item bucket 4.5x its window expectation; hottest item 0x4000e7 at 355 reads, predicted source none. p8: site 14 (instr 51, r7, window 2^22, offset 2, writer xor at 44) 1.423 percent, entropy 13.72 of 14, bucket 3.1x, and site 6 (instr 33, r3, window 2^23, offset 1, writer mad at 30) 0.834 percent, entropy 14.71 of 15, bucket 3.5x; hottest 0x837de4 at 420 reads, source none. p10: site 8 (instr 28, r0, window 2^22, offset 1, writer mad at 20) 2.040 percent, entropy 13.71 of 14, bucket 5.6x; hottest 0x4004da at 362 reads, source none. p34: site 1 (instr 13, r3, window 2^23, offset 1, writer sub at 5) 1.352 percent, entropy 14.96 of 15, bucket 3.5x; hottest 0x800010 at 541 reads, predicted source a one-bit value from the sub at 5, saturated source 0.0001 percent. Every other site in all four seeds reads at its flat share. Reading: the residue is a per-site bucket concentration of about a quarter bit (0.26 to 0.29 bits short of uniform on a 2^22 window; 0.04 on p34) at one narrow-window site whose last writer is a mad, an xor or a sub, and the ratio tracks the bucket excess (5.6x gives 1.50x; 3.1x to 4.5x give 1.22x to 1.38x). Sub-version 3's distinct-index ratio (c'') passes these sites at 0.9927 to 0.9963 because distinctness does not see a bucket. The check that would refuse all four is a per-site bound on the largest 256-item bucket, about 2x its window expectation, at the 2^20 units (c'') already draws; it is a generator change, so it is not for the frozen 017e7037, and its cost on clean seeds is unmeasured (a morning item). Chip consequence of the residue: none beyond the window model's (a quarter bit at one site of sixteen is under 1.002x on the item set a chip would hold). The row's tail is attributed; nothing in the stream, the id or the fingerprints changes.
The F8-256 attributions (8 October 2026, 14:0x UK; the attack-pass lane's 256-seed gate on the class v5 freeze 1c420786, section (e) of its record, and the hash lane's by-site reads): the two seeds the gate flagged with a predicted one-one-bit source were read by site. p212 (1.1917x over the window model, the attack-pass lane on the gate's own line, build-4): the excess sits at site 9 (instr 35, r6, window 2^22, offset 0) at 16.4x its flat share, index entropy 13.981 of 14 bits, the largest 256-item bucket 1.75x its window expectation, the hottest items read from that site in all eight iterations near the window's base; the predicted site (instr 7, r5, a load at 2) reads the ordinary narrow-window share. p225 (1.2452x, the hash lane on the 1c420786 crate, build-3): site 4 (instr 23, r7, window 2^23, offset 1, last base writer mad at 21) at 12.6x with full entropy and the bucket at expectation, the predicted site (instr 37, r5, a load at 36) second at 3.7x. Reading: p212 is this row's tail class (a quarter-bit bucket concentration at one narrow-window site) and p225 a value-level concentration on specific items from a mad-written index; in both the predicted-source line names a site that is not the excess, so that prediction is a hint and the by-site histogram is the attribution. Consequence for the next class's acceptance: a per-site bucket bound near 2x refuses this morning's four (3.1x to 5.6x) and neither of these (1.75x; none); the value-level test (adv-cache-2's) is what sees p225, and a bucket bound near 1.5x would take p212 at a clean-seed cost estimated at 3 to 6 percent. Nothing in the stream, the id or the fingerprints changes.
Two in-house pass findings against sub-version 3 (adv-accept-3, 7 October 2026, evening), neither a chain consequence. (1) Documentation: program.json's `program_id_derivation` and spec 01 section 1.4.6 stated the plain form while generator 4 appends `'sub/' || sub_version_le16`, so a client written from the text derives another id (the devnet v4 pack: 8aa9f185d63f269e from the text against a785001687d8688a; Devnet 3's epoch 0: 30956569d8f3d8d7 against fce15bf61030be57). Fixed at 15008aca and 0f45c8be: one byte recipe (`generator::IdRecipe`) builds the id and the text, the emitter prints that text, the spec states the suffix, the class v5 form (generator 5, no suffix) and the rung form, `tests/derivation.rs` re-derives every pinned pack's id from its own text with the pack's fields only (the plain text as the known-failed case), 38 packs' program.json corrected one line each with every other file and every id and fingerprint byte-identical on re-export; the twelve readwidth mix packs of 5 October keep their text (a class name the parser no longer accepts). (2) Correctness, unreachable: the last-resort program after 256 failed attempts (`last_resort_v4`, the xor rewrite) fails the acceptance rule in 223 of 2,500 seeds (209 by part (a)) and is handed to the chain unchecked; at r = 0.67 to 0.68 the path is reached with probability about 4.6e-44 per epoch, so no chain reaches it and sub-version 3 is not reopened for it. Sub-version 3's last resort is therefore UNREACHABLE AND UNVERIFIED against the rule; class v5's generator (the v5 lane) carries the verified construction with the known-failed test on one of the 223 seeds, and its entry states it. From the same pass: the three lowest-ratio steering seeds of 4,975 at a 256-unit sample read 1.0002x worst live, so the 256-unit ratio is noise as a selector and the selector works only at the 2^20-unit read, which is what rule (c'') uses.
Owed (recorded, not run, by the founder's word): G2 (the CPU verifier on 1,024 hashes per card) on the amended stream; G3 (the Metal fuzz, edge, stats and determinism runs) on the amended stream; the hash-rate ladder re-measure on the M5 Max and the RTX 5090 (the amendment changes the base program's source draws, not the op mix or the load count, so the latency-bound rows of `docs/analysis/latency-shadow-2026-10-06.md` are expected to hold within their spread; unmeasured); AMD (the RX 9070 XT, PC 1); the 2019-class verifier core (O-1.14); F8's phase E (the 64-seed dynamic census) on the amended stream, which is the attack-pass lane's and the test of the per-op table. The row reads FIXED-AND-PASSED only after phase E passes against the amended class.

View file

@ -70,9 +70,10 @@ input[readonly]{color:var(--ash)}
.status[data-state="bad"]{color:var(--bad)}
.status code,td code,dd code{font-family:var(--f-mono);font-size:13px;background:var(--obsidian);padding:1px 6px;border-radius:6px}
p.small,.small{font-size:14px;color:var(--ash);line-height:1.55}
table.swaps,table.pools{width:100%;border-collapse:collapse;font-size:14px;margin-top:8px}
table.swaps,table.pools{width:100%;table-layout:fixed;border-collapse:collapse;font-size:14px;margin-top:8px}
table.swaps td,table.pools td,table.swaps th,table.pools th{white-space:normal;overflow-wrap:anywhere;word-break:break-word}
table.swaps th,table.pools th{text-align:left;color:var(--ash);font-weight:500;padding:8px 6px;border-bottom:1px solid var(--line)}
table.swaps td,table.pools td{padding:10px 6px;border-bottom:1px solid var(--line);vertical-align:top;overflow-wrap:anywhere}
table.swaps td,table.pools td{padding:10px 6px;border-bottom:1px solid var(--line);vertical-align:top;white-space:normal;overflow-wrap:anywhere}
table.swaps td[data-state="bad"]{color:var(--bad)}
table.swaps tr.empty td{color:var(--ash)}
p.notice{margin-top:14px;padding:12px 14px;border-radius:10px;border:1px solid var(--line-2);font-size:13px;color:var(--molten);display:block}
@ -284,6 +285,7 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:13px;overflo
<thead><tr><th>Pool</th><th>Reserves</th></tr></thead>
<tbody id="pools-body"><tr><td colspan="2">Reading the chain…</td></tr></tbody>
</table>
<p class="small" id="pools-note" role="status" aria-live="polite"></p>
<div class="faucet-row">
<button class="btn ghost" id="drip-tta" type="button" disabled>Get 1,000 TTA</button>
<button class="btn ghost" id="drip-ttb" type="button" disabled>Get 1,000 TTB</button>
@ -512,9 +514,10 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:13px;overflo
function refreshPools(){
rpc('eth_blockNumber',[]).then(function(n){
var h=parseInt(n,16);rpcBehind=h<DEPLOY_BLOCK;
if(rpcBehind){$('pools-body').innerHTML='<tr><td colspan="2">The public RPC is still syncing: block '+h.toLocaleString('en-GB')+' of at least '+DEPLOY_BLOCK.toLocaleString('en-GB')+'. Reserves, quotes and swaps appear when it reaches the pools.</td></tr>';return;}
if(rpcBehind){$('pools-body').innerHTML='<tr><td colspan="2">RPC syncing</td></tr>';say($('pools-note'),'The public RPC is syncing: block '+h.toLocaleString('en-GB')+' of at least '+DEPLOY_BLOCK.toLocaleString('en-GB')+'. Reserves, quotes and swaps appear when it reaches the pools.','bad');return;}
say($('pools-note'),'','');
return readPools();
}).catch(function(){$('pools-body').innerHTML='<tr><td colspan="2">The public RPC did not answer. Try again in a minute.</td></tr>';});
}).catch(function(){$('pools-body').innerHTML='<tr><td colspan="2">no answer</td></tr>';say($('pools-note'),'The public RPC did not answer. Try again in a minute.','bad');});
}
function readPools(){
Promise.all(POOLS.map(function(p){return call(A.router,enc(S.getReserves,['address','address'],[p.a,p.b])).then(function(r){var w=words(r);return fmt(w[0],2)+' '+p.sa+' / '+fmt(w[1],2)+' '+p.sb;}).catch(function(){return 'unreadable';});})).then(function(rows){

View file

@ -0,0 +1,322 @@
# card-in (8 October 2026, the hash lane, the coordinator's ask at 09:5x UK): one job for the eGPU housings on PC 1 and PC 2. It
# reads the installed app's card list (api/state mining.cards: key, name, vendor, vram), compares it with the last read in a
# state file on the PC, and runs the full pass on the ONE card that was not there last time, with no hand on the PC:
# 1. class v4 and class v5 fingerprints against the kits (CUDA worker on an NVIDIA card with the sub-version 3 kit's
# v4-devnet-epoch0, expected e370fb2080b7dbb1; the v5 kit's OpenCL worker on every vendor with v5-dn3-epoch0, expected
# 82b19cbde8557ea5, and v4-genesis, expected 892b6d55a7ddcfcb, the 9070 XT's PASS reading of 8 October 00:16Z);
# 2. rate and watts (NVIDIA: the nvidia-smi sampler at 1 Hz, power.draw, power.draw.instant and power.draw.average, around
# a 60 s CUDA bench; AMD and Intel: measure-only, the app's own hash_now and power_w for the card, the median of 30 s
# after a 75 s settle, with the card mining in the app);
# 3. the clock-lock knee grid where the lever exists (NVIDIA through the Power Helper's cleared sequence, 300 MHz steps from
# the card's maximum down, 60 s each, stop when the rate falls 3 percent under the unlocked rate; AMD measure-only until
# the ADLX tool is on the PC, Intel measure-only; the row says which);
# 4. VRAM and the dataset fit (1 GiB dataset plus the cache; a card under 2 GiB does not fit);
# 5. a bench-log row and a miner-bench.json row (RESULT ROW lines) for the audit lane;
# 6. the restore (-rgc through the helper, the card's app choices put back exactly), the state file updated, and "next".
# Known-failed first: no new card reads "RESULT card-in no new card" and does nothing else (exit 0, status no_new_card); the
# first run on a PC writes the baseline and does nothing else (status baseline). Never two cards: more than one new key
# stops the job (status ambiguous) with the keys named. Nothing escalates, nothing is installed, settings.json is never
# written (the runner's --cards-off switches the card off for the quiet rows and back on after; a script never switches cards).
$ErrorActionPreference = 'Continue'
$jobName = 'pc-card-in'
$started = Get-Date
$capMinutes = 50
$deadline = $started.AddMinutes($capMinutes)
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 6)
}
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
function Median($vals) { $v = @($vals | ForEach-Object { [double]$_ } | Sort-Object); if ($v.Count -eq 0) { return $null }; return [math]::Round($v[[int][math]::Floor(($v.Count - 1) / 2)], 1) }
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) }
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
$u = Join-Path $appDir 'app.url'
if (-not (Test-Path -LiteralPath $u)) { 'RESULT error no app.url (the installed app is not running)'; Summary 'failed' @{ error = 'no app url' }; exit 2 }
$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) { 'RESULT error api/state unreachable'; Summary 'failed' @{ error = 'no state' }; exit 2 }
$cards = @($s.mining.cards)
$keys = @($cards | ForEach-Object { $_.key })
"RESULT cards now $(Stamp) $(($cards | ForEach-Object { $_.key + '=' + $_.name + ' [' + $_.vendor + ', ' + $_.kind + ', ' + $_.vram_mb + ' MB, ' + $(if ($_.enabled) { 'on' } else { 'off' }) + ']' }) -join ' | ')"
# ---- the last read ----
$stateDir = Join-Path $appDir 'jobs\card-in'
if (-not (Test-Path $stateDir)) { New-Item -ItemType Directory -Path $stateDir -Force | Out-Null }
$lastFile = Join-Path $stateDir 'last.json'
$last = @()
if (Test-Path -LiteralPath $lastFile) { try { $last = @((Get-Content -LiteralPath $lastFile -Raw | ConvertFrom-Json).keys) } catch { $last = @() } }
function WriteLast() { ([ordered]@{ keys = $keys; at = (Stamp); machine = $env:COMPUTERNAME } | ConvertTo-Json -Compress) | Set-Content -LiteralPath $lastFile -Encoding ascii }
if (-not (Test-Path -LiteralPath $lastFile)) {
WriteLast
"RESULT card-in baseline written: $($keys.Count) cards ($($keys -join ', ')); nothing measured (the first run on this PC records what is here; plug the card and say in)"
Summary 'baseline' @{ keys = $keys }
exit 0
}
$new = @($keys | Where-Object { $last -notcontains $_ })
$gone = @($last | Where-Object { $keys -notcontains $_ })
if ($gone.Count -gt 0) { "RESULT card-in gone since the last read: $($gone -join ', ')" }
if ($new.Count -eq 0) {
WriteLast
"RESULT card-in no new card (last read $($last.Count) keys, now $($keys.Count); nothing done)"
Summary 'no_new_card' @{ keys = $keys }
exit 0
}
if ($new.Count -gt 1) {
"RESULT card-in STOP: $($new.Count) new keys ($($new -join ', ')); one card at a time; nothing done, the last read unchanged"
Summary 'ambiguous' @{ new = $new }
exit 3
}
$key = $new[0]
$card = $cards | Where-Object { $_.key -eq $key } | Select-Object -First 1
$name = $card.name; $vendor = "$($card.vendor)"; $vram = [int64]$card.vram_mb
$fits = ($vram -ge 2048)
"RESULT card-in NEW key=$key name=`"$name`" vendor=$vendor kind=$($card.kind) vram_mb=$vram dataset_fit=$(if ($fits) { 'yes (1 GiB dataset plus the cache)' } else { 'NO (under 2 GiB)' }) enabled=$($card.enabled) identities=$($card.identities) power_pct=$($card.power_pct)"
# ---- the kits ----
$jobs = Split-Path $env:IGNEUM_JOB_DIR
$v5kit = Join-Path $jobs 'fetch-ca3-v5-kit-20261007'
$v4kit = $null
foreach ($id in @('fetch-ca3-v4-sub3-pc1-20261007', 'fetch-ca3-v4-sub3-pc2-20261007')) { $d = Join-Path $jobs $id; if (Test-Path (Join-Path $d 'packs\v4-devnet-epoch0\kernel_bound.cu')) { $v4kit = $d; break } }
if (-not (Test-Path $v5kit)) { 'RESULT kits v5 MISSING (the fetch job fetch-ca3-v5-kit-20261007 runs first; republish it after any app update)' }
$clExe = Join-Path $v5kit 'bin\windows\igneum-worker-opencl.exe'
$haveV5 = (Test-Path $clExe) -and (Test-Path (Join-Path $v5kit 'packs\v5-dn3-epoch0\leaves.bin'))
"RESULT kits v5=$(if ($haveV5) { $v5kit + ' (worker sha256 ' + (Get-FileHash -Algorithm SHA256 $clExe).Hash.ToLower().Substring(0, 16) + ')' } else { 'MISSING (the fetch job fetch-ca3-v5-kit-20261007 runs first)' }) v4=$(if ($v4kit) { $v4kit } else { 'none (no CUDA pack on this PC; the NVIDIA v4 row runs through the OpenCL kit)' })"
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
$cudaExe = if ($inst) { Join-Path $inst 'igneum-worker-cuda.exe' } else { $null }
$expected = @{ 'v4-devnet-epoch0' = 'e370fb2080b7dbb1'; 'v5-dn3-epoch0' = '82b19cbde8557ea5'; 'v4-genesis' = '892b6d55a7ddcfcb' }
# ---- the card's device on each path ----
$isNv = ($vendor -eq 'nvidia')
$smi = $null; $idx = $null; $uuid = $null; $bus = $null; $maxSm = $null; $driver = ''
if ($isNv) {
$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 ($smi) {
$gpus = @(& $smi --query-gpu=index,name,uuid,pci.bus_id,power.limit,power.max_limit,clocks.max.sm,driver_version,memory.total --format=csv,noheader 2>&1 | ForEach-Object { "$_" })
$gpus | ForEach-Object { "RESULT gpu $_" }
# the app's name is the driver's name; a second identical card carries "#2" in its key and sits at the higher index
$rows = @($gpus | Where-Object { $_ -match [regex]::Escape($name) })
$pick = 0; if ($key -match '#(\d+)$') { $pick = [int]$Matches[1] - 1 }
if ($rows.Count -gt $pick) { $fld = $rows[$pick] -split ',\s*'; $idx = $fld[0].Trim(); $uuid = $fld[2].Trim(); $bus = $fld[3].Trim(); $maxSm = $fld[6].Trim(); $driver = $fld[7].Trim() }
if ($null -ne $idx) { "RESULT card nvidia index=$idx uuid=$uuid bus=$bus max_sm_mhz=$maxSm driver=$driver" } else { "RESULT card nvidia: '$name' not in nvidia-smi's list; the NVIDIA rows run through the OpenCL kit without watts" }
} else { 'RESULT card nvidia: nvidia-smi not found; the NVIDIA rows run through the OpenCL kit without watts' }
}
$clDev = $null; $clLine = ''
if ($haveV5) {
$listExe = $clExe; 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 $_" }
$want = $name -replace '^(NVIDIA|AMD|Intel\(R\))\s+', ''
$short = ($want -replace '\(R\)|\(TM\)|Graphics|GeForce|Radeon', '' -replace '\s+', ' ').Trim()
$cands = @()
foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\]\s+(.*?)\s+\|' -and $l -notmatch 'dup') { $n = [int]$Matches[1]; $t = $Matches[2]; $ts = ($t -replace '\(R\)|\(TM\)|Graphics|GeForce|Radeon', '' -replace '\s+', ' ').Trim(); if ($ts -match [regex]::Escape($short) -or $short -match [regex]::Escape($ts) -or ($card.code -and $t -match [regex]::Escape("$($card.code)"))) { $cands += ,@($n, $l) } } }
$pick = 0; if ($key -match '#(\d+)$') { $pick = [int]$Matches[1] - 1 }
if ($cands.Count -gt $pick) { $clDev = $cands[$pick][0]; $clLine = $cands[$pick][1] }
if ($null -ne $clDev) { "RESULT card opencl device=$clDev line=$($clLine.Trim())" } else { "RESULT card opencl: no --list device matches '$name' (code $($card.code)); the OpenCL rows are skipped" }
}
# ---- the card's quiet state is the RUNNER's: a script never switches cards (the playbook-quit rule). The detect run reads the
# new card with the app mining on it; the measured pass is published again with --cards-off <the new key> and finds it off here.
$choices = @($cards | ForEach-Object { [ordered]@{ key = $_.key; enabled = [bool]$_.enabled; identities = [int]$_.identities; power_pct = [int]$_.power_pct } })
$restored = $true
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+', ' ')]" }) }
"RESULT workers_before $(Stamp) $((@(Workers)) -join ' ')"
$quiet = -not $card.enabled
"RESULT card $(Stamp) $(if ($quiet) { 'quiet: the runner switched it off (--cards-off) before this script' } else { 'LOADED: the card mines in the app (a detect run, or a pass published without --cards-off); the rows below are LOADED-card figures' })"
if (-not $quiet -and $env:IGNEUM_CARDIN_DETECT_ONLY -eq '1') { WriteLast; "RESULT next: detect only; publish the pass with --cards-off $key"; Summary 'detected' @{ key = $key; name = $name; vendor = $vendor; vram_mb = $vram; fits = $fits }; exit 0 }
$rowsOut = [ordered]@{}
$samples = Join-Path $stateDir 'nv-samples.csv'
$sampler = $null
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 Watts([datetime] $t0, [datetime] $t1) {
$w = ReadWindow $t0.AddSeconds(8) $t1
return [ordered]@{ draw = (Mean ($w | ForEach-Object { $_[1] })); instant = (Mean ($w | Where-Object { $_.Count -ge 8 -and $_[6] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[6] })); average = (Mean ($w | Where-Object { $_.Count -ge 8 -and $_[7] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[7] })); sm = (Mean ($w | ForEach-Object { $_[2] })); mem = (Mean ($w | ForEach-Object { $_[3] })); tmax = (($w | ForEach-Object { [int]$_[4] } | Measure-Object -Maximum).Maximum); n = $w.Count }
}
# ---- the Power Helper (NVIDIA lock path, the cleared sequence) ----
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'
$cmdFile = Join-Path $helperDir 'cmd.txt'
$helperLog = Join-Path $helperDir 'helper.log'
function HelperAliveAge() { $a = Join-Path $helperDir 'helper.alive'; if (-not (Test-Path -LiteralPath $a)) { return 9999 }; try { $v = [int64]((Get-Content -LiteralPath $a -Raw).Trim()); return ([int64][double]::Parse((Get-Date -UFormat %s)) - $v) } catch { return 9999 } }
function HelperSeqBase() {
$m = 0
foreach ($f in @($cmdFile, $helperLog)) { if (Test-Path $f) { foreach ($l in (Get-Content -LiteralPath $f -ErrorAction SilentlyContinue)) { if ($l -match '(?:^|\s)(\d{1,6}) (?:dev|pl|lgc|rgc|lmc|rmc|nvidia-smi|reregister)\b') { $v = [int]$Matches[1]; if ($v -gt $m) { $m = $v } } } } }
return $m + 100
}
function HelperReset() {
Set-Content -LiteralPath $cmdFile -Value '' -NoNewline -Encoding ascii
try { Stop-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue } catch { }
Start-Sleep -Seconds 2
try { Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop } catch { $t = "$_" -replace '\s+', ' '; "RESULT helper start refused: $t"; return $false }
$t = 0
while ($t -lt 20) { Start-Sleep -Seconds 1; $t++; if ((HelperAliveAge) -le 4) { "RESULT helper fresh after $t s"; return $true } }
$s0 = HelperSeqBase
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
Set-Content -LiteralPath $cmdFile -Value "$s0 dev $idx`n" -NoNewline -Encoding ascii
$t2 = 0
while ($t2 -lt 20) { Start-Sleep -Seconds 1; $t2++; $new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue); if ($new.Count -gt $mark -and ($new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s0 dev " })) { "RESULT helper up after $($t + $t2) s (probe dev line answered)"; return $true } }
'RESULT helper STOP: no heartbeat and no answer to a probe dev line; nothing escalates; the lock rows are skipped'
return $false
}
$viaHelper = $false
$seq = 0
function HelperSend([string] $verb) {
$script:seq += 2
$s0 = $script:seq; $s1 = $script:seq + 1; $script:seq = $s1
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
if ((HelperAliveAge) -gt 10) { if (-not (HelperReset)) { return 'no helper (the reset failed)' } }
Set-Content -LiteralPath $cmdFile -Value "$s0 dev $idx`n$s1 $verb`n" -NoNewline -Encoding ascii
$t = 0
while ($t -lt 30) {
Start-Sleep -Seconds 1; $t++
$new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue)
if ($new.Count -gt $mark) { $hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1; if ($hit) { return ($hit -replace '^\d+\s+', '') } }
}
$b = HelperSeqBase
if ($b -gt $script:seq) { "RESULT helper seq re-based from $script:seq to $b"; $script:seq = $b; return (HelperSend $verb) }
return "no helper.log line for seq $s1 within 30 s"
}
function SetLock([int] $mhz) { if (-not $viaHelper) { return 'refused: no helper' }; $o = HelperSend "lgc $mhz"; if ($o -match 'not have permission|No permission|Insufficient|not supported|Unable|ERROR|no helper') { return "refused: $o" }; return "ok (helper, -lgc 0,$mhz): $o" }
function ResetClocks() { if (-not $viaHelper) { return 'not attempted (no helper)' }; $o = HelperSend 'rgc'; if ($o -match 'no helper.log') { if (HelperReset) { $o = "after a task restart: $(HelperSend 'rgc')" } }; return $o }
# ---- the benches ----
function ClBench([string] $pk, [int] $batches) {
$d = Join-Path $v5kit "packs\$pk"
$t0 = Get-Date
$out = @(& $clExe --bench-pack --pack $d --device $clDev --batch-log2 24 --batches $batches 2>&1 | ForEach-Object { "$_" })
$code = $LASTEXITCODE; $t1 = Get-Date
$res = $out | Where-Object { $_ -match '^RESULT ' } | Select-Object -Last 1
$fp = ''; $mhs = $null; $check = ''
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -match 'mhs=([0-9.]+)') { $mhs = [double]$Matches[1] }
if ($res -match 'check=(\w+)') { $check = $Matches[1] }
$err = ''; if (-not $res -or $code -ne 0) { $e = ($out | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); $err = if ($e) { ($e -replace '\s+', ' ') } else { "exit $code, no RESULT line" } }
return [pscustomobject]@{ pack = $pk; t0 = $t0; t1 = $t1; rc = $code; fp = $fp; mhs = $mhs; check = $check; err = $err; path = 'opencl-kit' }
}
function CudaBench([string] $pk, [int] $batches) {
$d = Join-Path $v4kit "packs\$pk"
$env:CUDA_VISIBLE_DEVICES = $uuid
$t0 = Get-Date
$out = @(& $cudaExe --bench --pack $d --batches $batches --batch-log2 24 --block-warps 1 --device 0 2>&1 | ForEach-Object { "$_" })
$code = $LASTEXITCODE; $t1 = Get-Date
$res = $out | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
$fp = ''; $mhs = $null; $check = ''
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 $code -ne 0) { $e = ($out | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); $err = if ($e) { ($e -replace '\s+', ' ') } else { "exit $code, no RESULT line" } }
return [pscustomobject]@{ pack = $pk; t0 = $t0; t1 = $t1; rc = $code; fp = $fp; mhs = $mhs; check = $check; err = $err; path = 'cuda-installed' }
}
function Report($b, [string] $label, $w) {
$want = $expected[$b.pack]; $match = if ($want -and $b.fp -eq $want) { 'yes' } elseif ($want) { 'no' } else { 'unknown' }
$eff = if ($w -and $w.draw -and $w.draw -gt 0 -and $b.mhs) { [math]::Round($b.mhs / $w.draw, 4) } else { '' }
$wt = if ($w) { "watts=$($w.draw) watts_instant=$($w.instant) watts_average=$($w.average) sm_mhz=$($w.sm) mem_mhz=$($w.mem) temp_max=$($w.tmax) samples=$($w.n)" } else { 'watts= (no sampler on this path)' }
"RESULT $label card=`"$name`" key=$key pack=$($b.pack) path=$($b.path) fingerprint=$($b.fp) expected=$want match=$match check=$($b.check) mhs=$($b.mhs) $wt mh_per_w=$eff wall_s=$([int]($b.t1 - $b.t0).TotalSeconds) exit=$($b.rc) error=$($b.err)"
return $match
}
$lockRows = @()
$fpV4 = ''; $fpV5 = ''; $unlocked = $null; $bestEff = $null; $knee = ''
try {
if ($isNv -and $smi -and $null -ne $idx) {
if (Test-Path $samples) { Remove-Item -LiteralPath $samples -Force }
$sampler = Start-Process -FilePath $smi -ArgumentList @('-i', $idx, '--query-gpu=timestamp,power.draw,clocks.sm,clocks.mem,temperature.gpu,clocks_throttle_reasons.active,power.draw.instant,power.draw.average', '--format=csv,noheader,nounits', '-lms', '1000') -RedirectStandardOutput $samples -WindowStyle Hidden -PassThru
Start-Sleep -Seconds 6
"RESULT sampler pid=$($sampler.Id) first=[$((@(Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue | Select-Object -First 1)) -join '')]"
}
# 1. fingerprints: the OpenCL kit on every vendor (v5 then the v4 control), 5 batches each
if ($haveV5 -and $null -ne $clDev) {
$b5 = ClBench 'v5-dn3-epoch0' 5; $fpV5 = Report $b5 'FP' $(if ($sampler) { Watts $b5.t0 $b5.t1 } else { $null }); $rowsOut['v5'] = $b5.fp
$b4 = ClBench 'v4-genesis' 5; $fpV4 = Report $b4 'FP' $(if ($sampler) { Watts $b4.t0 $b4.t1 } else { $null }); $rowsOut['v4_genesis'] = $b4.fp
} else { 'RESULT FP skipped: no OpenCL kit or no matching device' }
# 2. the rate row: CUDA on NVIDIA with the sub-version 3 pack (60 s), else the OpenCL kit's v4 control for 60 s
$probe = $null
if ($isNv -and $v4kit -and $cudaExe -and (Test-Path $cudaExe) -and $uuid) { $probe = CudaBench 'v4-devnet-epoch0' 5 }
elseif ($haveV5 -and $null -ne $clDev) { $probe = ClBench 'v4-genesis' 5 }
if ($probe -and -not $probe.err -and $probe.mhs) {
$n = [math]::Max(20, [math]::Min(2000, [int][math]::Ceiling(60 * $probe.mhs * 1e6 / 16777216.0)))
$r0 = ResetClocks
$u = if ($probe.path -eq 'cuda-installed') { CudaBench 'v4-devnet-epoch0' $n } else { ClBench 'v4-genesis' $n }
$w = if ($sampler) { Watts $u.t0 $u.t1 } else { $null }
$m = Report $u 'RATE unlocked' $w
if ($u.path -eq 'cuda-installed') { $rowsOut['v4_devnet'] = $u.fp }
$unlocked = [ordered]@{ mhs = $u.mhs; watts = $(if ($w) { $w.draw } else { $null }); eff = $(if ($w -and $w.draw) { [math]::Round($u.mhs / $w.draw, 4) } else { $null }); sm = $(if ($w) { $w.sm } else { $null }); path = $u.path; match = $m }
# 3. the knee grid (NVIDIA through the helper): 300 MHz steps from the card's maximum down, 60 s each, stop at a 3 percent fall
if ($isNv -and $null -ne $idx -and -not $probe.err) {
$task = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
if ($task) { $seq = HelperSeqBase; if (HelperReset) { $viaHelper = $true; "RESULT helper task started; commands from seq $seq" } }
else { 'RESULT helper task NOT REGISTERED (Power control off in the app): no lock path; measure-only' }
if ($viaHelper) {
$top = 0; if ($maxSm -match '^\d+$') { $top = [int]$maxSm }
$start = [int]([math]::Floor(($top - 1) / 300) * 300); if ($start -le 0) { $start = 2400 }
$bestEff = $unlocked.eff; $bestAt = 'unlocked'
for ($mhz = $start; $mhz -ge 600; $mhz -= 300) {
if ((Get-Date) -gt $deadline.AddMinutes(-6)) { "RESULT LOCK mhz=$mhz skipped: budget spent (six minutes kept for the restore and the app row)"; break }
$lk = SetLock $mhz
if ($lk -match '^refused') { "RESULT LOCK mhz=$mhz $lk"; break }
Start-Sleep -Seconds 3
$b = if ($u.path -eq 'cuda-installed') { CudaBench 'v4-devnet-epoch0' $n } else { ClBench 'v4-genesis' $n }
$w = if ($sampler) { Watts $b.t0 $b.t1 } else { $null }
$m = Report $b "LOCK mhz=$mhz" $w
if ($b.err -or -not $b.mhs) { break }
$eff = if ($w -and $w.draw) { [math]::Round($b.mhs / $w.draw, 4) } else { $null }
$lockRows += [ordered]@{ mhz = $mhz; sm = $(if ($w) { $w.sm } else { $null }); mhs = $b.mhs; watts = $(if ($w) { $w.draw } else { $null }); eff = $eff; match = $m }
if ($eff -and $eff -gt $bestEff) { $bestEff = $eff; $bestAt = "$mhz MHz" }
if ($m -eq 'no') { "RESULT LOCK mhz=$mhz fingerprint mismatch: the grid stops, clocks reset"; break }
if ($unlocked.mhs -and $b.mhs -lt 0.97 * $unlocked.mhs) { $knee = "$mhz MHz (rate $([math]::Round(100 * (1 - $b.mhs / $unlocked.mhs), 1)) percent under unlocked)"; "RESULT KNEE $knee best=$bestAt eff=$bestEff"; break }
}
if (-not $knee) { $knee = "not reached by $([math]::Max(600, $mhz + 300)) MHz"; "RESULT KNEE $knee best=$bestAt eff=$bestEff" }
}
} elseif (-not $isNv) { "RESULT LOCK measure-only: $vendor has no lock path in this job ($(if ($vendor -eq 'amd') { 'the ADLX tool once it is on the PC' } else { 'Intel: none' }))" }
} else { "RESULT RATE skipped: $(if ($probe) { $probe.err } else { 'no bench path for this card' })" }
} finally {
if ($viaHelper) { "RESULT restore rgc: $(ResetClocks)" }
if ($sampler) { try { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue } catch { }; 'RESULT restore sampler stopped' }
'RESULT restore cards: the runner puts the card back with its own choices on exit (nothing switched here)'
}
# 4. the app row (every vendor): the card mining in the app, 75 s settle, the median of 30 s of hash_now and power_w
$appMhs = $null; $appW = $null
if ($card.enabled) {
Start-Sleep -Seconds 75
$hs = @(); $ws = @()
for ($i = 0; $i -lt 6; $i++) { $st = State; if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $key } | Select-Object -First 1; if ($cc -and $cc.hash_now -gt 0) { $hs += [double]$cc.hash_now; $ws += [double]$cc.power_w } }; Start-Sleep -Seconds 5 }
$appMhs = Median $hs; $appW = Median $ws
"RESULT APP card=`"$name`" key=$key mhs=$appMhs power_w=$appW mh_per_w=$(if ($appMhs -and $appW -and $appW -gt 0) { [math]::Round($appMhs / $appW, 4) } else { '' }) samples=$($hs.Count) (the installed app's own reading, the card mining)"
} else { 'RESULT APP skipped (the card was off in the app before this job, or the restore was refused)' }
# 5. the rows
$date = (Get-Date).ToUniversalTime().ToString('yyyy-MM-dd')
$mhs = if ($unlocked -and $unlocked.mhs) { $unlocked.mhs } else { $appMhs }
$watts = if ($unlocked -and $unlocked.watts) { $unlocked.watts } else { $appW }
$eff = if ($mhs -and $watts -and $watts -gt 0) { [math]::Round($mhs / $watts, 3) } else { $null }
$best = $lockRows | Where-Object { $_.eff } | Sort-Object { $_.eff } -Descending | Select-Object -First 1
$tuned = if ($best) { "lock $($best.mhz) MHz: $($best.mhs) MH/s at $($best.watts) W ($($best.eff) MH/W)" } elseif ($isNv) { 'stock (no lock path on this run)' } else { "stock (measure-only on $vendor)" }
$fpNote = "v5 $(if ($fpV5 -eq 'yes') { 'match' } elseif ($fpV5 -eq 'no') { 'MISMATCH' } else { 'not read' }), v4 $(if ($fpV4 -eq 'yes' -or ($unlocked -and $unlocked.match -eq 'yes')) { 'match' } elseif ($fpV4 -eq 'no' -or ($unlocked -and $unlocked.match -eq 'no')) { 'MISMATCH' } else { 'not read' })"
"RESULT ROW bench-log | $name ($vram MB) | $env:COMPUTERNAME | $date | $mhs MH/s | $watts W | $eff MH/W | $fpNote | $tuned | knee $(if ($knee) { $knee } else { 'n/a' }) | app $appMhs MH/s at $appW W |"
$mb = [ordered]@{ card = "$name ($([math]::Round($vram / 1024)) GB)"; generator = 'v4'; mh_s = $mhs; watts = $watts; mh_per_w = $eff; miner = "installed app $env:IGNEUM_APP_VERSION, class v4 program ($(if ($unlocked) { $unlocked.path } else { 'app row' }))"; date = $date; source = "card-in job on $env:COMPUTERNAME, $date"; by = 'measured by the hash lane (card-in)'; note = "fingerprints: $fpNote; vram $vram MB, dataset fit $(if ($fits) { 'yes' } else { 'no' }); app row $appMhs MH/s at $appW W"; v4_cost = 'class v4 program only'; tuned = $tuned; driver_os = "$(if ($driver) { 'NVIDIA driver ' + $driver + ', ' } else { '' })Windows 11"; hive = [ordered]@{ core_mhz = $(if ($best) { $best.mhz } else { $null }); mem_mhz = $null; pl_w = $null; label = $(if ($best) { "lock $($best.mhz) MHz, $($best.eff) MH/W" } else { 'stock' }) }; group = 'egpu' }
"RESULT ROW miner-bench $($mb | ConvertTo-Json -Compress -Depth 4)"
$rowsOut['locks'] = $lockRows
WriteLast
"RESULT next: $name done on $env:COMPUTERNAME at $(Stamp); the last read now carries it; unplug or plug the next card and say in"
Summary 'done' @{ key = $key; name = $name; vendor = $vendor; vram_mb = $vram; fits = $fits; fp_v5 = $fpV5; fp_v4 = $fpV4; unlocked = $unlocked; locks = $lockRows; knee = $knee; app_mhs = $appMhs; app_w = $appW; restored = $restored }
exit 0

View file

@ -0,0 +1,87 @@
# Class v5 kit bench on PC 1's RX 9070 XT (7 October 2026, docs/design/class-v5-stored-state.md section 13, "the kit:
# OpenCL (AMD)"): the kit's igneum-worker-opencl.exe (THIS tree's proto-opencl/host.c with the class v5 leaf upload:
# igneum_build(ds, cache, leaves, nLeaves, nItems), the leaves of leaves.bin checked against the pack's FNV-1a 64 first)
# runs --bench-pack on the first class v5 pack, proto-cuda/packs-ca3-v5/v5-dn3-epoch0 (program id e5a4ac5978462156, 11
# leaves under state root 7e37a9fb..., cache FNV 7334fa46e5d972eb), on the gfx1201 device. Expected: the self-test
# PASS line (cache head, last line and FNV; dataset head, word [268435455] and 64 samples; 96 of 96 vector lanes) and the
# fingerprint of the 2^24 outputs at base nonce 0 equal to the Metal reading, 82b19cbde8557ea5 (the M5 Max, 7 October
# 2026, 18:07:09Z). The v4-genesis control pack runs after it so the run has a known class v4 row beside the v5 row.
# Beside the miners (a correctness and fingerprint gate; the rate row is labelled loaded when the card mines): no card is
# switched, nothing is posted to the installed app, nothing is built on the PC. Published by the Counter ASIC coordinator
# only (the PC 1 queue is its); the kit is fetch job $kitId. Every result line starts with RESULT; SUMMARY {json} ends it.
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-amd-cardin-bench'
$cardMatch = $env:IGNEUM_AMD_CARD; if (-not $cardMatch) { $cardMatch = '7600' }
$sizesId = 'fetch-ca3-amd-sizes-20261008'
$kitId = $env:IGNEUM_V5_KIT_ID; if (-not $kitId) { $kitId = 'fetch-ca3-v5-kit-20261008-pc1' }
$expected = '82b19cbde8557ea5'
$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 expected_fingerprint=$expected"
$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\windows\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 }
"RESULT worker kit $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
$sizes = Join-Path (Join-Path $jobs $sizesId) 'packs'
if (-not (Test-Path $sizes)) { "RESULT error sizes kit missing at $sizes (the fetch job $sizesId runs first)"; Summary 'failed' @{ error = 'sizes kit missing' }; exit 2 }
foreach ($pk in @('v5-dn3-epoch0', 'v4-genesis', 'v3-1g', 'v3-ds29', 'v3-ds30')) {
$d = if ($pk -like 'v3-*') { Join-Path $sizes $pk } else { Join-Path $packs $pk }
if (-not (Test-Path (Join-Path $d 'kernel_bound.cl'))) { "RESULT error pack $pk missing at $d"; Summary 'failed' @{ error = "pack $pk missing" }; exit 2 }
"RESULT pack $pk kernel_bound.cl sha256 $((Get-FileHash -Algorithm SHA256 (Join-Path $d 'kernel_bound.cl')).Hash.ToLower()) program.h sha256 $((Get-FileHash -Algorithm SHA256 (Join-Path $d 'program.h')).Hash.ToLower())"
}
$leaves = Join-Path $packs 'v5-dn3-epoch0\leaves.bin'
if (-not (Test-Path $leaves)) { "RESULT error leaves.bin missing at $leaves (a class v5 pack without its leaves builds nothing)"; Summary 'failed' @{ error = 'leaves missing' }; exit 2 }
"RESULT leaves $leaves bytes $((Get-Item $leaves).Length) sha256 $((Get-FileHash -Algorithm SHA256 $leaves).Hash.ToLower())"
# the OpenCL device index of the 9070 XT (the installed worker's list when present: the app's own indices)
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
$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+)\].*' + [regex]::Escape($cardMatch)) -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
if ($null -eq $dev) { foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1200' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } } }
if ($null -eq $dev) { "RESULT error no device matching $cardMatch (or gfx1200) in --list: is the card in its housing and on the bus?"; Summary 'failed' @{ error = 'no card' }; exit 2 }
"RESULT device $dev match=$cardMatch (list from $listExe)"
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)
$loaded = ($w | Where-Object { $_ -match "igneum-worker-opencl.*--device\s+$dev(\s|$)" }).Count -gt 0
"RESULT workers_before $(Stamp) $($w -join ' ')"
$state = if ($loaded) { 'loaded' } else { 'quiet' }
"RESULT context card_state=$state (a fingerprint gate: the load changes the rate row, never the bytes)"
$rows = @{}
$fpOk = $false
foreach ($pk in @('v5-dn3-epoch0', 'v4-genesis')) {
$d = Join-Path $packs $pk
$t0 = Get-Date
"RESULT bench $pk start $(Stamp) cmd=igneum-worker-opencl.exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches $(if ($pk -like 'v3-*') { 30 } else { 5 })"
$out = @(& $exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches $(if ($pk -like 'v3-*') { 30 } else { 5 }) 2>&1 | ForEach-Object { "$_" })
$code = $LASTEXITCODE
$secs = [int]((Get-Date) - $t0).TotalSeconds
foreach ($l in $out) { if ($l -match '^(pack |class v5|RESULT |FAIL|error|warm-up)') { "RESULT bench $pk out $l" } }
$res = $out | Where-Object { $_ -match '^RESULT ' } | Select-Object -Last 1
$fp = ''; $mhs = ''; $check = ''
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -match 'mhs=([0-9.]+)') { $mhs = $Matches[1] }
if ($res -match 'check=(\w+)') { $check = $Matches[1] }
$rows[$pk] = [ordered]@{ exit = $code; seconds = $secs; fingerprint = $fp; mhs = $mhs; check = $check; card_state = $state }
if ($pk -eq 'v5-dn3-epoch0') {
$fpOk = ($fp -eq $expected -and $check -eq 'PASS')
"RESULT v5 fingerprint=$fp expected=$expected match=$fpOk check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
} elseif ($pk -eq 'v4-genesis') {
"RESULT v4-control fingerprint=$fp check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
} else {
"RESULT size pack=$pk fingerprint=$fp check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
}
}
"RESULT workers_after $(Stamp) $((@(Workers)) -join ' ')"
Summary $(if ($fpOk) { 'done' } else { 'failed' }) @{ expected = $expected; v5 = $rows['v5-dn3-epoch0']; v4_control = $rows['v4-genesis']; device = $dev; kit = $kitId }
exit $(if ($fpOk) { 0 } else { 1 })

View file

@ -17,19 +17,39 @@ function Summary([string] $status, [hashtable] $extra) { $o = [ordered]@{ job =
$jobDir = $env:IGNEUM_JOB_DIR; if (-not $jobDir) { $jobDir = Join-Path $env:TEMP 'igneum-ca4-mb' }
New-Item -ItemType Directory -Force -Path $jobDir | Out-Null
$jobs = Split-Path $jobDir
$packs = Join-Path (Join-Path $jobs 'fetch-ca4-packs-20261007') 'packs'
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs (the fetch job fetch-ca4-packs-20261007 runs first)"; Summary 'failed' @{ error = 'packs missing' }; exit 2 }
# the class v6 design rows (8 October 2026, an id carrying "v6"): the ca4-v6 kit (today's class v3 x8 and x16, the two re-weighted shadow
# packs, the invention lane's two per-load packs, the controls mx8-genesis and mx8_sh256x27), same shape, label ca4-v6
$v6 = (Split-Path -Leaf $env:IGNEUM_JOB_DIR) -match 'v6'
$v6b = (Split-Path -Leaf $env:IGNEUM_JOB_DIR) -match 'v6b'
$v6c = (Split-Path -Leaf $env:IGNEUM_JOB_DIR) -match 'v6c'
$rw = (Split-Path -Leaf $env:IGNEUM_JOB_DIR) -match 'readwidth'
$v6d = (Split-Path -Leaf $env:IGNEUM_JOB_DIR) -match 'v6d'
$kitId = if ($v6d) { 'fetch-ca4-v6d-packs-20261008' } elseif ($rw) { 'fetch-ca3-readwidth-20261008' } elseif ($v6c) { 'fetch-ca4-v6c-packs-20261008' } elseif ($v6b) { 'fetch-ca4-v6b-packs-20261008' } elseif ($v6) { 'fetch-ca4-v6-packs-20261008' } else { 'fetch-ca4-packs-20261007' }
$packs = Join-Path (Join-Path $jobs $kitId) 'packs'
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs (the fetch job $kitId runs first)"; Summary 'failed' @{ error = 'packs missing' }; exit 2 }
$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'
if ($rw -or $v6d) { $kexe = Join-Path (Join-Path $jobs 'fetch-ca3-v5-kit-20261008-pc1') 'bin\windows\igneum-worker-cuda.exe'; if (Test-Path $kexe) { foreach ($dll in (Get-ChildItem (Join-Path $inst '*.dll') -ErrorAction SilentlyContinue)) { if (-not (Test-Path (Join-Path (Split-Path $kexe) $dll.Name))) { Copy-Item $dll.FullName (Split-Path $kexe) -ErrorAction SilentlyContinue } }; $exe = $kexe; "RESULT worker readwidth: the v5 kit's CUDA worker $kexe" } else { 'RESULT error the v5 kit worker is missing on this PC (fetch-ca3-v5-kit-20261008-pc1)'; Summary 'failed' @{ error = 'no v5 worker' }; exit 2 } }
# mx8_shl256x27 is the research lane's first per-load export (id 854050a4293f0615, the known-failed record: 11 percent duplicate reads from a lossy
# base writer under the sub-blocks) and mx8_shl256x27_v2 its fix (counter-asic-4 2f718001, attempt 3, id bd64b207a30413fb); both run and every row
# names its pack directory and the program id read from its program.json. 22:44 UTC: the research lane's acceptance rule, judging the per-load
# class in execution order, refuses the 16 x 27 construction on every attempt, so neither per-load export is a candidate class; both rows
# still run as the energy reading of the placement and carry construction=unsound-energy-reading-only
$packOrder = @('mx8-genesis', 'mx8_sh256x27', 'mx8_shl256x27', 'mx8_shl256x27_v2', 'mx8_mm128', 'mx8_mm512', 'mx8_mm1430')
if ($v6) { $packOrder = @('mx8-genesis', 'mx8_sh256x27', 'v3-x8-today', 'v3-x16-today', 'mix-mulheavy', 'mix-shflheavy', 'mx8_shl4096x1', 'mx8_shl2304x3', 'v3-ds29', 'v3-ds30', 'v3-ds31') }
if ($v6b) { $packOrder = @('mix-shflheavy', 'v3-ds29', 'v3-ds30', 'v3-ds31', 'v3-x16-g2', 'v3-x8-g2') }
if ($v6c) { $packOrder = @('mix-mulheavy') }
# the read-width rows (the floor programme, 8 October 2026): W = 4, 8 and 16 words under the class v5 state term (each its own draw), the
# pinned v5-genesis pack as the reference; the w64 pack also at 2, 4 and 8 warps per block in the unlocked state (the best lane count)
if ($rw) { $packOrder = @('v5-genesis', 'rw-w4', 'rw-w32', 'rw-w64', 'rw5-w4', 'rw5-w64') }
# kit d (8 October 2026, the class v6 close): the two weight tables inside the shadow block, and floor lane 5's read-width rows (the 5 October
# w4 control, the w64 pack, the hinted w64-l2 pack: the first uint4 load of each item as ld.global.L2::64B, fingerprint 836e56e7)
if ($v6d) { $packOrder = @('mix-mulheavy-sh', 'mix-shflheavy-sh', 'rw5-w4', 'rw5-w64', 'rw5-w64-l2') }
# the two w64 packs (the v5-state one and the 5 October class v2 one, fingerprint 836e56e7) at 1, 2, 4 and 8 warps per block unlocked and at 1 and 4 at the lock
function WarpsFor([string] $pk, [string] $state) { if ($rw -and $pk -like '*w64*') { if ($state -eq 'unlocked') { @(1, 2, 4, 8) } else { @(1, 4) } } else { @(1) } }
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())" } }
"RESULT start $(Stamp) job=$jobName exe=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) label=ca4-research"
"RESULT start $(Stamp) job=$jobName exe=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) label=$(if ($v6) { 'ca4-v6' } else { 'ca4-research' })"
$smi = (Get-Command nvidia-smi -ErrorAction SilentlyContinue).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 } } }
$row = @(& $smi --query-gpu=index,name,uuid,pci.bus_id --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match 'RTX 5090' } | Select-Object -First 1
@ -84,11 +104,11 @@ function ParseUtc([string] $s) { try { return [datetime]::Parse($s, [cultureinfo
function RunState([string] $state) {
$env:CUDA_VISIBLE_DEVICES = $uuid
"RESULT packs-state $state start $(Stamp) $((& $smi -i $idx --query-gpu=clocks.sm,clocks.applications.graphics,power.draw --format=csv,noheader 2>&1 | Out-String).Trim())"
foreach ($pk in $packOrder) {
foreach ($pk in $packOrder) { foreach ($bw in (WarpsFor $pk $state)) {
$d = Join-Path $packs $pk
$cls = ''; $progId = ''; try { $pj = Get-Content -LiteralPath (Join-Path $d 'program.json') -Raw | ConvertFrom-Json; $cls = [string] $pj.load_class; $progId = [string] $pj.program_id } catch { }
$t0 = Get-Date
$lines = @(& $exe --bench --pack $d --batches 250 --batch-log2 24 --block-warps 1 --device 0 2>&1 | ForEach-Object { "$_" })
$lines = @(& $exe --bench --pack $d --batches 250 --batch-log2 24 --block-warps $bw --device 0 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
$t1 = Get-Date
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
@ -101,9 +121,9 @@ function RunState([string] $state) {
$eff = if ($watts -and $mhs) { [math]::Round([double]$mhs / $watts, 4) } else { '' }
$err = ''; if (-not $res -or $rc -ne 0) { $e = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH|compile' } | Select-Object -First 1); $err = if ($e) { ($e -replace '\s+', ' ') } else { "exit $rc, no RESULT line" } }
$tag = if ($pk -like 'mx8_shl256x27*') { ' construction=unsound-energy-reading-only' } else { '' }
"RESULT ca4-research pack=$pk dir=$d program_id=$progId class=$cls$tag state=$state mhs=$mhs watts=$watts watts_instant=$wi watts_average=$wa mh_per_w=$eff sm_mhz=$sm mem_mhz=$mem util=$util fingerprint=$fp check=$check exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds) samples=$($w.Count) error=$err"
"RESULT ca4-research pack=$pk dir=$d program_id=$progId class=$cls$tag state=$state warps=$bw mhs=$mhs watts=$watts watts_instant=$wi watts_average=$wa mh_per_w=$eff sm_mhz=$sm mem_mhz=$mem util=$util fingerprint=$fp check=$check exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds) samples=$($w.Count) error=$err"
Start-Sleep -Seconds 3
}
} }
"RESULT packs-state $state end $(Stamp)"
}
$locked = $false

View file

@ -44,6 +44,26 @@ case "$STEP" in
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 ;;
card-in-pc1) $P add --kind run --target ae432dc7 --id "run-card-in-pc1-$(date -u +%Y%m%dT%H%M)" --script tools/ca3-v4-amend/pc-card-in.ps1 --timeout-minutes 55 --title "card-in: the newly plugged eGPU card on PC 1, the full pass (fingerprints, rate and watts, the lock grid where the lever exists, the rows); no new card = nothing done" --expires-hours 12 $DEPLOY ;;
card-in-pc2) $P add --kind run --target 1ccfe586 --id "run-card-in-pc2-$(date -u +%Y%m%dT%H%M)" --script tools/ca3-v4-amend/pc-card-in.ps1 --timeout-minutes 55 --title "card-in: the newly plugged eGPU card on PC 2, the full pass (fingerprints, rate and watts, the lock grid where the lever exists, the rows); no new card = nothing done" --expires-hours 12 $DEPLOY ;;
kit-pc2) $P add --kind fetch --target 1ccfe586 --id fetch-ca3-v4-sub3-pc2-20261007 --file "$KITS/igneum-ca3-v4-sub3-pc1-kit.zip" --dir jobs --extract --title "CA3 PC 2: the sub-version 3 kit (worker, 13 packs) for the card-in CUDA rows" --expires-hours 36 $DEPLOY ;;
arc-kit) $P add --kind fetch --target 1ccfe586 --id fetch-ca3-v5-kit-20261008 --file "$KITS/igneum-ca3-v5-kit-20261008.zip" --dir jobs --extract --title "Class v5 kit rebuilt with the Intel rotate-fold fix (tree 8f481459; worker 55722527...)" --expires-hours 36 $DEPLOY ;;
arc-v5) $P add --kind run --target 1ccfe586 --id run-ca3-pc2-v5-intel-bench-20261008 --script "$KITS/arc-v5-bench-c0d398a1.ps1" --timeout-minutes 15 --title "Class v5 kit fingerprint on PC 2 Arc B580, the rotate-fold kit (the v5 lane's script c0d398a1, whole stdout kept; lock-free, quiet, nothing installed)" --expires-hours 12 $DEPLOY ;;
v5kit-pc1) $P add --kind fetch --target ae432dc7 --id fetch-ca3-v5-kit-20261008-pc1 --file "$KITS/igneum-ca3-v5-kit-20261008.zip" --dir jobs --extract --title "Class v5 kit (rotate-fold build, tree 8f481459) on PC 1 for the v5 against v4 lock rows" --expires-hours 36 $DEPLOY ;;
v5lock-5090) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v5lock-5090-20261008-b --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 30 --title "Class v5 against class v4 on the RTX 5090 at unlocked, 1,300 and 1,200 MHz (the v5 kit's CUDA packs, 60 s rows, the helper's cleared sequence, the card alone)" --expires-hours 12 $DEPLOY ;;
v5lock-5080) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v5lock-5080-20261008 --cards-off "nvidia:NVIDIA GeForce RTX 5080" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 30 --title "Class v5 against class v4 on the RTX 5080 at unlocked, 1,100 and 1,000 MHz (the v5 kit's CUDA packs, 60 s rows, the helper's cleared sequence, the card alone)" --expires-hours 12 $DEPLOY ;;
v6-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca4-v6-packs-20261008 --file "$KITS/igneum-ca4-v6-packs-20261008.zip" --dir jobs --extract --title "Class v6 design rows: today's class v3 x8 and x16, two re-weighted shadow packs, the invention lane's two per-load packs, the two controls" --expires-hours 36 $DEPLOY ;;
v6-packs) $P add --kind run --target ae432dc7 --id run-ca4-pc1-v6-packs-5090-20261008 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-packs.ps1 --timeout-minutes 60 --title "Class v6 design rows on the RTX 5090 alone: eleven packs (the class v3 pack at 2, 4 and 8 GiB datasets among them) at unlocked and the 1,300 MHz lock (self-test, 2^24 fingerprint, MH/s, W), label ca4-v6" --expires-hours 12 $DEPLOY ;;
v6b-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca4-v6b-packs-20261008 --file "$KITS/igneum-ca4-v6b-packs-20261008.zip" --dir jobs --extract --title "Class v6 design rows, kit b: the byte-seed re-exports (x8 and x16 on generator 2 with the era, the re-weighted tables, the pinned class v3 program at 2, 4 and 8 GiB)" --expires-hours 36 $DEPLOY ;;
v6b-packs) $P add --kind run --target ae432dc7 --id run-ca4-pc1-v6b-packs-5090-20261008 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-packs.ps1 --timeout-minutes 45 --title "Class v6 design rows, kit b, on the RTX 5090 alone at unlocked and the 1,300 MHz lock, label ca4-v6" --expires-hours 12 $DEPLOY ;;
v6c-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca4-v6c-packs-20261008 --file "$KITS/igneum-ca4-v6c-packs-20261008.zip" --dir jobs --extract --title "Class v6 design rows, kit c: the multiply-heavy weight table (byte-seed form)" --expires-hours 36 $DEPLOY ;;
v6c-packs) $P add --kind run --target ae432dc7 --id run-ca4-pc1-v6c-packs-5090-20261008 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-packs.ps1 --timeout-minutes 20 --title "Class v6 design rows, kit c (the multiply-heavy table) on the RTX 5090 alone at unlocked and the 1,300 MHz lock, label ca4-v6" --expires-hours 12 $DEPLOY ;;
rw-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca3-readwidth-20261008 --file "$KITS/igneum-ca3-readwidth-20261008.zip" --dir jobs --extract --title "The floor programme's read-width packs: W = 4, 8 and 16 words under the class v5 state term, the v5-genesis reference" --expires-hours 36 $DEPLOY ;;
rw-packs) $P add --kind run --target ae432dc7 --id run-ca3-pc1-readwidth-5090-20261008 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-packs.ps1 --timeout-minutes 45 --title "Read width on the RTX 5090 alone: W = 4, 8, 16 words (the v5 state term), the w64 pack at 1, 2, 4, 8 warps per block, unlocked and at the 1,300 MHz lock; label ca4-v6" --expires-hours 12 $DEPLOY ;;
v6d-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca4-v6d-packs-20261008 --file "$KITS/igneum-ca4-v6d-packs-20261008.zip" --dir jobs --extract --title "Class v6 close, kit d: the two weight tables inside the shadow block; floor lane 5's w4, w64 and hinted w64-l2 packs" --expires-hours 36 $DEPLOY ;;
v6d-packs) $P add --kind run --target ae432dc7 --id run-ca4-pc1-v6d-packs-5090-20261008 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-packs.ps1 --timeout-minutes 40 --title "Class v6 close, kit d, on the RTX 5090 alone: the shadow-block weight tables, and w4 against w64 and the hinted w64-l2 at 1, 2, 4, 8 warps unlocked and 1, 4 at the 1,300 MHz lock; label ca4-v6" --expires-hours 12 $DEPLOY ;;
amd-sizes-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca3-amd-sizes-20261008 --file "$KITS/igneum-ca3-amd-sizes-20261008.zip" --dir jobs --extract --title "The 8 GB tier's dataset rows: the pinned class v3 program at 1, 2 and 4 GiB (OpenCL)" --expires-hours 36 $DEPLOY ;;
amd-cardin) $P add --kind run --target ae432dc7 --id "run-ca3-pc1-amd-cardin-$(date -u +%Y%m%dT%H%M)" --script tools/ca3-v4-amend/pc1-amd-cardin-bench.ps1 --timeout-minutes 30 --title "RX 7600 card-in: the class v5 and v4 fingerprints and the dataset rows at 1, 2 and 4 GiB on the OpenCL kit worker, beside the miners" --expires-hours 12 $DEPLOY ;;
read) node tools/jobs.mjs "$1" --all ;;
*) sed -n 2,12p "$0"; exit 2 ;;
esac

View file

@ -25,6 +25,10 @@
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-v4-efficiency'
$kitId = 'fetch-ca3-v4-sub3-pc1-20261007'
# the class v5 against class v4 mode (8 October 2026, main's order at 10:4x UK, an id carrying "v5lock"): the v5 kit's CUDA packs (v4-genesis,
# v5-genesis, v5-dn3-epoch0, the same epoch seed for the first two) at unlocked, 1,300 and 1,200 MHz, 60 s rows; the 5080 at 1,100 and 1,000
$v5lock = (Split-Path -Leaf $env:IGNEUM_JOB_DIR) -match 'v5lock'
if ($v5lock) { $kitId = 'fetch-ca3-v5-kit-20261008-pc1' }
$started = Get-Date
$deadline = $started.AddMinutes(66)
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
@ -39,6 +43,7 @@ $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')
if ($v5lock) { $expected = @{ 'v4-genesis' = '892b6d55a7ddcfcb'; 'v5-dn3-epoch0' = '82b19cbde8557ea5'; 'v5-genesis' = '' }; $packOrder = @('v4-genesis', 'v5-genesis', 'v5-dn3-epoch0') }
# the grid by the job id: an id carrying "floor" runs the second pass, 1,400 MHz down to the driver's floor in 100 MHz steps (the
# first pass found the best MH/W at its lowest lock, 1,400, so the knee sits below it); a lock the driver clamps shows in sm_mhz
# the counter-asic-4 research mode (an id carrying "ca4sparse"): the research lane's SM-sparse worker variants (proto-cuda/nvrtc/worker.cpp on
@ -48,7 +53,7 @@ $packOrder = @('v4-devnet-epoch0', 'mx8-devnet-epoch0')
# label=ca4-research and the worker's race line verbatim; nothing in any pack changes, the fingerprint is checked on every row
$ca4 = $jobId -match 'ca4sparse'
$variants = if ($ca4) { @('base', 'sp170-w32', 'sp85-w32', 'sp43-w32', 'sp21-w32', 'sp11-w32') } else { @('base') }
$locks = if ($ca4) { @(1300) } elseif ($jobId -match '5080') { @(2850, 2781, 2700, 2550, 2472, 2400, 2250, 2163, 2100, 1950, 1854, 1800, 1650, 1500, 1400, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 400, 300) } elseif ($jobId -match 'floor2') { @(1100, 1000, 900, 800, 700, 600, 500, 400, 300) } elseif ($jobId -match 'floor') { @(1400, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 400, 300) } else { @(2850, 2781, 2700, 2550, 2472, 2400, 2250, 2163, 2100, 1950, 1854, 1800, 1650, 1500, 1400) }
$locks = if ($v5lock -and $jobId -match '5080') { @(1100, 1000) } elseif ($v5lock) { @(1300, 1200) } elseif ($ca4) { @(1300) } elseif ($jobId -match '5080') { @(2850, 2781, 2700, 2550, 2472, 2400, 2250, 2163, 2100, 1950, 1854, 1800, 1650, 1500, 1400, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 400, 300) } elseif ($jobId -match 'floor2') { @(1100, 1000, 900, 800, 700, 600, 500, 400, 300) } elseif ($jobId -match 'floor') { @(1400, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 400, 300) } else { @(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 grid=[$($locks -join ' ')] 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'
@ -67,6 +72,9 @@ foreach ($pk in $packOrder) { $d = Join-Path $packs $pk; if (-not (Test-Path $d)
$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'
# the v5lock mode runs the v5 kit's own CUDA worker (generator 5 packs: the installed 0.3.20 worker answers "generator 5 is not a generator
# version this worker runs (2, 3 or 4)", the first run of 10:55 UK); its DLLs are the installed app's, copied beside it
if ($v5lock) { $kexe = Join-Path $kit 'bin\windows\igneum-worker-cuda.exe'; if (Test-Path $kexe) { foreach ($dll in (Get-ChildItem (Join-Path $inst '*.dll') -ErrorAction SilentlyContinue)) { if (-not (Test-Path (Join-Path (Split-Path $kexe) $dll.Name))) { Copy-Item $dll.FullName (Split-Path $kexe) -ErrorAction SilentlyContinue } }; $exe = $kexe; "RESULT worker v5lock: the kit's CUDA worker $kexe sha256 $((Get-FileHash -Algorithm SHA256 $kexe).Hash.ToLower())" } else { 'RESULT error the v5 kit has no windows CUDA worker'; Summary 'failed' @{ error = 'no v5 cuda worker' }; exit 2 } }
if ($ca4) {
# the research lane's exe with the race fix (counter-asic-4 after 2d0013d1, 00:46 UTC; the first exe parsed --variant and never applied it:
# every row of the 00:22Z run read "variant base"); the rows carry its variant= and sparse_blocks= fields and any variant_not_installed line

View file

@ -10,7 +10,7 @@
| the red watcher fires on cancelled and timed-out runs too (`ci-red.yml`, `red-watch.mjs`) | The watcher's `if` missing any of failure, cancelled, timed_out, or the conclusion not handed to the record step (the self-test reads the workflow file); the line names the kind: CI red, CI cancelled, CI timed out. | 7 October 2026 |
| gh's active account is the stored Igneum entry (`gh-account-check.sh`, in Igneum's own gh directory `~/.config/gh-igneum` through `gh-env.sh`, never the founder's) | A push or a landing from this Mac while Igneum's gh directory names any other account as active, or none (the refusal names the one step: the founder or main stores the Igneum token there with `GH_CONFIG_DIR=~/.config/gh-igneum gh auth login --with-token`; no lane does); skipped with a line while `github-suspended` stands. RULE: no lane switches gh accounts on this Mac, ever; the second owner's login belongs to other projects and must never touch Igneum; the stored entry's name is in ~/.config/igneum/gh-user, never in the repository. | 7 October 2026, 21:41 UK: a lane switched gh to the other login during the suspension; nobody could say which |
| no landing on the public host while the marker stands (`pre-push.sh` `forgejo_master_frozen`, `merge-to-master.sh` `forgejo_master_refusal`); the hook binds every remote rule to the remote's URL | A push of master to git.igneum.network, or a `--remote` naming it, while `tools/ci/github-suspended` stands: its master is a rewritten copy replaced at cut-over, so the landing would be lost (a branch pushed there for safekeeping passes). Before the fix the hook matched the remote NAME, so `git push origin master` bound neither the GitHub refusal nor the CI rule; the self-test now drives the hook by name through a fixture repo. Also: `gate-manifest-check.sh` and four other pipefail checks no longer pipe a file-sized producer into `grep -q` (GNU sed took SIGPIPE on an early match and the check read it as a missing run line on the Linux runners and boxes); `mirror_master` fast-forwards every box with a build-server file | 8 Oct 2026 |
| no landing on the public host while the marker stands (`pre-push.sh` `forgejo_master_frozen`, `merge-to-master.sh` `forgejo_master_refusal`); the hook binds every remote rule to the remote's URL | A push of master to git.igneum.network, or a `--remote` naming it, while `tools/ci/github-suspended` stands: its master is a rewritten copy replaced at cut-over, so the landing would be lost (a branch pushed there for safekeeping passes). Before the fix the hook matched the remote NAME, so `git push origin master` bound neither the GitHub refusal nor the CI rule; the self-test now drives the hook by name through a fixture repo. Also: `gate-manifest-check.sh` and four other pipefail checks no longer pipe a file-sized producer into `grep -q` (GNU sed took SIGPIPE on an early match and the check read it as a missing run line on the Linux runners and boxes); `mirror_master` fast-forwards every box with a build-server file after a landing on ANY remote (before, only a GitHub landing fanned out, so a box landing left build-3 and build-4 at a tip 23 hours old), as a `--no-verify` copy of the master the gate already passed (a stale mirror had re-run the full gate for six minutes per box) | 8 Oct 2026 |
| kill by exact command or pid file (owed as a check) | 6 October 2026, 21:09Z: a Mac-side `pkill -f <log file name>` matched nothing (the log name was a redirect, not part of the command line), the roll-everything script lived on and wiped a box it had been told to hold. Rule: a job is stopped by its pid file (`tools/fleet/fleet-bg.sh start|stop <name>`) or by a pattern anchored on its exact command line (`^python3 -u /root/fleet/in/box-prover.py`), never by a word that may or may not appear in it. The check that flags a `pkill -f`/`pgrep -f` whose literal is a path or a name that never starts a command line is owed to the CI lane |

View file

@ -89,12 +89,17 @@ master_gate() {
# a4bca198 while GitHub's carried cf7d6ccb, and three branches were cut from the stale tip; the mirrors only ever received what a
# build happened to push). One push per mirror in IGNEUM_MIRRORS (default: both box files), fast-forward only, best effort: a mirror
# that is down prints a line and never fails the landing.
mirror_master() { # <sha>
local sha="$1" m host key="${BS_KEY:-$HOME/.ssh/igneum_ed25519}" list="${IGNEUM_MIRRORS:-}"
mirror_master() { # <sha> [landed-remote-url]: the landed master to every other mirror
local sha="$1" landed="${2:-}" m host key="${BS_KEY:-$HOME/.ssh/igneum_ed25519}" list="${IGNEUM_MIRRORS:-}"
# every box with a build-server file (8 October 2026, 13:5x UK: build-3 and build-4's mirrors sat at 7 October 15:27 with only the first two listed)
if [ -z "$list" ]; then for f in "$HOME/.config/igneum/build-server" "$HOME"/.config/igneum/build-server-[0-9]*; do [ -s "$f" ] && list="$list $(head -1 "$f" | tr -d '[:space:]'):/srv/igneum.git"; done; fi
for m in $list; do
if GIT_SSH_COMMAND="ssh -i $key -o BatchMode=yes -o ConnectTimeout=10" git push -q "$m" "$sha:refs/heads/master" 2>/dev/null; then echo "merge-to-master: mirror $m master -> ${sha:0:8}"
host="${m#*@}"; host="${host%%:*}"; host="${host%%/*}" # build@<ip>:/srv/igneum.git -> <ip>; the landed URL may be ssh://build@<ip>/srv/igneum.git
key="$host"; [ -n "$key" ] || key="${m#file://}" # a bare-path mirror (the self-test) is known by its path
if [ -n "$landed" ]; then case "$landed" in *"$key"*) continue ;; esac; fi # the mirror that took the landing itself (8 October 2026, 14:19 UK: the host:/path form never matched the ssh:// URL)
# --no-verify: this is a copy of a master the gate already passed on landing (the sha is the landed remote's master, read back),
# not a landing; with the hook on, a stale mirror re-ran the full gate for six minutes per box (8 October 2026, 14:0x UK)
if GIT_SSH_COMMAND="ssh -i $key -o BatchMode=yes -o ConnectTimeout=10" git push -q --no-verify "$m" "$sha:refs/heads/master" 2>/dev/null; then echo "merge-to-master: mirror $m master -> ${sha:0:8}"
else echo "merge-to-master: mirror $m did not take master ${sha:0:8} (down, or not a fast-forward); the next landing tries again"; fi
done
}
@ -163,6 +168,9 @@ success 4 u push run
git clone -q --bare "$d/src" "$d/mirror.git" && ( cd "$d/mirror.git" && git update-ref refs/heads/master "$(git rev-parse master~1)" )
tip=$(git -C "$d/src" rev-parse master)
out=$( cd "$d/src" && IGNEUM_MIRRORS="$d/mirror.git" mirror_master "$tip" )
grep -qE '^ mirror_master "\$\(git rev-parse "\$REMOTE/master"\)"' "$0" || { echo "self-test failed: the landing path does not fan master out to the mirrors for every remote"; fails=1; }
out2=$( cd "$d/src" && IGNEUM_MIRRORS="$d/mirror.git" mirror_master "$tip" "file://$d/mirror.git" ); case "$out2" in *"mirror"*) echo "self-test failed: the mirror that took the landing was pushed to again: $out2"; fails=1 ;; esac
out3=$( cd "$d/src" && IGNEUM_MIRRORS="build@203.0.113.9:/srv/igneum.git" mirror_master "$tip" "ssh://build@203.0.113.9/srv/igneum.git" ); case "$out3" in *"mirror"*) echo "self-test failed: the landed box in ssh:// form was pushed to again from its host:/path mirror entry: $out3"; fails=1 ;; esac
[ "$(git -C "$d/mirror.git" rev-parse master)" = "$tip" ] || { echo "self-test failed: the mirror was not fast-forwarded to the landed master: $out"; fails=1; }
( cd "$d/src" && git checkout -q -b other master~1 && git -c user.name=t -c user.email=t@t commit -q --allow-empty -m fork ); fork=$(git -C "$d/src" rev-parse other)
out=$( cd "$d/src" && IGNEUM_MIRRORS="$d/mirror.git" mirror_master "$fork" )
@ -204,7 +212,9 @@ for i in $(seq 1 "$TRIES"); do
if ( cd "$W" && git push -q "$REMOTE" HEAD:master ); then # on a GitHub remote the hook asks ci-state about ${SHA:0:8} once more
git worktree remove --force "$W"; git fetch -q "$REMOTE" master
echo "merge-to-master: pushed on try $i: $REMOTE/master $(git log -1 --format='%h %ci' "$REMOTE/master") $(TZ=Europe/London date '+%H:%M %Z')"
remote_is_github && mirror_master "$(git rev-parse "$REMOTE/master")"; exit 0
# the landed master to every other mirror, whichever remote took the landing (8 October 2026, 14:0x UK: a box landing never fanned
# out, so build-3 and build-4 cut branches from a tip 23 hours old)
mirror_master "$(git rev-parse "$REMOTE/master")" "$(git remote get-url "$REMOTE" 2>/dev/null)"; exit 0
fi
echo "merge-to-master: try $i: the push was rejected (master moved or the hook was red); again"
else