Intel Arc: the plan, vendor intel in the app (detection, badge, no-cap row, tests), the PC 1 driver, restart, read-back and bench jobs

docs/plans/intel-arc.md: the OpenCL worker runs the B580 first (Intel's OpenCL 3.0 runtime, exchange auto with the
local-memory fallback the worker already carries), the expected ceiling from 456 GB/s (13 to 20 MH/s, approximate until
the memprobe), 12 GB = mine only (the prover is CUDA only), the Arc OpenCL gotchas (sub-group 16 vs 32, SLM, spills,
64-bit multiply, IGC's build options and compile time, two Intel platforms, no power API through OpenCL: IGCL next),
the fallback ladder with hours (SPIR-V into the same runtime, a Level Zero host, SYCL), the consequences per tier.
App: vendor "intel" from the OpenCL vendor string and the Windows name; Arc cards discrete, the Arc-branded processor
graphics integrated; the INTEL badge in the Intel blue token, light and dark; the row's no-reading sentence, the
measure-only tune words and the details panel's no-cap note; sweep.rs names IGCL as the source to come; detect.rs test
on a B580 line beside PC 1's list; view.test.mjs row test; ui-mock scenario intel.
PC 1 jobs (tools/intel-arc): the elevated driver job (ran twice on 7 October: run 1 exit 1007 = the bootstrapper's wrap
of the inner installer's 7 on my -report flag; run b with -s alone exit 1014 = inner 14, installed, restart required),
the restart job (the project lead's word), the read-back job, the packs kit and the bench job with the memprobe, class v4 and v3
benches, the local-exchange row, the bench-log row, the card-picker entry and the owner's consequences line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 10:02:14 +00:00
parent 5e5a309a04
commit f39d64a7e2
13 changed files with 635 additions and 5 deletions

View file

@ -111,8 +111,11 @@ fn card(index: usize, name: &str, vendor: &str, worker: &str, detail: &str, devi
/// CL_DEVICE_NAME: PC 1's Ryzen iGPU is "gfx1036", 5 October 2026).
pub fn looks_integrated(name: &str) -> bool {
let n = name.to_ascii_lowercase();
if n.contains("arc") && n.contains("intel") {
return arc_is_integrated(&n);
}
let integrated = ["radeon(tm) graphics", "radeon graphics", "vega 8", "vega 7", "vega 6", "vega 3", "vega 11", "iris", "uhd graphics", "hd graphics", "intel(r) graphics", "intel graphics", "apu", "780m", "760m", "680m", "610m", "890m", "880m"];
if integrated.iter().any(|k| n.contains(k)) && !n.contains("arc ") {
if integrated.iter().any(|k| n.contains(k)) {
return true;
}
// AMD APU graphics by gfx code (approximate list from AMD's ROCm and Mesa target tables): Raven/Picasso gfx902 and
@ -123,6 +126,22 @@ pub fn looks_integrated(name: &str) -> bool {
apu.iter().any(|k| code == *k || code.starts_with(&format!("{k}:")) || code.starts_with(&format!("{k} ")))
}
/// An Intel name carrying "Arc" (lower-cased): the cards ("Arc(TM) A770", "Arc(TM) B580", "Arc(TM) Pro A60") are
/// discrete; the Arc-branded processor graphics are integrated: the bare "Intel(R) Arc(TM) Graphics" of Meteor Lake
/// and Arrow Lake, the "Arc(TM) 130V / 140V" of Lunar Lake and the "Arc(TM) 1xxT" of Panther Lake (approximate names
/// from Intel's product pages, 7 October 2026; the B580 is the first Intel card in hand, docs/plans/intel-arc.md).
fn arc_is_integrated(lower: &str) -> bool {
let after = lower.split("arc").nth(1).unwrap_or("").trim_start_matches("(tm)").trim_start_matches("(r)").trim();
let word = after.split(|c: char| !c.is_ascii_alphanumeric()).next().unwrap_or("");
// "graphics" or nothing after "Arc": the processor graphics; "130v", "140t": digits first, the processor graphics
if word.is_empty() || word == "graphics" || word.starts_with(|c: char| c.is_ascii_digit()) {
return true;
}
// "pro", "a770", "b580": a card. Anything else unknown is read as a card (on by default; a wrong call costs an
// iGPU 8 identities at 1 to 2 MH/s, a card called integrated would sit off with no reason the owner can see)
false
}
/// One row of Windows' adapter list (Win32_VideoController), the part this app reads.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Adapter {
@ -186,6 +205,10 @@ pub fn vendor_of(name: &str) -> &'static str {
"amd"
} else if n.contains("apple") {
"apple"
} else if n.contains("intel") {
// Arc cards and Intel processor graphics alike (the kind tells them apart); the first Intel card is the B580
// on PC 1, 7 October 2026 (docs/plans/intel-arc.md)
"intel"
} else {
"other"
}
@ -440,6 +463,9 @@ impl ClDevice {
"nvidia"
} else if self.vendor.contains("Advanced Micro") || self.vendor.contains("AMD") || self.name.contains("Radeon") || self.name.contains("AMD") || self.name.to_ascii_lowercase().starts_with("gfx") {
"amd"
} else if self.vendor.contains("Intel") || self.name.contains("Intel") {
// "Intel(R) Corporation" on the "Intel(R) OpenCL Graphics" platform (the Arc driver's runtime)
"intel"
} else {
"other"
}
@ -809,6 +835,8 @@ mod tests {
// the Mac: detect() labels Apple silicon "apple" itself; the name rules do not call it integrated
assert!(!looks_integrated("Apple M5 Max"));
assert_eq!(vendor_of("Apple M5 Max"), "apple");
assert_eq!(vendor_of("Intel(R) Arc(TM) B580 Graphics"), "intel");
assert_eq!(vendor_of("Intel(R) UHD Graphics 770"), "intel");
assert_eq!(vendor_of("gfx1036"), "amd");
assert_eq!(vendor_of("AMD Radeon RX 9070 XT"), "amd");
assert_eq!(vendor_of("NVIDIA GeForce RTX 5090"), "nvidia");
@ -951,6 +979,39 @@ mod tests {
assert!(diff.removed.is_empty());
}
#[test]
/// The first Intel card (the B580 on PC 1, 7 October 2026, docs/plans/intel-arc.md): the Arc driver's OpenCL
/// platform lists it as "Intel(R) Arc(TM) B580 Graphics" under vendor "Intel(R) Corporation" (the line shape is
/// the worker's; the device name and vendor string are Intel's documented forms, approximate until the PC 1 job
/// prints them). It becomes a discrete 12 GB card, vendor intel, worker OpenCL, on with 8 identities; the
/// Arc-branded processor graphics stay integrated; the NVIDIA cards beside it are untouched.
#[test]
fn an_intel_arc_card_on_the_intel_platform_is_a_discrete_intel_card() {
let list = String::from(PC1_LIST)
+ " [4] Intel(R) Arc(TM) B580 Graphics | Intel(R) OpenCL Graphics (OpenCL 3.0 )\n GPU, vendor Intel(R) Corporation, driver 32.0.101.9034, OpenCL C 3.0, 160 compute units, 2850 MHz, pci 05:00.0\n global 12208 MiB, max alloc 4095 MiB, local 64 KiB, max work-group 1024, sub-group extension: cl_intel_subgroups\n";
let (devs, _) = parse_opencl_list(&list);
let arc = devs.iter().find(|d| d.name.contains("B580")).expect("the Arc line parses");
assert_eq!(arc.vendor_word(), "intel");
assert_eq!(arc.mem_mb, 12208);
assert_eq!(arc.bus, "05:00.0");
let d = assemble(pc1_inputs(&list));
let c = d.cards.iter().find(|c| c.vendor == "intel").expect("an intel card");
assert_eq!((c.name.as_str(), c.kind.as_str(), c.worker.as_str(), c.device.as_str(), c.enabled, c.identities), ("Intel(R) Arc(TM) B580 Graphics", "discrete", "OpenCL", "4", true, 8));
assert_eq!(c.key, "intel:Intel(R) Arc(TM) B580 Graphics");
assert_eq!(c.vram_mb, 12208);
assert!(!c.sweep_supported, "no cap through OpenCL");
assert!(c.sweep_note.contains("Intel Arc"), "{}", c.sweep_note);
assert!(d.listed(c), "the OpenCL list answered, so a vanished Arc can be called removed");
// the Arc names: cards discrete, processor graphics integrated
for card in ["Intel(R) Arc(TM) B580 Graphics", "Intel(R) Arc(TM) A770 Graphics", "Intel(R) Arc(TM) A380 Graphics", "Intel(R) Arc(TM) Pro A60 Graphics", "Intel(R) Arc(TM) B570 Graphics"] {
assert_eq!(classify_kind(card, None), "discrete", "{card}");
}
for igpu in ["Intel(R) Arc(TM) Graphics", "Intel(R) Arc(TM) 140V GPU (16GB)", "Intel(R) Arc(TM) 130V GPU", "Intel(R) Arc(TM) 140T GPU"] {
assert_eq!(classify_kind(igpu, None), "integrated", "{igpu}");
}
assert_eq!(classify_kind("Intel(R) UHD Graphics 770", None), "integrated");
}
#[test]
fn keys_number_identical_cards() {
let mut cards = vec![card(0, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "0"), card(1, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "1"), card(2, "gfx1201", "amd", "OpenCL", "", "2")];

View file

@ -439,6 +439,9 @@ pub fn unsupported_reason(vendor: &str, power_default_w: f64, device: &str) -> O
// Ember Tune (src/ember.rs, 5 October 2026): AMD is tuned through igneum-gpu-telemetry, Apple measures only;
// the tune itself says which at its start (the card row's note)
"apple" | "amd" => None,
// the first Intel card, the B580 on PC 1 (7 October 2026, docs/plans/intel-arc.md section 2.3): OpenCL
// exposes neither a cap nor a reading; Intel's path is IGCL (ControlLib.dll), the telemetry helper's next piece
"intel" => Some("not available: Intel Arc exposes no power cap or reading through OpenCL (the driver's IGCL counters come next)"),
_ => Some("not available: no power reading or cap for this card"),
}
}

View file

@ -17,7 +17,7 @@
:root{
/* colour, dark */
--obsidian:#0C0C0E;--graphite:#16161A;--row:#111114;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--ember-hi:#FF6A2B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;
--ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);--molten-40:rgba(255,179,92,.4);--nvidia:#8BE37A;--rail-bg:#111114;--hover:rgba(255,255,255,.04);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--scrim:rgba(12,12,14,.72);--ok:#FFB35C;
--ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);--molten-40:rgba(255,179,92,.4);--nvidia:#8BE37A;--intel:#7CC4FF;--rail-bg:#111114;--hover:rgba(255,255,255,.04);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--scrim:rgba(12,12,14,.72);--ok:#FFB35C;
/* type scale */
--t-xs:11px;--t-sm:12px;--t-base:13px;--t-md:14px;--t-lg:15px;--t-xl:17px;--t-num:24px;--t-hero:34px;--t-h2:28px;--t-h1:44px;
/* spacing scale */
@ -27,10 +27,10 @@
--top:60px;--bottom:0px;--drawer-h:260px;--rail:196px}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){
--obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;--ember:#E04A14;--ember-hi:#F2541B;--molten:#B8731F;--bone:#16161A;--ash:#6B6B70;--ink-2:#3C3C42;--ember-ink:#FFFFFF;
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}}
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--intel:#1C6FD6;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}}
:root[data-theme="light"]{
--obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;--ember:#E04A14;--ember-hi:#F2541B;--molten:#B8731F;--bone:#16161A;--ash:#6B6B70;--ink-2:#3C3C42;--ember-ink:#FFFFFF;
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--intel:#1C6FD6;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--sans);font-size:var(--t-md);line-height:1.5;-webkit-font-smoothing:antialiased;overflow:hidden;user-select:none;-webkit-user-select:none;font-variant-numeric:tabular-nums}
@ -191,6 +191,7 @@ body[data-phase="welcome"] #screen-welcome,body[data-phase="cards"] #screen-card
.badge.apple{color:var(--bone)}
.badge.nvidia{color:var(--nvidia)}
.badge.amd{color:var(--ember)}
.badge.intel{color:var(--intel)}
.gpu-row .info{flex:1;min-width:180px;display:flex;flex-direction:column;gap:var(--s-1)}
.gpu-row .name{font-family:var(--head);font-weight:700;font-size:var(--t-xl);line-height:1.2;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.kind{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;border-radius:999px;padding:2px 8px;border:1px solid var(--line-2);color:var(--ash);font-weight:500;white-space:nowrap}

View file

@ -333,6 +333,7 @@ var View = (function () {
if (cd.power_w > 0 || cd.temp_gpu > 0) return '';
if (cd.vendor === 'apple') return 'No power or temperature reading on Apple silicon';
if (cd.kind === 'integrated') return 'No power or temperature reading on an integrated GPU';
if (cd.vendor === 'intel') return 'No power or temperature reading on Intel Arc yet: the driver exposes them through IGCL, not OpenCL';
return '';
}
// the row's cells: rate, watts with MH/W, £ a day, temperature; a cell with no number is left out, never n/a
@ -559,6 +560,7 @@ var TuneLine = (function () {
// the model: {kind: running|tuned|measured|stopped|idle|off, text, note}
function model(cd, now) {
cd = cd || {};
if (cd.vendor === 'intel') return { kind: 'off', text: 'tuning: measure only on Intel Arc once a power reading exists', note: 'no cap through OpenCL; IGCL next' };
if (cd.vendor !== 'nvidia' && cd.vendor !== 'amd' && cd.vendor !== 'apple') return { kind: 'off', text: cd.sweep_note || 'tuning: no power or clock control for this card' };
if (cd.sweep_state === 'running') return { kind: 'running', text: cd.sweep_note || 'tuning: running' };
var when = cd.sweep_at && now ? ', ' + rel(now - cd.sweep_at) + ' ago' : '';
@ -1146,7 +1148,7 @@ if (typeof document !== 'undefined') (function () {
// ---------- render ----------
function badgeHtml(cd) {
if (cd.vendor === 'apple') return '<svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M16.4 12.6c0-2.5 2-3.6 2.1-3.7-1.2-1.7-3-1.9-3.6-2-1.5-.2-3 .9-3.8.9-.8 0-2-.9-3.3-.8-1.7 0-3.2 1-4.1 2.5-1.8 3-.5 7.6 1.3 10.1.9 1.2 1.9 2.6 3.2 2.5 1.3 0 1.8-.8 3.3-.8 1.6 0 2 .8 3.3.8 1.4 0 2.3-1.2 3.1-2.5 1-1.4 1.4-2.8 1.4-2.9 0 0-2.7-1-2.9-4.1zM13.9 5.3c.7-.8 1.2-2 1-3.2-1 0-2.2.7-2.9 1.5-.6.7-1.2 1.9-1 3 1.1.1 2.2-.5 2.9-1.3z"/></svg>';
return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : 'GPU';
return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : cd.vendor === 'intel' ? 'INTEL' : 'GPU';
}
function vramText(cd) { if (!cd.vram_mb) return ''; var gb = cd.vram_mb / 1024; return (gb >= 10 ? Math.round(gb) : gb.toFixed(gb < 4 ? 1 : 0)) + ' GB' + (cd.kind === 'apple' ? ' unified' : ''); }
// ---------- the first-run GPU rows: one per card, with a switch ----------
@ -1255,6 +1257,7 @@ if (typeof document !== 'undefined') (function () {
'<p class="help">Lower is cooler and quieter; most cards hash nearly as fast at 70%. ' + (cd.pinned ? 'Set by hand, so the tune leaves it. <button class="btn tiny ghost" data-pin="0">Unpin</button>' : 'Ember Tune may move it.') + '</p></div>';
h += cd.power_applied ? '<div class="line ok">cap applied: <b>' + want + ' W</b>' + (cd.sweep_pct === pct && cd.sweep_pct ? ' <span class="pin">chosen by the tune</span>' : '') + '</div>' : '<div class="line hot">cap not applied: Windows needs the administrator prompt <button class="btn tiny" data-power-retry="1">Retry</button></div>';
} else if (cd.vendor === 'apple') h += '<p class="help">Apple silicon manages its own power; there is no cap to set.</p>';
else if (cd.vendor === 'intel') h += '<p class="help">Intel Arc exposes no power cap through OpenCL; the card runs at the driver’s default limit (Intel Graphics Software can set one). No watts reading yet, so the MH/W cell stays empty until the IGCL reader lands.</p>';
var tl = [];
if (cd.temp_mem > 0) tl.push('memory <b>' + Math.round(cd.temp_mem) + ' °C</b>');
if (cd.gclk_mhz > 0) tl.push('core clock <b>' + Math.round(cd.gclk_mhz) + ' MHz</b>' + (cd.clock_cap_mhz > 0 ? ' (cap ' + cd.clock_cap_mhz + ')' : ''));

View file

@ -50,6 +50,23 @@ test('a GPU row: temperatures turn amber then red; unknown numbers are empty', (
assert.equal(apple.meta, '128 GB unified');
});
test('an Intel Arc card is a discrete Intel row: INTEL badge, no power reading sentence, measure-only tune words (intel-arc, 7 October 2026)', () => {
const arc = card({ key: 'intel:Intel(R) Arc(TM) B580 Graphics', name: 'Intel(R) Arc(TM) B580 Graphics', vendor: 'intel', kind: 'discrete', worker: 'OpenCL', vram_mb: 12208, enabled: true, state: 'mining', hash_now: 15.2, power_w: 0, temp_gpu: 0, sweep_supported: false, sweep_state: 'unsupported', sweep_note: 'not available: Intel Arc exposes no power cap or reading through OpenCL (the driver\'s IGCL counters come next)' });
const r = V.cardRow(arc);
assert.equal(r.vendor, 'intel');
assert.equal(r.kindWord, 'Discrete');
assert.equal(r.integrated, false);
assert.equal(r.on, true);
assert.ok(r.meta.startsWith('12 GB'), r.meta);
assert.equal(r.power, '', 'no watts cell without a reading');
assert.equal(V.noReading(arc), 'No power or temperature reading on Intel Arc yet: the driver exposes them through IGCL, not OpenCL');
assert.equal(V.tunable(arc), false, 'no cap, no clock control: not tunable');
assert.equal(V.proveTier(arc), 'Intel(R) Arc(TM) B580 Graphics: cannot prove yet (the prover is CUDA only).');
const tm = mod.exports.TuneLine.model(arc, 1000);
assert.equal(tm.kind, 'off');
assert.equal(tm.text, 'tuning: measure only on Intel Arc once a power reading exists');
});
test('an integrated GPU is shown as integrated and off, with its reason', () => {
const r = V.cardRow(card({ key: 'intel:1:UHD', name: 'Intel UHD Graphics 770', vendor: 'other', kind: 'integrated', enabled: false, state: 'off', hash_now: 0, reason: 'integrated GPU: slow and shares the machine memory', power_w: 0, temp_gpu: 0 }));
assert.equal(r.integrated, true);

115
docs/plans/intel-arc.md Normal file
View file

@ -0,0 +1,115 @@
# Intel Arc B580 on Igneum: the first Intel card (plan, 7 October 2026)
Branch `intel-arc` (worktree `../igneum-wt-intel-arc`, scratch prefix `ia-*`). the project lead, 7 October 2026, 13:4x UK: "the intel card has arrived ... it will go into pc1 and replace one of the cards, it is the b580". CLAUDE.md names Intel as a tier "when it exists"; it exists today. This page says which worker runs on the card first, what to expect, what can go wrong, and the fallback with its hours. Every number below is either cited or labelled approximate; the measured rows land in `docs/bench-log.md` when the PC 1 job runs.
## 1. The card
| Fact | Value | Source |
|---|---|---|
| Name, generation | Intel Arc B580, Battlemage (BMG-G21), Xe2 architecture | Intel's product page, approximate (not re-read today) |
| Memory | 12 GB GDDR6, 192-bit, 19 Gbps, 456 GB/s | the brief; Intel's specification, approximate |
| Compute | 20 Xe2 cores (160 vector engines, SIMD16 each), 2,670 MHz boost (approximate) | approximate |
| Board power | 190 W TBP, one 8-pin, two slots, PCIe 4.0 x8 | approximate |
| Cache | 18 MB L2 (approximate) | approximate |
| Driver for Windows | Intel Arc Graphics driver 32.0.101.9034 (non-WHQL, released 2 October 2026; 9033 is the WHQL one), `gfx_win_101.9034.exe`, 932,631,144 bytes, SHA512 `3fc6f9b4...3cf3f0` | Intel download page 785597, read 7 October 2026; the file is on PC 1 (job `run-ia-intel-driver-20261007`, SHA512 equal, Authenticode "CN=Intel Corporation") |
| Compute APIs in the driver | OpenCL 3.0 (platform "Intel(R) OpenCL Graphics", the Intel Graphics Compute Runtime with the IGC compiler), Level Zero, DirectX 12, Vulkan | Intel's runtime documentation, approximate |
PC 1 today (`ae432dc7`, Windows 11, the installed app 0.3.18): RTX 5090, RTX 4070, RX 9070 XT (eGPU), the Ryzen iGPU gfx1036. The B580 replaces one of the cards (the project lead's choice; the job reads what is present and never assumes).
## 2. Which worker first: the OpenCL worker, unchanged
The app drives NVIDIA by the CUDA worker, AMD by the OpenCL worker (`proto-opencl/host.c`, shipped as `igneum-worker-opencl.exe`, `OpenCL.dll` loaded at run time) and Apple by Metal. The Intel driver ships an OpenCL 3.0 platform, so the first run is the shipped OpenCL worker on Intel's runtime with nothing rebuilt: `detect.rs` already reads the worker's `--list` for "AMD, Intel" and `assemble` makes a card of every non-NVIDIA GPU device it prints. The 4 October United States laptop mined on an Intel UHD iGPU through this path at 1.46 MH/s (bench-log, 4 October 2026), so Intel's compiler has already built the kernel text once; the Arc is the first discrete Intel card.
What the installed 0.3.18 app will do by itself the moment the driver is in: list the Arc as vendor "other", kind discrete (the name carries "Arc"), 12 GB, enabled with 8 identities, and start an OpenCL worker on it with `--device <index> --pack packs\devnet`. Its ready line or its error is the first light; the PC 1 job reads it before the bench (section 5).
### 2.1 The expected ceiling
The hash is bound by dependent random 4-byte reads over the 1 GiB dataset, 128 per hash (bench-log, 5 October 2026, "the 9070 XT on the eGPU"; `docs/plans/read-width.md` section 1). The measured dependent-read rates at 1024 MiB:
| Card | Memory bandwidth | Dependent random loads at 1 GiB | Rate at 128 loads per hash | Watts | MH/W |
|---|---|---|---|---|---|
| RTX 5090 | 1,792 GB/s, 96 MB L2 | 16 to 18 G/s | 127 to 137 MH/s measured | 227 W tuned (Ember run 6) to 311 W | 0.41 to 0.56 |
| RX 9070 XT | 640 GB/s, 64 MB Infinity Cache | 2.42 to 2.68 G/s | 18.0 to 18.2 MH/s measured (87 to 95% of the ceiling) | 199 W | 0.09 |
| Apple M5 Max | 546 GB/s unified | 3.45 G/s | 23 to 28 MH/s measured | 38 W | 0.70 |
| Arc B580 (expected) | 456 GB/s, 18 MB L2 | 1.7 to 2.5 G/s (approximate: the 9070 XT's rate scaled by bandwidth gives 1.8; a 64-byte line per 4-byte read on both) | 13 to 20 MH/s, the job decides | 150 to 190 W under load (approximate) | 0.07 to 0.13 |
So the first-light expectation is "a 9070 XT-class rate at a 9070 XT-class efficiency", 13 to 20 MH/s, and the job's `--memprobe` row replaces the guess with the card's own dependent-load ceiling. A B580 sells for about a third of a 9070 XT (approximate, street prices), so the per-pound figure is the one to watch.
### 2.2 Memory: 12 GB
Mining needs about 3.4 GB resident (the 1 GiB dataset, the 256 MiB cache, the program and the context: `memory.used min 3,396 MiB` on the 5090 with the miner alone, bench-log 5 October). 12 GB holds that three times over. Proving is CUDA-only today (SP1's GPU prover; the app's words: "cannot prove yet (the prover is CUDA only)"), so an Arc owner mines only, by the prover's vendor and not by the 12 GB; and even on NVIDIA a 12 GB card cannot mine and prove at once on this build (15.6 GB peak, CLAUDE.md consequences rule). The row for a B580 owner therefore reads "mine only" twice over, and the prover question for Intel is a Level Zero or SYCL port of the prover, out of scope here.
### 2.3 The known Arc OpenCL gotchas, and where the worker already stands
| Gotcha | What it does to this kernel | Where the worker stands |
|---|---|---|
| Sub-group size: Xe2 executes SIMD16 natively; IGC picks a sub-group size of 8, 16 or 32 per kernel and `cl_intel_subgroups` lets the kernel require one (`intel_reqd_sub_group_size(32)`) | the worker's lane unit is 32 (one warp = one 32-lane work-group); a 16-wide sub-group means two sub-groups per unit, so the sub-group shuffle exchange is wrong at width 16 | `setupProgram` already handles it: it asks `clGetKernelSubGroupInfo` for the size at a 32-item work-group and falls back to the local-memory exchange unless it reads 32 (`exchange 2` = `intel_sub_group_shuffle_xor`, host.c line 508; the probe kernel `igneum_probe_subgroup` is the second check). The local-memory path is the one AMD runs (wave32, exchange 0) at no measured cost against sub-group shuffles (the 5090's OpenCL path is 4% under CUDA). First run: `--exchange auto`; a `--exchange local` comparison row in the same job |
| Local memory: 64 KB SLM per Xe core shared by the work-groups resident on it (approximate); a big local footprint cuts occupancy | the exchange buffer is small (32 lanes x a few words); the dataset and cache live in global memory | no change; the kernel-info line prints `local memory N bytes`, read in the report |
| Private memory and spills: IGC spills large per-lane state to scratch, and the `private memory` figure in the kernel query is the signal | the hash keeps its state in registers on AMD (`private memory 0`); a non-zero figure on Intel means spills, and the rate would show it | the job prints the kernel-info line; a spill is a finding, not a blocker |
| 64-bit integer multiply: Xe has no native 64-bit integer multiply (emulated from 32-bit, approximate 3 to 4x the cost of a 32-bit one) | the program's `mul`, `mulhi` and `mad` families are 64-bit; on a latency-bound hash this is in the shadow of the memory reads, and the shadow work of class v4 (`sh256x27`) is the one place the ALU budget could bind on a weak-ALU card | the job's class v4 row against the class v3 control shows whether the card holds its rate under the shadow; the 5 percent rule of Counter ASIC 2.0 is the test |
| The compiler: IGC compiles OpenCL C 1.2 and 2.0 fine; `-cl-std=CL3.0` with features the device lacks fails the build; generic address space and `cl_khr_fp64` are the usual failure sites; build logs are long | the kernel is C99-style OpenCL C 1.2 on the local-memory path (`-cl-std=CL1.2`), CL2.0 or CL3.0 only for the sub-group variant | if the sub-group variant fails to build, `setupProgram` already falls to local memory with the log printed ("the sub-group variant did not compile") |
| Compile time: IGC takes seconds for a kernel of this size (the UHD iGPU built packs in 3.0 to 6.4 s, bench-log M11) against 18 ms on Metal and 150 to 180 ms on NVRTC | the per-epoch compile-ahead window is 600 s (`docs/plans/epoch-length.md`); 6 s is 1 percent of it | fine; the job records `build <ms> clBuildProgram` for the Arc |
| Two Intel platforms: the driver may also expose a CPU OpenCL platform (Intel's CPU runtime, when installed) and, with an Intel iGPU present, the same "Intel(R) OpenCL Graphics" platform lists both GPUs | a CPU device would be a false card | `dedupe_platforms` keeps GPUs only (`is_gpu`); PC 1 has no Intel iGPU |
| Power: OpenCL exposes no power limit and no power reading; Intel's control path is IGCL (the Intel Graphics Control Library, `ControlLib.dll`, shipped with the driver: `ctlPowerTelemetryGet` gives an energy counter, clocks, temperature; `ctlPowerLimitsSet` sets the sustained limit, approximate names from the SDK) | the app's cap slider is NVIDIA's (nvidia-smi); AMD goes through ADLX in `igneum-gpu-telemetry` | the Cards row says so (section 3); an IGCL reader in the telemetry helper is the next piece, scoped in section 4 |
| Driver resets: an Intel display driver reset (TDR) kills the context; the worker exits and the miner restarts it (`WORKER FAULT`) | the app's watchdog path, already exercised on AMD | nothing new |
## 3. The app side (this branch)
| Piece | Change |
|---|---|
| Vendor word | `detect.rs`: `vendor_of` and `ClDevice::vendor_word` return `"intel"` for a vendor string or name carrying "Intel" (the OpenCL vendor string is "Intel(R) Corporation", the device name "Intel(R) Arc(TM) B580 Graphics"); the card's key becomes `intel:Intel(R) Arc(TM) B580 Graphics`. The jobs runner's `--cards-off` matches that key with or without the index |
| Kind | `classify_kind` already answers discrete for an Arc A-series or B-series name; the integrated guard now reads the Arc-branded iGPUs ("Intel(R) Arc(TM) Graphics" of Meteor Lake, "Arc(TM) 1xxV" of Lunar Lake, "Arc(TM) 1xxT" of Panther Lake, approximate names) as integrated and the "Arc(TM) A", "Arc(TM) B" and "Arc(TM) Pro" cards as discrete |
| Badge | the Cards row badge reads `INTEL` in the Intel blue token (`--intel`), light and dark |
| Defaults | unchanged: discrete, 12 GB, on with 8 identities |
| Power cap | `sweep.rs::unsupported_reason`: `"intel"` answers "not available: Intel Arc exposes no power cap or reading through OpenCL (the driver's IGCL counters come next)"; the row's details panel says "Intel Arc exposes no power cap through OpenCL; the card runs at the driver's default limit (Intel Graphics Software can set one). No watts reading yet, so the MH/W cell stays empty until the IGCL reader lands." The tune line says "tuning: measure only on Intel Arc once a power reading exists" |
| Tests | detect.rs: an Intel platform line parses to vendor `intel`, a B580 card named and keyed, Arc iGPU names integrated, Arc cards discrete; view.test.mjs: the Intel discrete row's words, badge and no-reading sentence; the mock scenario `intel` for the capture |
## 4. The fallback if OpenCL fails on Arc, scoped in hours
"Fails" means: the kernel does not build on IGC in either exchange mode, or it builds and the vectors mismatch, or it runs under 5 MH/s with the memprobe saying the card can do more. In that order the fallbacks are:
| Step | What | Hours (Claude side, per the project lead's rule: hours, not weeks) |
|---|---|---|
| F1: SPIR-V into the same OpenCL runtime | build the kernel text offline with clang (`-cl-std=CL1.2 -target spir64-unknown-unknown -emit-llvm` then `llvm-spirv`) on the box, feed it with `clCreateProgramWithIL`: this skips IGC's OpenCL C front end and keeps every other line of host.c | 2 to 3 hours plus one PC 1 job; needs clang and llvm-spirv on igneum-build-1 (apt) |
| F2: a Level Zero host | a second worker, `proto-l0/host.c`, mirroring host.c's serve loop (the job protocol, pack read, dataset and cache fill, the bound kernel, read-back) on `ze*` calls: `zeModuleCreate` with the SPIR-V from F1 (`ZE_MODULE_FORMAT_IL_SPIRV`), `zeCommandListAppendLaunchKernel`, events for timing; Level Zero's loader `ze_loader.dll` ships with the driver | 6 to 10 hours for the worker plus 2 for the app (a third worker kind beside CUDA and OpenCL in `detect.rs` and the launcher), one PC 1 job per round |
| F3: a SYCL (oneAPI) build | the kernel ported to SYCL and built with icpx on the box; the largest rewrite and the most toolchain | 12 to 16 hours; last resort |
| Not a fallback | a CUDA-to-Intel translation layer (ZLUDA and the like): foreign runtime, no provenance, never in the shipped app | none |
The decision rule: F1 first because it is the cheapest test of "is it IGC's front end"; F2 only if F1 also fails or the rate stays under half the memprobe ceiling (then the OpenCL runtime's queueing is the suspect and Level Zero's explicit command lists are the fix).
The telemetry piece (not a fallback, needed either way): an IGCL reader in `proto-opencl/gpu-telemetry.c` (load `ControlLib.dll` at run time, `ctlInit`, `ctlEnumerateDevices`, `ctlPowerTelemetryGet` per card; watts from the energy counter difference over the sample interval, clocks and temperature from the same call; the `intel <ordinal> ...` line beside the `amd` lines), 2 to 3 hours plus a PC 1 sample. Until it lands the bench row records watts as "not exposed" and names IGCL as the source to come.
## 5. The PC 1 job (written on this branch, published only on the project lead's go after the card is in)
`tools/intel-arc/pc1-arc-bench.ps1`, published as a signed `run` job with the RUNNER's `--cards-off <the Arc's key>` (never `--stop-miners`: the 5090 and whatever else is in keep mining; the script never posts to `api/cards`, `api/pause` or `api/quit`), after a small fetch job that places the class v4 pack `v4-devnet-epoch0` and the class v3 control `mx8-devnet-epoch0` (`tools/intel-arc/make-kit.sh`, packs only, no exe: the installed worker is the one under test). Steps and lines:
| Step | Line | What it settles |
|---|---|---|
| detect | `RESULT list ...` from the installed worker's `--list`; `RESULT device <n> intel ...`; the Windows adapter row (name, driver version, the class key's `qwMemorySize` = 12,288 MiB) | the card is enumerated by name with 12 GB on the Intel OpenCL platform, driver 32.0.101.9034 |
| first light | the installed app's own worker line for the card from its log (`ready ... exchange N ... sub-group`), read only | OpenCL compiled and self-tested on Arc inside the app, or the exact error |
| memprobe | `igneum-worker-opencl.exe --device <n> --memprobe` | the dependent-load ceiling at 4, 64 and 1024 MiB: the number that bounds every rate below |
| bench, class v4 | `--bench-pack --pack <kit>\packs\v4-devnet-epoch0 --batches 5 --batch-log2 24 --device <n>` (fingerprint expected `f410c731b6bc2d31`, the Mac's and the 5090's) | bit-exact on the fourth vendor, and the rate on the current class |
| bench, class v3 control | the same on `mx8-devnet-epoch0` (expected `90f794dd556f7a3b`) | whether the shadow work costs the Arc more than 5 percent (the 2.0 rule) |
| bench, the live pack | `--bench-pack --pack <app>\packs\devnet` | the rate on today's program as the app mines it |
| exchange | the class v4 pack again with `--exchange local` | the sub-group path against the local-memory path |
| watts and clocks | `not exposed` through OpenCL; the PDH GPU Engine counter gives busy percent only; the line names IGCL as the source to come | the row's watts cell honest |
| row | `RESULT ROW card="Intel Arc B580" mhs= ceiling_mhs= mhw=not-exposed blocks_per_day= hours_per_block= ...` and the consequences line | the bench-log row, the card-picker entry (`['Intel Arc B580', <mhs>]` in `site/yourcard.js`), the owner's sentence |
Time to a block: the network estimate was 675 MH/s at 09:34Z on 7 October (`live_state.hashes_per_second_estimate`); at 1 block/s a card at X MH/s expects X / 675 x 86,400 blocks a day, so 15 MH/s is 1.9 blocks a day (one every 12.5 h) at today's rate, and the subsidy is 31.69 IGN a block (spec 02 section 2.5). The job computes the line from the live number at run time.
## 6. Consequences per tier (the rule of 5 October 2026), before the measurement
| Tier | What the Arc means | What this branch does |
|---|---|---|
| Home miner, one 12 GB Intel card (the B580 owner) | mines on the OpenCL worker from the app's first run after the driver; expected 13 to 20 MH/s (section 2.1, approximate until measured), 1.7 to 2.6 blocks a day at today's network; no proving (CUDA only); no power cap and no watts reading in the app until IGCL lands, so the £ a day cell stays empty | the job's row replaces the estimate; the IGCL reader is the next 2 to 3 hours |
| Home miner, 8 GB Intel (Arc A750, B570 10 GB) | the same path; 3.4 GB resident fits; rate lower by the card's dependent-read rate | the A-series is not in hand; the row is owed |
| 16 GB Intel (Arc A770 16 GB, B770 if it ships) | the same; still mine only | owed |
| Rig | several Arcs on one OpenCL platform: one worker per device index, as AMD; the PCIe 4.0 x8 link is no cost (the eGPU's USB4 cost 6 percent per job and was removed by the select read-back) | nothing new |
| Pool user | sees nothing different | nothing |
| Windows | this path | this branch |
| Linux and HiveOS | Intel's compute runtime is a distro package (`intel-opencl-icd`), the same worker binary | a line in the install notes once the Windows row exists |
| macOS | no Intel GPUs | nothing |
## 7. What is not done here
The IGCL telemetry reader (section 4), an Intel prover, the Linux package line, the driver-check feature (queued: per-vendor minimum-driver table in the manifest and one-click install, branch `driver-check` off the miner-ui-5 tip), and the site's bench table row, which takes the measured number only.

39
tools/intel-arc/README.md Normal file
View file

@ -0,0 +1,39 @@
# Intel Arc B580 on PC 1: the jobs (7 October 2026, branch intel-arc)
Plan: `docs/plans/intel-arc.md`. PC 1 (`ae432dc7`) is the project lead's desk: nothing here runs without his go for that run; every job is a signed `run` job through `packaging/ota/publish-jobs.sh` from the main checkout (its signer binary lives there), never pausing, resuming or quitting the installed app, never posting to `api/cards` (the RUNNER's `--cards-off` takes a card alone and restores it on any exit; `tools/ci/playbook-quit-check.sh`).
## 1. The driver (ran 7 October 2026)
`pc1-intel-driver.ps1`, elevated (one UAC prompt, the project lead's click), no `--stop-miners`. Downloads `gfx_win_101.9034.exe` from `downloadmirror.intel.com/929959/` (932,631,144 bytes), checks the SHA512 Intel prints on page 785597 and the Authenticode signature, runs the installer with `-s` alone (silent, no `-b`: never reboots by itself), reads Intel's own logs under `C:\ProgramData\Intel\GFXInstaller`, then every adapter row, the PnP display devices, the class key's memory size and the installed worker's `--list`.
| Run | Published | Outcome |
|---|---|---|
| `run-ia-intel-driver-20261007` | 09:25:20Z | download 10 s, SHA512 equal, Authenticode "CN=Intel Corporation"; installer exit 1007 after 17 s: the bootstrapper wrapping the inner `Installer.exe`'s exit 7 ("unsupported parameter", my `-report <path>`); no device check ran; the .NET Desktop Runtime it needs was installed (exit 0) |
| `run-ia-intel-driver-20261007-b` | 09:43:59Z, the card in | `-s` alone; the report's lines are in the bench log entry |
packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ia-intel-driver-<date>-<x> --elevated \
--script tools/intel-arc/pc1-intel-driver.ps1 --timeout-minutes 60 --title "Intel Arc driver 32.0.101.9034 on PC 1" --deploy
## 2. The bench (published only on the project lead's go, after the card is in and the driver is on)
The kit first (packs only; the installed worker is the one under test):
tools/intel-arc/make-kit.sh "$TMPDIR/igneum-ia-arc-packs.zip" # prints sha256 and bytes
packaging/ota/publish-jobs.sh add --kind fetch --target ae432dc7 --id fetch-ia-arc-packs-20261007 \
--file "$TMPDIR/igneum-ia-arc-packs.zip" --dir jobs --extract --title "Intel Arc bench packs (class v4 and the v3 control)" --deploy
Then the bench, with the live network rate substituted (the consequences line's blocks a day) and the Arc's key for the runner's `--cards-off` (read it from the app's Cards page or `api/state`: on the 0.3.18 app the Arc is `other:Intel(R) Arc(TM) B580 Graphics`, on an app that carries this branch `intel:Intel(R) Arc(TM) B580 Graphics`; the runner matches the key with or without the device index, and both can be named):
NET=$(node -e "..." ) # live_state.hashes_per_second_estimate / 1e6, or read it from the observer (docs/plans/intel-arc.md section 5: 675 at 09:34Z on 7 October)
sed "s/__NETWORK_MHS__/$NET/" tools/intel-arc/pc1-arc-bench.ps1 > "$TMPDIR/ia-pc1-arc-bench.ps1"
packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ia-arc-bench-20261007 \
--cards-off "other:Intel(R) Arc(TM) B580 Graphics,intel:Intel(R) Arc(TM) B580 Graphics" \
--script "$TMPDIR/ia-pc1-arc-bench.ps1" --timeout-minutes 30 --title "Intel Arc B580: first light, memprobe, class v4 and v3 benches" --deploy
What it prints (`node tools/jobs.mjs run-ia-arc-bench-20261007`): `RESULT list ...` (the Intel OpenCL platform line), `RESULT device <n> name= global_mib=`, `RESULT app_card key= vendor= kind= state=` (read only), `RESULT firstlight ...` (the app's own worker line for the card), `RESULT memprobe | chase | 1024 | ...` (the dependent-load ceiling), `RESULT G1 pack=v4-devnet-epoch0 fingerprint= match=yes|no` (bit-exact on the fourth vendor), the same for the v3 control, the live pack and the `--exchange local` row, `RESULT watts not-exposed source=IGCL...`, then `RESULT ROW ...`, `RESULT PICKER ['Intel Arc B580', <mhs>]` (the `site/yourcard.js` entry, measured so it may go on the site) and `RESULT CONSEQUENCES ...` (the B580 owner's sentence), and a `SUMMARY {json}` line. About 3 to 5 minutes: the card switch up to 150 s, the memprobe under a minute, four benches of 5 dispatches of 2^24 (about 1 s each at 15 MH/s, plus IGC's compile of a few seconds per pack).
If OpenCL fails on the Arc the `RESULT G1 ... error=` and `RESULT buildlog ...` lines carry the compiler's exact words; the fallback order and hours are in the plan's section 4.
## 3. Dry run
The playbooks are PowerShell; this Mac has no `pwsh` (the Homebrew cask is gone from the tap and the Command Line Tools are too old for a cask install today), so the Mac-side dry run against the UI mock (`node tools/ui-mock/server.mjs 4310` with `?scenario=intel`, whose `api/state` carries the Intel row) is owed until a PowerShell is on the Mac (Microsoft's pkg, the project lead's word for the download). What stands in: the driver playbook ran live on PC 1 twice with the same helper shapes (Stamp, Summary, the adapter and worker-list readers), and the four CI checks pass on both scripts (`kit-path-check`, `ps-drive-ref-check`, `kill-by-name-check`, `playbook-quit-check`).

View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# The packs kit for tools/intel-arc/pc1-arc-bench.ps1 (7 October 2026, docs/plans/intel-arc.md section 5): the class v4
# pack v4-devnet-epoch0 (the current class, fingerprint f410c731b6bc2d31 at 2^24) and the class v3 control
# mx8-devnet-epoch0 (90f794dd556f7a3b), both from proto-cuda/packs-ca3-v4 as the 6 October gate run exported them, plus
# SHA256SUMS. Packs only: the INSTALLED igneum-worker-opencl.exe on PC 1 is the worker under test, so no exe travels.
# tools/intel-arc/make-kit.sh [out.zip] default $TMPDIR/igneum-ia-arc-packs.zip
# Prints the zip's sha256 and size: the README's fetch line takes them.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
OUT="${1:-${TMPDIR:-/tmp}/igneum-ia-arc-packs.zip}"
STAGE="$(mktemp -d)"
mkdir -p "$STAGE/packs"
for p in mx8-devnet-epoch0 v4-devnet-epoch0; do
src="$ROOT/proto-cuda/packs-ca3-v4/$p"
[ -f "$src/kernel_bound.cl" ] || { echo "pack $p has no kernel_bound.cl at $src" >&2; exit 1; }
mkdir -p "$STAGE/packs/$p"
cp "$src"/* "$STAGE/packs/$p/"
done
( cd "$STAGE" && find . -type f ! -name SHA256SUMS | sort | xargs shasum -a 256 > SHA256SUMS )
rm -f "$OUT"
( cd "$STAGE" && zip -X -q -r "$OUT" . )
rm -rf "$STAGE"
echo "kit $OUT"
echo "sha256 $(shasum -a 256 "$OUT" | cut -c1-64) bytes $(wc -c < "$OUT" | tr -d ' ')"

View file

@ -0,0 +1,150 @@
# Igneum run job: the first Intel card, the Arc B580 on PC 1 (machine ae432dc7), 7 October 2026 (branch intel-arc,
# docs/plans/intel-arc.md section 5). NOT published until the project lead says the card is in and gives the go.
# Published as a signed `run` job with the RUNNER's --cards-off <the Arc's key> (NOT --stop-miners): the runner switches
# the Arc off through the app's own card path before this script and puts it back on ANY exit; every other card keeps
# mining. This script never posts to api/cards, api/pause, api/resume or api/quit (tools/ci/playbook-quit-check.sh); it
# reads api/state once for the record. It never quits, pauses or updates the installed app and writes no settings.
# After the fetch job that places the two packs (tools/intel-arc/make-kit.sh: v4-devnet-epoch0 = class v4, the current
# class; mx8-devnet-epoch0 = the class v3 control; packs only, the INSTALLED worker is the one under test).
# Steps: detect (the worker's --list, the Windows adapter and class-key rows: name, driver, 12 GB), first light (the
# app's own worker line for the card from its log, read only), --memprobe (the dependent-load ceiling), --bench-pack on
# class v4 (fingerprint f410c731b6bc2d31 expected), on the class v3 control (90f794dd556f7a3b), on the app's live pack,
# then class v4 again with --exchange local; watts and clocks: not exposed through OpenCL (IGCL named as the source to
# come); the row, the card-picker entry and the owner's consequences line from the live network rate.
# Lines start with RESULT; a SUMMARY {json} line ends the job. Read back with `node tools/jobs.mjs <job id>`.
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-arc-bench'
$kitId = 'fetch-ia-arc-packs-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)
}
$expected = @{ 'v4-devnet-epoch0' = 'f410c731b6bc2d31'; 'mx8-devnet-epoch0' = '90f794dd556f7a3b' } # bench-log, 6 October 2026, the G1 table (Metal = Apple OpenCL = RTX 5090)
$batches = 5
$networkMhs = [double] '__NETWORK_MHS__' # the publisher substitutes live_state.hashes_per_second_estimate / 1e6 (README); 0 = unknown, the line says so
if ($networkMhs -isnot [double] -or [double]::IsNaN($networkMhs)) { $networkMhs = 0 }
$subsidyIgn = 31.69 # IGN a block, spec 02 section 2.5 (BASE_SUBSIDY_PER_SECOND_SOMPI, 1 block a second)
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION network_mhs=$networkMhs"
$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 }
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-intel-arc' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
$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' }
$exe = $null
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $exe = Join-Path $inst 'igneum-worker-opencl.exe' }
if (-not $exe) { "RESULT error no installed igneum-worker-opencl.exe under $inst"; Summary 'failed' @{ error = 'no worker' }; exit 2 }
"RESULT app install=$inst app_dir=$appDir worker=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower())"
# ---- detect: Windows' rows for the Intel device, then the worker's own list ----
try {
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) { "RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)" }
} catch { "RESULT adapter error=$($_.Exception.Message)" }
try {
# the class key carries the true memory size (the 32-bit AdapterRAM caps at 4 GB); readable without elevation on PC 1? the driver job's run said no: printed when it answers, else the worker's global MiB is the size
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
if ($p -and $p.DriverDesc -match 'Intel') { $mem = '-'; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) memory=$mem" }
}
} catch { "RESULT class not_readable=$($_.Exception.Message)" }
$list = @(& $exe --list 2>&1 | ForEach-Object { "$_" })
$list | ForEach-Object { "RESULT list $_" }
$dev = $null; $devName = ''
for ($i = 0; $i -lt $list.Count; $i++) {
$l = $list[$i]
if ($l -match '^\s*\[(\d+)\]\s+(.*?)\s+\|' -and $l -match 'Intel' -and $l -notmatch 'dup') {
# the next line says GPU or CPU: Intel's CPU runtime, when present, lists a CPU device on its own platform
$next = if ($i + 1 -lt $list.Count) { $list[$i + 1] } else { '' }
if ($next -match '^\s*GPU') { $dev = [int]$Matches[1]; $devName = $Matches[2]; break }
if ($l -match '^\s*\[(\d+)\]\s+(.*?)\s+\|') { $dev = [int]$Matches[1]; $devName = $Matches[2]; break }
}
}
if ($null -eq $dev) { "RESULT error no Intel GPU device in --list (is the driver 32.0.101.9034 in and the card seen by Windows? the adapter rows above say)"; Summary 'failed' @{ error = 'no intel device' }; exit 2 }
$globalMib = ''
for ($j = $i; $j -lt [math]::Min($list.Count, $i + 4); $j++) { if ($list[$j] -match 'global (\d+) MiB') { $globalMib = $Matches[1]; break } }
"RESULT device $dev name=`"$devName`" global_mib=$globalMib"
# READ ONLY: the app's view of the card (its key is what the publisher's --cards-off named; the runner, not this script, switched it)
$cardKey = ''
try {
$urlFile = Join-Path $appDir 'app.url'
if (Test-Path $urlFile) {
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
foreach ($c in @($all | Where-Object { $_.name -match 'Intel' })) { $cardKey = $c.key; "RESULT app_card key=$($c.key) vendor=$($c.vendor) kind=$($c.kind) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) message=`"$($c.message)`"" }
}
} catch { "RESULT app_card error=$($_.Exception.Message)" }
# first light, read only: the app's log lines for the Intel worker (ready, exchange, sub-group, or its error)
try {
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
$newest = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($newest) { Get-Content -LiteralPath $newest.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'Intel|intel:|--device ' + $dev + '(\s|$)' } | Select-Object -Last 12 | ForEach-Object { "RESULT firstlight $_" } }
} catch { "RESULT firstlight error=$($_.Exception.Message)" }
# the card quiet? read-only, by the process list: the runner's --cards-off took, or not
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 }) }
$cardAlone = $false
$t = 0
while ($t -lt 150) { if ((@(CardWorkers)).Count -eq 0) { $cardAlone = $true; break }; Start-Sleep -Seconds 5; $t += 5 }
if ($cardAlone) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)" } else { "RESULT card $(Stamp) UNCONFIRMED after $t s: an opencl worker still carries --device $dev (was the job published with --cards-off $cardKey ?); the rows below are LOADED-card figures" }
# ---- memprobe: the dependent-load ceiling ----
"RESULT memprobe start $(Stamp)"
$mp = @(& $exe --device $dev --memprobe 2>&1 | ForEach-Object { "$_" })
$mp | Where-Object { $_ -match '^memprobe|^\|' -and $_ -notmatch '^\|---' } | ForEach-Object { "RESULT memprobe $_" }
$ceilingGl = 0.0
foreach ($l in $mp) { if ($l -match '^\|\s*chase\s*\|\s*1024\s*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|\s*([\d.]+)\s*\|') { $v = [double]$Matches[1]; if ($v -gt $ceilingGl) { $ceilingGl = $v } } }
$ceilingMhs = if ($ceilingGl -gt 0) { [math]::Round($ceilingGl * 1000 / 128, 2) } else { 0 }
"RESULT memprobe end $(Stamp) chase_1024mib_gloads_s=$ceilingGl ceiling_mhs_at_128_loads=$ceilingMhs"
# ---- the benches ----
function Bench([string] $label, [string] $packDir, [string[]] $extra) {
if (-not (Test-Path $packDir)) { "RESULT bench $label error=pack missing at $packDir"; return $null }
$t0 = (Get-Date).ToUniversalTime()
"RESULT bench $label start $(Stamp) pack=$packDir batches=$batches extra=$($extra -join ' ')"
$lines = @(& $exe --bench-pack --pack $packDir --batches $batches --batch-log2 24 --device $dev @extra 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
$lines | Where-Object { $_ -match '^RESULT|^pack |^warm-up|^build |^kernel:|^exchange|sub-group|FAIL|error|MISMATCH|spill|private memory' } | Select-Object -First 30 | ForEach-Object { "RESULT bench $label $_" }
"RESULT bench $label end $(Stamp) exit=$rc wall_s=$([int]((Get-Date).ToUniversalTime() - $t0).TotalSeconds)"
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 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=$label error=$($err -replace '\s+', ' ')"
# the build log, when the compiler refused the kernel: the exact error for the fallback decision (plan section 4)
$lines | Where-Object { $_ -match 'error|warning|catastrophic|undefined|spir' } | Select-Object -First 20 | ForEach-Object { "RESULT buildlog $label $_" }
return $null
}
$fp = ''; $mhs = ''; $check = ''
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
if ($res -match 'check=(\S+)') { $check = $Matches[1] }
$want = $expected[$label]
$match = if ($want) { if ($fp -eq $want) { 'yes' } else { 'no' } } else { 'n/a' }
"RESULT G1 pack=$label fingerprint=$fp match=$match expected=$want check=$check card=intel-arc harness=opencl-installed"
return [pscustomobject]@{ label = $label; mhs = [double]$mhs; fp = $fp; match = $match; check = $check }
}
$v4 = Bench 'v4-devnet-epoch0' (Join-Path $kit 'packs\v4-devnet-epoch0') @()
$v3 = Bench 'mx8-devnet-epoch0' (Join-Path $kit 'packs\mx8-devnet-epoch0') @()
$live = Bench 'live-devnet' (Join-Path $appDir 'packs\devnet') @()
$v4local = Bench 'v4-local-exchange' (Join-Path $kit 'packs\v4-devnet-epoch0') @('--exchange', 'local')
# watts and clocks: OpenCL exposes neither; the PDH GPU Engine counter gives busy percent only (named, not a watt)
"RESULT watts not-exposed source=IGCL(ctlPowerTelemetryGet, ControlLib.dll) to come; clocks not-exposed; the helper igneum-gpu-telemetry reads AMD (ADLX) only today"
# ---- the row and the consequences ----
$rate = if ($v4) { $v4.mhs } elseif ($live) { $live.mhs } else { 0 }
$blocksDay = if ($networkMhs -gt 0 -and $rate -gt 0) { [math]::Round($rate / $networkMhs * 86400, 2) } else { 0 }
$hoursBlock = if ($blocksDay -gt 0) { [math]::Round(24 / $blocksDay, 1) } else { 0 }
$ignDay = [math]::Round($blocksDay * $subsidyIgn, 1)
$shadowCost = if ($v4 -and $v3 -and $v3.mhs -gt 0) { [math]::Round((1 - $v4.mhs / $v3.mhs) * 100, 1) } else { 'owed' }
$share = if ($ceilingMhs -gt 0 -and $rate -gt 0) { [math]::Round($rate / $ceilingMhs, 2) } else { 'owed' }
"RESULT ROW card=`"Intel Arc B580`" driver=32.0.101.9034 device=$dev global_mib=$globalMib class_v4_mhs=$(if ($v4) { $v4.mhs } else { 'failed' }) class_v3_mhs=$(if ($v3) { $v3.mhs } else { 'failed' }) live_mhs=$(if ($live) { $live.mhs } else { 'failed' }) local_exchange_mhs=$(if ($v4local) { $v4local.mhs } else { 'failed' }) ceiling_mhs=$ceilingMhs latency_share=$share shadow_cost_pct=$shadowCost watts=not-exposed mhw=not-exposed network_mhs=$networkMhs blocks_per_day=$blocksDay hours_per_block=$hoursBlock ign_per_day=$ignDay card_alone=$cardAlone"
"RESULT PICKER ['Intel Arc B580', $rate] (site/yourcard.js CARDS entry; measured, so it may go on the site)"
$prove = 'mine only: the prover is CUDA only, and even a 12 GB NVIDIA card cannot mine and prove at once on this build'
"RESULT CONSEQUENCES a B580 owner (12 GB, Windows): $rate MH/s on the OpenCL worker, $blocksDay blocks a day at today's $networkMhs MH/s network (one every $hoursBlock h, about $ignDay IGN a day at $subsidyIgn IGN a block, approximate: the network rate moves); hash per watt not measurable yet (no power reading through OpenCL; IGCL reader next, 2 to 3 hours); $prove; 8 GB and 16 GB Intel rows owed (no card in hand); Linux and HiveOS take the same worker on intel-opencl-icd (owed a line)"
$status = if ($v4 -and $v4.match -eq 'yes') { 'done' } elseif ($v4 -or $live) { 'partial' } else { 'failed' }
"RESULT end $(Stamp)"
Summary $status @{ device = $dev; name = $devName; rate_mhs = $rate; ceiling_mhs = $ceilingMhs; v4_match = $(if ($v4) { $v4.match } else { 'failed' }); v3_match = $(if ($v3) { $v3.match } else { 'failed' }); blocks_per_day = $blocksDay; card_alone = $cardAlone; watts = 'not-exposed' }
if ($status -eq 'failed') { exit 1 }
exit 0

View file

@ -0,0 +1,136 @@
# Igneum run job: the Intel Arc graphics driver on PC 1 (machine ae432dc7), 7 October 2026 (branch intel-arc,
# docs/plans/intel-arc.md). the project lead's word: "can you install drivers to pc1"; the B580 goes into PC 1 today.
# Published as a signed `run` job with --elevated and WITHOUT --stop-miners: the RUNNER raises the one elevation prompt
# (Start-Process -Verb RunAs) at the script's start, the project lead clicks it at the desk, and everything below runs under it:
# the download, the hash check and the installer, with no second prompt. The installed app, its node and its miners
# are never quit, paused, resumed or switched (no api/* request in this script); the Intel installer touches only
# the Intel display device, never the NVIDIA or AMD driver, and runs with -s (silent) and no -b, so it never reboots
# on its own; exit 14 = "reboot required", reported and left to the project lead. Lines start with RESULT so
# `node tools/jobs.mjs <job id>` shows them; a SUMMARY {json} line ends the job.
$ErrorActionPreference = 'Continue'
$jobName = 'pc1-intel-driver'
$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)
}
# Intel's download page 785597 (read 7 October 2026): version 32.0.101.9034, released 2 October 2026 (non-WHQL; 9033
# is the WHQL one), file gfx_win_101.9034.exe, 889.4 MB, mirror folder 929959; the SHA512 is the string the page prints.
$version = '32.0.101.9034'
$url = 'https://downloadmirror.intel.com/929959/gfx_win_101.9034.exe'
$expectedBytes = 932631144
$expectedSha512 = '3fc6f9b4c8ef40bf066f1ad77f3c6aea55d47bae6dc8ab9575a7a9fe8eee84bf0c79333f81825cd7538ea1bb7ca78b6ddb0b75f96b321b4dcf524dec843cf3f0'
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-intel-driver' }
New-Item -ItemType Directory -Force -Path $job | Out-Null
$exe = Join-Path $job 'gfx_win_101.9034.exe'
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
$elevated = (New-Object Security.Principal.WindowsPrincipal($id)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION user=$($id.Name) elevated=$elevated version_wanted=$version"
if (-not $elevated) { "RESULT error not elevated: the installer needs administrator rights (publish with --elevated)"; Summary 'failed' @{ error = 'not elevated' }; exit 2 }
# ---- what Windows sees before: every display adapter, the Intel ones by PnP, the OpenCL platforms the worker lists ----
function Adapters([string] $tag) {
try {
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
"RESULT adapter_$tag name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID) ram32=$($a.AdapterRAM)"
}
} catch { "RESULT adapter_$tag error=$($_.Exception.Message)" }
# the 32-bit AdapterRAM field caps at 4 GB: the true size is the class key's qwMemorySize
try {
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
if ($p -and $p.DriverDesc) { $mem = ''; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class_$tag key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) provider=`"$($p.ProviderName)`" memory=$mem" }
}
} catch { "RESULT class_$tag error=$($_.Exception.Message)" }
try {
foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) { "RESULT pnp_$tag name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) id=$($d.InstanceId)" }
} catch { "RESULT pnp_$tag error=$($_.Exception.Message)" }
}
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
$worker = $null
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
"RESULT app install=$inst worker=$worker"
function OpenCLList([string] $tag) {
if (-not $worker) { "RESULT opencl_$tag error=no installed igneum-worker-opencl.exe to list with"; return @() }
$lines = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
foreach ($l in $lines) { "RESULT opencl_$tag $l" }
return $lines
}
Adapters 'before'
$clBefore = OpenCLList 'before'
$intelBefore = @($clBefore | Where-Object { $_ -match 'Intel' }).Count
"RESULT opencl_before intel_lines=$intelBefore"
# ---- the download: curl.exe (shipped with Windows 10 1803 and later), the size and the SHA512 Intel publishes ----
$dl = $null
if (Test-Path -LiteralPath $exe) {
$len = (Get-Item -LiteralPath $exe).Length
if ($len -eq $expectedBytes) { "RESULT download reuse $exe bytes=$len (already here)"; $dl = $true } else { Remove-Item -LiteralPath $exe -Force -ErrorAction SilentlyContinue }
}
if (-not $dl) {
$curl = Join-Path $env:SystemRoot 'System32\curl.exe'
if (-not (Test-Path -LiteralPath $curl)) { "RESULT error no curl.exe at $curl"; Summary 'failed' @{ error = 'no curl' }; exit 2 }
$t0 = Get-Date
"RESULT download start $(Stamp) url=$url expected_bytes=$expectedBytes"
$attempt = 0
while ($attempt -lt 3) {
$attempt++
$out = @(& $curl -sS -L --retry 3 --retry-delay 5 --connect-timeout 30 --max-time 1500 -o $exe $url 2>&1 | ForEach-Object { "$_" })
$rc = $LASTEXITCODE
$len = 0; if (Test-Path -LiteralPath $exe) { $len = (Get-Item -LiteralPath $exe).Length }
"RESULT download attempt=$attempt curl_exit=$rc bytes=$len wall_s=$([int]((Get-Date) - $t0).TotalSeconds) $($out -join ' ')"
if ($rc -eq 0 -and $len -eq $expectedBytes) { $dl = $true; break }
Start-Sleep -Seconds 10
}
if (-not $dl) { "RESULT error download did not reach $expectedBytes bytes in 3 attempts"; Summary 'failed' @{ error = 'download' }; exit 2 }
}
$sha = (Get-FileHash -Algorithm SHA512 -LiteralPath $exe).Hash.ToLower()
$sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $exe).Hash.ToLower()
"RESULT file $exe bytes=$((Get-Item -LiteralPath $exe).Length) sha512=$sha sha256=$sha256"
if ($sha -ne $expectedSha512) { "RESULT error sha512 mismatch: Intel publishes $expectedSha512; the installer is NOT run"; Summary 'failed' @{ error = 'sha512 mismatch'; sha512 = $sha }; exit 2 }
"RESULT sha512 ok (equal to the string on Intel's page 785597)"
try { $sig = Get-AuthenticodeSignature -LiteralPath $exe; "RESULT authenticode status=$($sig.Status) signer=`"$($sig.SignerCertificate.Subject)`"" } catch { "RESULT authenticode error=$($_.Exception.Message)" }
# ---- the installer: -s silent, no -b (no reboot by the installer); its exit code says what happened ----
# Intel's codes (support article 000098195, read 7 October 2026): 0 success, 1 failed, 2 the installer rebooted the
# system itself (-b only), 8 no driver for this device, 13 reboot required BEFORE setup, 14 reboot required after.
$meaning = @{ 0 = 'installed, no reboot asked'; 1 = 'installer failed (general)'; 2 = 'the installer rebooted the system'; 5 = 'the OS or a device on this platform is not supported by the driver (no supported Intel GPU present?)'; 6 = 'the installer was closed before it finished'; 7 = 'unsupported command-line parameter'; 8 = 'no INF files found in the bundle'; 9 = 'driver files without an Intel signature'; 11 = 'no free space'; 13 = 'a reboot is required BEFORE this driver can install'; 14 = 'installed; a reboot is required to apply it (NOT taken: the project lead decides)'; 1007 = 'the bootstrapper wrapped an inner Installer.exe exit 7 (unsupported parameter)'; 1014 = 'installed; a reboot is required to apply it (the bootstrapper wrapping the inner 14; NOT taken: the project lead decides)' }
# Run 1 (09:27Z): `-s -report <path>` made the inner Installer.exe exit 7 ("unsupported parameter") and the bootstrapper
# answer 1007 before any device check (its log: C:\ProgramData\Intel\GFXInstaller\Bootstrapper\IntelGFX_Bootstrapper_*.log).
# So: -s alone. The bootstrapper also installs the .NET Desktop Runtime it needs (it did on run 1, exit 0).
$t1 = Get-Date
"RESULT install start $(Stamp) args=-s (silent, no -b); the screen may blink while the Intel device resets; NVIDIA and AMD drivers are not touched"
$p = $null
try { $p = Start-Process -FilePath $exe -ArgumentList @('-s') -Wait -PassThru -WindowStyle Hidden -ErrorAction Stop } catch { "RESULT error installer launch: $($_.Exception.Message)"; Summary 'failed' @{ error = 'launch' }; exit 2 }
$code = $p.ExitCode
$word = if ($meaning.ContainsKey([int]$code)) { $meaning[[int]$code] } else { 'unknown code' }
"RESULT install end $(Stamp) exit=$code meaning=`"$word`" wall_s=$([int]((Get-Date) - $t1).TotalSeconds)"
# the installer's own logs: the bootstrapper's under GFXInstaller\Bootstrapper, the inner installer's beside it (read 7 October 2026)
$logRoot = Join-Path $env:ProgramData 'Intel\GFXInstaller'
if (Test-Path -LiteralPath $logRoot) {
foreach ($lf in @(Get-ChildItem -LiteralPath $logRoot -Recurse -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $t1.AddMinutes(-1) } | Sort-Object LastWriteTime)) {
"RESULT log $($lf.FullName) bytes=$($lf.Length)"
Get-Content -LiteralPath $lf.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'exit|error|fail|device|not supported|reboot|restart|install' } | Select-Object -Last 25 | ForEach-Object { "RESULT log $_" }
}
} else { "RESULT log none under $logRoot" }
# ---- what Windows sees after: the Intel device by name and version, its memory, the OpenCL platform ----
Start-Sleep -Seconds 10
Adapters 'after'
$clAfter = OpenCLList 'after'
$intelAfter = @($clAfter | Where-Object { $_ -match 'Intel' })
"RESULT opencl_after intel_lines=$($intelAfter.Count)"
$found = @()
try { $found = @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | ForEach-Object { "$($_.Name) driver $($_.DriverVersion) status $($_.Status) code $($_.ConfigManagerErrorCode)" }) } catch { }
foreach ($f in $found) { "RESULT intel_device $f" }
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($version) }).Count -gt 0
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } } else { "RESULT clinfo not installed (the worker's --list above is the enumeration)" }
$reboot = ($code -eq 14 -or $code -eq 13 -or $code -eq 1014)
$status = if ($code -eq 0 -or $code -eq 14 -or $code -eq 1014) { 'done' } else { 'failed' }
"RESULT end $(Stamp) version_installed=$versionOk reboot_required=$reboot intel_opencl_platform=$($intelAfter.Count -gt 0)"
Summary $status @{ version = $version; installer_exit = $code; meaning = $word; reboot_required = $reboot; sha512 = $sha; sha256 = $sha256; intel_devices = $found; intel_opencl_lines = $intelAfter.Count; opencl_intel_before = $intelBefore }
if ($status -eq 'failed') { exit 1 }
exit 0

View file

@ -0,0 +1,52 @@
# Igneum run job: read back PC 1 (machine ae432dc7) after the restart that applied the Intel Arc driver 32.0.101.9034
# (7 October 2026, branch intel-arc). Read only, NOT elevated, beside the miners: no api/* request other than one GET
# of api/state for the card row; never quits, pauses, resumes or switches anything. Lines: the Intel adapter row (driver
# version, status, Code), the PnP display devices, the installed worker's --list (the "Intel(R) OpenCL Graphics" platform
# line with the device's global MiB), clinfo when present, and the app's own card row for the Arc after hotplug.
$ErrorActionPreference = 'Continue'
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
$want = '32.0.101.9034'
"RESULT start $(Stamp) job=pc1-intel-readback machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION uptime_s=$([int]((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime).TotalSeconds)"
$found = @()
try {
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
"RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)"
if ($a.Name -match 'Intel') { $found += "$($a.Name) driver $($a.DriverVersion) status $($a.Status) code $($a.ConfigManagerErrorCode)" }
}
} catch { "RESULT adapter error=$($_.Exception.Message)" }
try { foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) { "RESULT pnp name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) id=$($d.InstanceId)" } } catch { "RESULT pnp error=$($_.Exception.Message)" }
try {
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
if ($p -and $p.DriverDesc -match 'Intel') { $mem = '-'; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) memory=$mem" }
}
} catch { "RESULT class not_readable=$($_.Exception.Message)" }
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
$worker = $null
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
$intelLines = @()
if ($worker) {
$list = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
foreach ($l in $list) { "RESULT list $_" }
$intelLines = @($list | Where-Object { $_ -match 'Intel' })
} else { "RESULT list error=no installed igneum-worker-opencl.exe" }
"RESULT opencl intel_lines=$($intelLines.Count)"
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } }
# the app's own row for the Arc (read only)
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
$row = ''
try {
$urlFile = Join-Path $appDir 'app.url'
if (Test-Path $urlFile) {
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
foreach ($c in @($all)) { $line = "key=$($c.key) name=`"$($c.name)`" vendor=$($c.vendor) kind=$($c.kind) worker=$($c.worker) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) problem=`"$($c.problem)`" message=`"$($c.message)`""; "RESULT app_card $line"; if ($c.name -match 'Intel') { $row = $line } }
} else { "RESULT app_card error=no app.url (the app is not up yet)" }
} catch { "RESULT app_card error=$($_.Exception.Message)" }
foreach ($f in $found) { "RESULT intel_device $f" }
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($want) -and $_ -match 'code 0' }).Count -gt 0
"RESULT end $(Stamp) version_ok=$versionOk intel_opencl_platform=$($intelLines.Count -gt 0) app_row=$(if ($row) { 'yes' } else { 'no' })"
'SUMMARY ' + ([ordered]@{ job = 'pc1-intel-readback'; status = $(if ($versionOk -and $intelLines.Count -gt 0) { 'done' } else { 'partial' }); finished_at = (Stamp); version_ok = $versionOk; intel_opencl_lines = $intelLines.Count; intel_devices = $found; app_row = $row } | ConvertTo-Json -Compress -Depth 3)
exit 0

View file

@ -0,0 +1,21 @@
# Igneum run job: restart PC 1 (machine ae432dc7) to apply the Intel Arc driver 32.0.101.9034 (7 October 2026, branch
# intel-arc). the project lead's word, relayed by main at 14:5x UK: "if the installer reports reboot required (exit 14) you have his
# word to reboot PC 1". Run b of the driver job exited 1014 (the bootstrapper's wrap of the inner installer's 14).
# A plain `run` job, NOT elevated: shutdown.exe needs only SeShutdownPrivilege, which the signed-in user holds, so no
# prompt. Windows closes the installed app with the session (it is never quit, paused or resumed by this script: no
# api/* request here) and the app's autostart brings it back after the restart. The read-back job follows.
$ErrorActionPreference = 'Continue'
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
"RESULT start $(Stamp) job=pc1-reboot machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
try {
$arc = @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | ForEach-Object { "$($_.Name) driver $($_.DriverVersion) status $($_.Status) code $($_.ConfigManagerErrorCode)" })
foreach ($a in $arc) { "RESULT before $a" }
} catch { "RESULT before error=$($_.Exception.Message)" }
$pending = Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
try { $p = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Name PendingFileRenameOperations -ErrorAction SilentlyContinue; "RESULT pending_file_renames=$([bool]($p -and $p.PendingFileRenameOperations))" } catch { }
"RESULT restart requested $(Stamp): shutdown /r /t 20 (the app closes with the session and autostarts after)"
& "$env:SystemRoot\System32\shutdown.exe" /r /t 20 /c "Igneum: restart to apply the Intel Arc driver 32.0.101.9034 (the project lead's word, 7 October 2026)" 2>&1 | ForEach-Object { "RESULT shutdown $_" }
"RESULT shutdown exit=$LASTEXITCODE"
'SUMMARY ' + ([ordered]@{ job = 'pc1-reboot'; status = $(if ($LASTEXITCODE -eq 0) { 'done' } else { 'failed' }); finished_at = (Stamp); shutdown_exit = $LASTEXITCODE } | ConvertTo-Json -Compress)
if ($LASTEXITCODE -ne 0) { exit 1 }
exit 0

View file

@ -124,6 +124,13 @@ function stateNow() {
if (scenario === 'clock') { s.clock = { ...s.clock, severity: 'block', skew_s: -412, message: 'This machine\'s clock is 6 min 52 s behind the network. Blocks it finds are rejected until the clock is right.', hint: 'System Settings > General > Date & Time: turn on "Set time and date automatically"' }; s.mining.state = 'waiting'; s.mining.hash_total = 0; }
if (scenario === 'fresh') { s.setup_done = false; s.phase = 'welcome'; s.address = { display: '', key_saved: false, source: '', value: '', wallet_file: '' }; }
if (scenario === 'job') { s.jobs = { ...s.jobs, active: true, id: 'job-2026-10-04-17', kind: 'shard-benchmark', title: 'shard benchmark', stage: 'proving shard 3 of 8', started_at: t - 400, results: ['RESULT shard=1 prove_s=41.2', 'RESULT shard=2 prove_s=39.8'], message: '' }; }
if (scenario === 'intel') {
// PC 1 after the B580 swap (7 October 2026, docs/plans/intel-arc.md): the 5090 and the first Intel card; the Arc's
// numbers are the plan's expectation, not a measurement, until the PC 1 job's row lands
s.mining.cards = [{ ...recState.mining.cards[0], key: 'nvidia:NVIDIA GeForce RTX 5090', name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '21760 cores', vram_mb: 32768, device: '0', identities: 8, ids: ['8fafda27', '1b2c3d4e', '5f6a7b8c'], power_default_w: 575, power_pct: 80, power_applied: true, power_limit_w: 460, power_w: 227, temp_gpu: 61, temp_mem: 70, hash_now: 127.7, hash_avg: 127.1, eff_mhw: 0.563, sweep_supported: true, sweep_state: 'idle', sweep_note: '', tune_line: 'Tuned: 127.7 MH/s at 227 W (0.563 MH/W)', tune_source: 'full', tune_control: true, tune_clock_mhz: 1854, sweep_pct: 100, sweep_watts: 227, sweep_at: t - 7200 },
{ ...recState.mining.cards[0], key: 'intel:Intel(R) Arc(TM) B580 Graphics', name: 'Intel(R) Arc(TM) B580 Graphics', code: 'Intel(R) Arc(TM) B580 Graphics', platform: 'Intel(R) OpenCL Graphics (OpenCL 3.0 ), driver 32.0.101.9034', kind: 'discrete', vendor: 'intel', worker: 'OpenCL', detail: '160 compute units', vram_mb: 12208, device: '1', bus: '05:00.0', identities: 8, ids: ['c0ffee01', 'c0ffee02'], enabled: true, state: 'mining', hash_now: 15.2, hash_avg: 15.0, accepted: 3, rejected: 0, power_default_w: 0, power_pct: 0, power_applied: false, power_w: 0, temp_gpu: 0, temp_mem: 0, eff_mhw: 0, sweep_supported: false, sweep_state: 'unsupported', sweep_note: 'not available: Intel Arc exposes no power cap or reading through OpenCL (the driver\'s IGCL counters come next)', tune_line: '', tune_source: '', tune_control: false, driver: '32.0.101.9034' }];
s.mining.hash_total = 142.9; s.display_name = 'pc-1';
}
if (scenario === 'nvidia') {
s.mining.cards = [{ ...recState.mining.cards[0], key: 'nvidia:0:NVIDIA GeForce RTX 5090', name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '21760 cores', vram_mb: 32768, device: '0', identities: 8, ids: ['8fafda27', '1b2c3d4e', '5f6a7b8c'], power_default_w: 575, power_pct: 80, power_applied: true, power_limit_w: 460, power_w: 441, temp_gpu: 71, temp_mem: 92, telemetry_at: t, hash_now: 412.5, hash_avg: 405.2 },
{ ...recState.mining.cards[0], key: 'nvidia:1:NVIDIA GeForce RTX 4070', name: 'NVIDIA GeForce RTX 4070', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '5888 cores', vram_mb: 12288, device: '1', identities: 2, ids: ['9a8b7c6d'], power_default_w: 200, power_pct: 80, power_applied: false, power_note: 'nvidia-smi: wanted 160 W, card reports 200 W', power_limit_w: 200, power_w: 188, temp_gpu: 64, temp_mem: 0, telemetry_at: t, hash_now: 98.1, hash_avg: 97.4, state: 'restarting', restart_in_s: 4 },