diff --git a/docs/analysis/class-v6/multi-family-adversary.md b/docs/analysis/class-v6/multi-family-adversary.md index f8b62baf1..6d0ee3e61 100644 --- a/docs/analysis/class-v6/multi-family-adversary.md +++ b/docs/analysis/class-v6/multi-family-adversary.md @@ -27,11 +27,15 @@ population, Apple reported and not headlined; every defence is scored after the 2. The rows (ASAP7, placed and routed, SPEF, gate-level VCD; the SRAM term modelled, bands shown; node factors claimed): the full core 9.36 pJ per lane-op at ASAP7 on the class v4 draw (8.6 to 11.1), 6.55 at N5, 4.72 at N3; k 0.64 node-for-node and 0.46 a node ahead at the 5090's lock; the reserve families k 0.33 to 0.86 placed; the - genesis-only core 7.78 (k 0.53 / 0.38); the 32-lane genesis core 10 percent under (synthesis). + genesis-only core 7.78 (k 0.53 / 0.38); the 32-lane cores 10 to 13 percent under their 8-lane forms (synthesis: + the full core 5.73, k 0.39 / 0.28), so the adversary's cheapest row is the 32-lane placed core at about 5.7 pJ + per lane-op at N5 (k 0.55 same-node), a sixth under the headline row. 3. The complete machine (memory, controller, host share, power train, cooling) on the card's own GDDR7: 1.5x the - 5090 at its lock per joule node-for-node (1.3x to 1.7x), 1.8x a node ahead (1.5x to 2.0x); 3.3x per dollar; HBM3 - buys it nothing; the N2 SRAM die 2.3x and 3.1x; the stored-half hybrid (section 13) 1.9x and 2.4x at USD 2.80 per - MH/s. Capex per MH/s is the larger half of the chip's edge; the project cost is its hold. + 5090 at its lock per joule node-for-node on the placed 8-lane full core (1.3x to 1.7x), 2.1x on the placed + 32-lane core's floor (1.7x to 2.4x); 1.8x and 2.4x a node ahead; 3.3x per dollar; HBM3 buys it nothing; the N2 + SRAM die 2.3x to 4.2x same-node; the stored-half hybrid (section 13) 1.9x to 2.6x at USD 2.80 per MH/s. The + same-node honest bracket across the adversary's choices is 1.5x to 2.1x on the DRAM board. Capex per MH/s is the + larger half of the chip's edge; the project cost is its hold. 4. Data-local execution cannot pay (the live state is 26x the read, section 11); memory sharing is the baseline; recomputation loses; selective participation gains 2 to 5 percent for 50 to 90 percent of revenue (section 13). 5. Class v7 should take the epoch-defined bounded dataset with a published support horizon and keep the floor @@ -348,10 +352,28 @@ under the class v4 draw (shfla and mm8 live -1.2 percent, all eight live -9.2 pe hash and pays +29 percent of shadow energy, and the card's premium rises by the same +29 percent (its tile at 70 pJ per lane against the draw's 10.3): the ratio does not move, the lane count does (section 14, row 1). -The 32-lane genesis core (synthesis only; four window macros, one imem pair shared by 32 lanes): 261,440 cells, -5.29 pJ per lane-op at ASAP7 (4.65 to 6.77) on the class v4 draw, 3.70 at N5, k 0.36 at the lock: 10 percent -under the 8-lane core, the imem and the sequencer amortised over four times the lanes. The 32-lane full core -(synthesis) and its placed form are in the flow (adv-a and adv-f at 16:4x BST) and land as a delta. +The 32-lane cores: the genesis comparator PLACED AND ROUTED (adv-g, 20:1x BST; SPEF, gate-level VCD at the +30 ns constraint, four window macros and one imem macro, 717,268 cells with fill): 4.80 pJ per lane-op at ASAP7 on +the class v4 draw (4.17 to 6.28), 3.36 at N5, 2.42 at N3, k 0.33 node-for-node and 0.24 a node ahead at the +5090's lock. That is 9 percent UNDER its own synthesis row (5.29), where the 8-lane cores read 32 to 42 percent +over theirs: the 32-lane core is routed under a 30 ns constraint (its unpipelined crossbar path is 27 ns) and the +flow's resizer downsizes every cell to it, so the placed figure carries smaller cells and lower capacitance than a +chip clocked at 1.5 ns with its pipeline registers would. The honest 32-lane row is therefore a band, not a point: +3.36 pJ at N5 (the relaxed-clock placement, the floor) to 5.7 (the synthesised 32-lane full core 5.73 at ASAP7 +times the 8-lane cores' measured placement factor 1.42, the ceiling), k 0.33 to 0.55 same-node and 0.24 to 0.40 a +node ahead. The synthesis rows beside it: the genesis core 261,440 cells, 5.29 (4.65 to 6.77), 3.70 at N5; the +full core 393,036 cells, 5.73 (5.09 to 7.20), 4.01 at N5, 2.89 at N3, k 0.39 / 0.28; the bank's cost at 32 lanes +8 percent of the energy on the draw and 50 percent of the cells. The placed 32-lane FULL core runs on adv-g (from +synthesis at 19:58 BST, the same 30 ns constraint) and lands as a delta; its placed figure will sit inside the +same band for the same reason. + +The complete machine at the 32-lane band (the genesis comparator's placed 3.36 pJ at N5 as the floor; the ceiling +is section 6's placed 8-lane full core less a sixth): the GDDR7 board 2.1x the 5090 at its lock per joule +node-for-node at the floor (1.7x to 2.4x) and 2.4x a node ahead (2.0x to 2.7x), against 1.5x and 1.8x on the +placed 8-lane full core; the N2 SRAM die 4.2x and 5.7x at the floor. So the same-node honest bracket across the +adversary's lane count, core and sizing choices is 1.5x to 2.1x per joule on the DRAM board (1.3x to 2.4x on the +bands), 1.9x to 2.6x with the stored-half hybrid, USD per MH/s unchanged (the core is a tenth of the board's capex +at 32 lanes). The board rows of section 6 restated at the placed energy (the full core at 6.55 pJ per lane-op at N5, band 6.01 to 7.77; 4.72 at N3): the complete GDDR7 machine 1.53 microjoules per hash, **1.5x the 5090 at its lock per joule @@ -726,7 +748,14 @@ percent (5th to 95th percentile 1.45x to 1.58x on the placed rows); a specialist gains 2, 3 or 5 percent of ratio and loses 50, 75 or 90 percent of revenue, because difficulty follows the fleet it joins and it earns in proportion to the time it mines; downtime and re-entry cost it the DAA window's lag each way and buy it nothing. The GPU-cost budget of 10 percent at the lock (set before these results) is untouched: -no honest energy is spent in this section. Served sentence for the bracket: against a chip on the same node the +no honest energy is spent in this section. (5) "Layer 8 off" (the hash lane's D2 experiment of 18:12 BST: the era layout's window-layer draw removed, every +window the whole dataset): the hottest half then serves exactly 50 percent of the reads at every program, so the +uniform-store row becomes the hot-set row: the hybrid reads 1.85x (1.58 to 2.03) same-node and 2.27x (1.95 to +2.50) a node ahead at USD 3.34 per MH/s and 273 MH/s per board, against 1.93x / 2.40x at USD 2.80 on today's mean +program (hit 0.581) and 2.09x / 2.65x at USD 1.88 on Devnet 3's p98 program (0.72): the window layer's removal +takes 4 percent off the hybrid's per-joule edge at the mean program and 11 percent at the tail, and its capex edge +from 5.7x to 4.8x of the card's, for one and-or per load on the honest side. The rows are in +`tools/chip-model/mf/results/d2b-n5.md` and `d2b-n3.md`. Served sentence for the bracket: against a chip on the same node the honest floor is the stored-half hybrid at 1.9x to 2.1x per joule and 6x to 9x per dollar (1.65x to 2.3x on the band), not the DRAM board's 1.5x and 3.3x; the lever on it is the dataset floor as a capex ticket. @@ -751,7 +780,131 @@ Reading: no row loses competitiveness; the cheapest adaptation is firmware for e sequence for an entry outside it (row 2, 1.3x to 1.45x same-node while live), and a respin only if the chain adds a family the sequence cannot carry, which it has 360 days' notice of. The stress life of three years holds in full. -PENDING with clocks: the 32-lane full core's synthesis row (adv-a, in ABC at 17:0x BST; by 19:30) and its placed -row (adv-f, at floorplan; by 21:00); the k lane's crossbar, scratch and tile rows (its pod); a real memory +PENDING with clocks: the 32-lane full core's placed row (adv-g from 19:58 BST on a 64-core host, adv-f since 18:12 +on a slower one, whichever lands first, by 22:00; the 32-lane genesis comparator's placed row landed at 20:1x, +section 5, and brackets it); the k lane's crossbar, scratch and tile rows (its pod); a real memory compiler's figure for the two macros (owed, no clock: FakeRAM gives area and pins only); the per-family rows on the 32-lane placed core (by 21:30 if adv-f lands, else the next pass). + +## 15. The SRAM die's ticket reconciled (the research lane's and the coordinator's ask, 17:2x BST; floor lane 3's USD 0.6 against this file's USD 2.94 per MH/s) + +The two figures price different machines. Lane B's USD 0.25 to 0.4 of silicon per MH/s (chip-model-v3 5.12, +hardware-future) is the die at ZERO SHADOW: 2,100 MH/s per 2 GiB reticle at 300 W, no shadow core at all; with a +board it reads about USD 0.6. This file's USD 2.94 is the same die carrying the class v4 shadow on the placed mf core +(6.55 pJ per lane-op at N5), with the machine held at lane B's 270 to 300 W: the shadow is 13x the die's own energy +per hash, so at that budget the machine makes 383 MH/s and the fixed tickets (die, package, board, host) divide by +383 instead of 2,100. Neither is the adversary's choice. The designer sets the power budget to minimise dollars per +MH/s; the die's read ceiling (floor lane 3: about 1,060 G reads per second, 8.3 GH/s) is never reached because the +core's silicon and the power train bind first. Line by line, every term with its source and label: + +| Component | This file at 270 W (section 6) | The optimised machine (this section) | Source and label | +|---|---|---|---| +| The 2 GiB SRAM die: 452 mm^2 of macro on a 550 to 650 mm^2 N2 die, a USD 30,000 wafer, lane B's yield model | USD 500 (400 to 600) | the same | lane B 4.9 (claimed density and wafer price; the yield approximate) | +| The shadow core's silicon: this core's placed floorplan 0.0036 mm^2 per lane at ASAP7, x0.55 to N5 (0.002 mm^2), one lane-op per 7.5 ns (the 5-phase slot), 770 lanes per MH/s; USD 0.36 per mm^2 of N5 (sram-mirror's yield model) | USD 0.55 per MH/s (590 mm^2 at 383 MH/s) | USD 0.11 to 0.55 per MH/s: a core pipelined to one op per cycle per lane is five times denser (0.0004 mm^2 per lane; about +0.1 to 0.2 pJ per op for the pipeline registers, inside the band); the record's USD 25 to 40 per 166 MH/s (0.15 to 0.24) sits inside | this file's placed floorplan (measured), the pipelining factor approximate | +| The package: two reticle-class dies (the SRAM die and the core die) with a wide link between them | USD 200 (an interposer, approximate) | USD 60 to 200: an organic flip-chip substrate carries a UCIe-class link at the hash's 80 bits per read (floor lane 3's hop, 0.5 pJ per bit); the interposer only if the link must be wider | approximate; the interposer price chip-model-v3 5.1 (claimed) | +| The board, assembly, PSU and cooling | USD 200 flat (board 150, assembly 50; PSU and cooling inside) | USD 150 plus USD 0.15 per watt of PSU and cooling (approximate): USD 240 at 600 W, 375 at 1.5 kW | approximate | +| The host (one full node per 100 machines) | USD 15 | USD 15 | section 6 | +| The sustained rate | 383 MH/s at 270 W (power-bound on the core) | 852 MH/s at 600 W, 1,420 at 1 kW, 2,130 at 1.5 kW, 2,840 at 2 kW (the die's own ceiling 8.3 GH/s, never reached) | board.py on the placed core (modelled) | +| **USD per MH/s** | **2.94** | **1.63 / 1.20 / 0.98 / 0.88 at 600 W to 2 kW on this core; 1.07 / 0.73 / 0.56 / 0.47 with the pipelined core and the organic package** | modelled | + +The reconciled figure: **about USD 1.0 per MH/s for the SRAM-die machine carrying the class v4 shadow (0.5 to 1.6), +at 1 to 1.5 kW per machine**, set by the power train and the core's silicon and not by the die; lane B's USD 0.6 +holds only at zero shadow, this file's USD 2.94 only at a 300 W budget, and both stand in the record with those +labels. Per joule the optimised machine is unchanged (the energy per hash does not depend on the budget: 2.3x +same-node, 3.1x a node ahead at the placed core). + +What a maker's first batch of 1,000 dies would actually pay: not the unit ticket. A thousand 2 GiB dies at 1 to 2 +GH/s each are 1 to 2 TH/s, several times the chain's whole hashrate at the rental equilibrium (floor lane 3's +section 4: a third of the network is under two dies at every price in its table), so the batch's cost is the +project (USD 100 M to 500 M at N2, claimed) spread over a hashrate the chain cannot absorb: USD 50 to 250 per MH/s +of NRE against USD 1 of unit cost, and the first dies' yield (a 600 mm^2 die with 452 mm^2 of macro, redundancy +untested) adds USD 100 to 400 per die, approximate. The die's economics are project economics: the coexistence +verdict should take USD 1.0 per MH/s (0.5 to 1.6) as the unit ticket of a fleet that exists and the project cost +as the gate on whether it ever does. + +## 16. The formal memory model: the class v6 evaluation in capacity, bandwidth, energy and amortisation terms (the 2.0 register's row, drafted 18:1x BST for the 12:00 delta) + +One evaluation (a hash) under class v6: 128 dependent loads of a 4-byte word (W = 1; 16 bytes at W = 4) from a +dataset of D bytes (2 GiB at genesis, the floor schedule 5.5 / 8.5 / 11.5 GiB), each address the fold of the lane's +live state (64 registers, 61 necessary), each load returning into that state; between loads the shadow block (6,912 +instructions per iteration, 102,100 per hash) and the base program (512); the dataset rebuilt once per window from +the chain's state (class v5 and v6) or from the epoch's seed (the class v7 default), 157 G ops per GiB. The terms, +each with its bound and whether the bound is closed-form or rests on physical design: + +| Term | Per evaluation | Across N evaluations on one machine | The bound | Closed-form or physical | +|---|---|---|---|---| +| Capacity, the dataset | D bytes must be addressable at the hash's latency: every item is reachable from every lane with uniform probability (the fold is keyed by the destination register; no item is colder than 1/D by construction, the hot set is per program and per window) | shared by every lane and every engine on the board: D once per machine, never per engine | a machine holds D or recomputes (section 13: recomputation loses at every point; a partial store of fraction f serves f of the reads uniformly, or up to 0.58 to 0.72 at f = 0.5 on the window layer, 0.50 with the layer off) | closed-form (the item distribution is the census's; the SRAM price per GiB is claimed) | +| Capacity, the live state | 2,112 bits per hash in flight; the chain forbids reducing it (61 of 64 registers necessary; the connected-state lane: free for a chip, only the width counts) | lanes in flight x 2,112 bits: 1,172 lanes on the GDDR7 board (310 KB), 21,000 on the SRAM die (5.5 MB) | an SRAM macro per 8 lanes, 3.5 pJ per access (2.0 to 7.0): the one term this file's placed rows measure | physical (the macro energy band; the rest of the core measured placed) | +| Bandwidth, random reads | 128 activates per hash: the device's activate rate, not its pin rate, binds (21.3 G per second on 16 GDDR7 devices, 82 percent reached by the 5090; 10.7 G on an HBM3 stack, unmeasured, the JEDEC floor 2.3 G; the SRAM die's 1,060 G never reached before power binds) | N x 128 activates: the sustained rate is activates per second over 128, shared across every engine on the board | the memory's activate ceiling per dollar of devices: a chip cannot buy more activates per device than the card has (section 11) | the GDDR7 ceiling measured on the card (82 percent); the HBM3 ceiling physical (the AWS F2 hour); the SRAM die's a model | +| Bandwidth, bytes | 32-byte sectors at W = 1 (4 KB per hash, 38 percent of the GDDR7 pins at the activate ceiling); 2 sectors at W = 16 (dead on the cards) | linear in N | never the bound at W = 1 to 8 | closed-form | +| Bandwidth, the state to the data | 2,112 bits per read if the computation moves to the item; 80 bits per read if the item moves to the lane | 26x on every medium (section 11) | data-local execution cannot pay | closed-form (the bit counts) with the per-bit energies claimed | +| Energy, the memory | 2.0 nJ per GDDR7 read (1.5 to 2.6), 1.2 on HBM3, 0.25 on the SRAM die at W = 1: 0.256 / 0.154 / 0.032 microjoules per hash | linear in N plus the static and controller terms (35 W on the GDDR7 board) | the device's activate energy (chip-model-v3 5.3, modelled from HBM2's breakdown and the vendors' per-bit figures) | physical (claimed breakdowns; the card's marginal measured at 8.7 nJ per read) | +| Energy, the shadow | 102,612 lane-ops x 6.55 pJ (placed, N5) = 0.67 microjoules; 0.48 at N3; the 32-lane core about 0.58 | linear in N; the clock, the window and the units measured per op; the leakage per lane | the placed rows (sections 3 to 5); the SRAM term's band | physical (measured placed; the SRAM term modelled) | +| Energy, the machine | the power train (PSU 92 percent, VRM 90), cooling (3 percent), the host (0.85 W per machine) | fixed per machine, amortised over its rate | section 6's rows | approximate | +| Amortisation, the set-up | the dataset build 0.7 J per GiB per window per machine; the host USD 15 and 0.85 W per machine; the era's registers | shared by every engine of the machine and every hash of the window: 1.4e-12 J per hash, under 1 percent of capex | nothing to amortise further: the set-up is already a window term | closed-form (the build measured on a card, the host approximate) | +| Amortisation, the silicon | the core die USD 0.11 to 0.55 per MH/s, the memory USD 320 to 1,000 per board, the package, the board | spread over the life (0.5 to 3 years): 0.085 USD per TH at 3 years on the GDDR7 machine, 0.22 to 0.27 on the cards | capex per sustained MH/s is the larger half of the chip's edge (section 8) | the core measured placed, the memory and board claimed or approximate | +| Selective participation | the per-epoch ratio spreads 9 percent (5th to 95th percentile 1.45x to 1.58x same-node) | a specialist mining the best x of epochs earns x of the revenue at +2 to +5 percent of ratio | nothing to gain | closed-form on the band (the draws uniform within it, approximate) | + +The reading: capacity is a ticket (D once per machine, in DRAM at USD 10 per GiB or SRAM at USD 250), bandwidth is +the activate ceiling per dollar of devices and is the card's own, energy is the memory's activate energy plus the +shadow at the placed core's pJ per op, and amortisation is already complete at one machine (the set-up is a window +term, the silicon a life term). The bounds that are closed-form: the capacity and state terms, the bit counts of +data-local execution, the bytes, the set-up amortisation, selective participation. The bounds that rest on physical +design: the SRAM macro's access energy (the one modelled term in the placed rows), the HBM3 activate ceiling, the +SRAM die's wire term and the on-package hop, the device activate energies (claimed breakdowns), and the board's +power train and cooling (approximate). Nothing in the model rewards a chip for anything but the memory's own +activate energy and the shadow's pJ per op, which is where sections 6 and 13 put the honest bracket. + +## 17. Review B's F05: the reg64 address mixer's incremental form, priced at the placed level (20:0x BST; the clock 23:30) + +The finding: the connected-state candidate's address (the fold over all 63 live registers before every load) has an +exact incremental form through a prefix-XOR tree, so a chip never reads and folds 63 registers per load; a design +that assumes the literal fold overstates the chip's cost, and a one-register perturbation test says nothing about a +minimum circuit. The shipped class (v4, v5, v6) folds ONE source register per load (this core's load op), so the +finding touches the connected-state candidate (killed as a class at 17:25 BST on other grounds) and the bound it +sets for any future class that couples the address to the whole window. Three designs for that coupling on this +core, priced with the placed per-op figures (6.55 pJ per lane-op at N5 on the class v4 draw; the SRAM macro 3.5 pJ +per 256-bit access, 2.0 to 7.0, shared by 8 lanes; the 102,612-op hash with 128 loads): + +| Design | Extra state per lane | Extra work | Extra lane-ops per hash | Extra energy per hash at N5 | Share of the chip's shadow energy (0.672 microjoules) | +|---|---|---|---|---|---| +| The literal fold: read and fold 63 registers at each load | none | 63 reads of the window (SIMD, one macro access per 8 lanes each) and 63 rotate-xor ops per load | 128 x 63 = 8,064 (plus 8,064 macro accesses per 8 lanes: 3.5 pJ x 8,064 / 8 = 3.5 nJ) | 8,064 x 6.55 pJ + 3.5 nJ = 56 nJ (48 to 72) | +8.4 percent | +| The prefix-XOR tree (the reviewer's form, floor lane 3's Fenwick reading): 65 words of prefix state, seven read-modify-writes per register write, eleven ops per load | 260 bytes in a second gated macro (one per 8 lanes, the same shape as the window) | 7 x 2 macro accesses and 7 xor-rotate ops per instruction that writes the window; 11 ops per load | on the base program's 512 writes and 128 loads: 4,992 ops and 7,168 accesses per 8 lanes; on every instruction of the hash (the shadow block writes the same window): 718,000 ops and 1.44 M accesses | base program only: 33 nJ of ops + 3.1 nJ of accesses = 36 nJ; every instruction: 4.7 microjoules of ops + 0.63 of accesses (7x the whole shadow) | +5.4 percent if the shadow block's writes are exempt; +800 percent if they are not | +| The running total (this lane's form: the fold is linear over GF(2), so a write to register j changes the fold by a fixed rotation of old xor new, and the old value is already read in phase 0 of this core's slot) | one 32-bit register per lane (flops, written every instruction) | two rotates and two xors per instruction, merged into the write phase | 102,612 (one op's worth of datapath per instruction, no extra macro access) | about 1.0 pJ per instruction (an xor-rotate pair on operands already latched; the add row's datapath term is 1.7 pJ, approximate): 0.10 microjoules | +15 percent, paid on every instruction; +0.8 percent if only the base program's writes count | + +Reading. (1) Which form the adversary picks depends on how many instructions write the window between loads: at +the shipped shape (one load per 800 instructions once the shadow block is in) the LITERAL fold is the cheapest +(+8.4 percent of the shadow, 56 nJ), because any incremental form pays per write and the shadow writes 800 times +per load; the running total wins only if the shadow block's registers are exempt from the fold (then +0.8 +percent), and the Fenwick tree never wins on this core (its seven read-modify-writes per instruction are macro +accesses, the one term this core pays dearly for). Floor lane 3's 30 nJ for the tree counts the base program's +writes only; the row above shows both counts. (2) The bound the review asks for: the cheapest adversary form on +the placed core costs at most 56 nJ per hash (the literal fold; 48 to 72 across the SRAM band) and at least 5 nJ +(the running total with the shadow exempt), so the complete-machine bracket for a class carrying the whole-window +fold moves from 1.5x (1.3 to 1.7) same-node to 1.42x to 1.49x (the chip's machine energy 1.528 to 1.53 + 0.056 x +1.21 = 1.60 microjoules at the literal fold; 1.535 at the running total), and 1.8x a node ahead to 1.7x to 1.8x: +the coupling buys the honest side at most 0.08x on this core, which is inside the SRAM term's band and agrees +with the connected-state lane's KILL (1.10x against its 1.25x gate) and floor lane 3's 2.6 percent on the GDDR7 +board. (3) No row here assumes a 63-read cost that the adversary can avoid: the literal fold is priced as 63 SIMD +macro reads shared by 8 lanes (3.5 nJ per hash of accesses, not 63 window reads per lane), and the incremental +forms are priced per write. The shipped class is untouched: its fold reads one register. (4) The GPU side of the same two forms (the fleet +lane, 20:4x BST, the hash lane's hl-v6-all fold form against hl-v6-all-prefix, the same program id and vectors, +stock clocks, 250 batches, fingerprints equal; evidence on build-1 under /srv/artefacts/tas/54900078/ and +/srv/artefacts/tas/si1xc4yhpakk1v/): the 5090 reads 70.6 MH/s at 437.8 W (6.20 nJ per hash) on the literal fold +and 70.2 at 500.0 W (7.13 nJ) on the prefix form, 100 against 248 registers per thread; the 4090 31.3 MH/s at +238.1 W (7.60 nJ) against 31.3 at 231.7 W (7.41 nJ), 93 against 154 registers. The rate is unmoved on both cards +(both are bound at the dataset reads) and the prefix form costs the 5090 14 percent more board power for the same +hashes; so on the card as on this core the cheapest form at the shipped shape is the literal fold, and the +review's incremental form is a design the adversary can take and does not want. (5) The bound restated for the +connected-state class's OWN shape (the hash lane's closed form, 20:5x BST: `address_source(s) = r[s] ^ ror(P_s, 1) +^ (S ^ P_s ^ a[s])`, a[k] = rotl(r[k], (63 - k) mod 32), S the xor of every a[k], P_s the prefix xor below s; +32 loads per 64-instruction iteration, 256 loads and 512 register writes per hash in the base program): there the +literal fold is 256 x 63 = 16,128 lane-ops and 16,128 SIMD macro reads per hash, 113 nJ at N5 (97 to 145), and the +prefix form is one running-total update per write (an xor-rotate pair, about 1 pJ) plus six Fenwick read-modify- +writes per write (twelve macro accesses per 8 lanes, 5.3 pJ) and six prefix reads with three xors per load: 512 x +12.3 pJ + 256 x 5.6 pJ = 7.7 nJ (5 to 15). So on that class the review is right by 14x: the chip pays about 8 nJ +per hash for the whole-window coupling, 1.2 percent of its shadow energy, and the complete-machine bracket moves +from 1.5x to 1.49x same-node (not to 1.42x, which was the literal fold's cost); the card meanwhile keeps the fold +at 6.20 nJ per hash on the 5090 and would pay 7.13 on the prefix form. The two shapes together: the chip's cost of +the coupling is 8 nJ (connected-state shape, prefix form) to 56 nJ (shipped shape, literal form), 0.02x to 0.08x +of the bracket, and no design in the record charges the chip a 63-read cost it can avoid. diff --git a/docs/analysis/pool/pool-pair-2026-10-08.md b/docs/analysis/pool/pool-pair-2026-10-08.md new file mode 100644 index 000000000..e79c28d8b --- /dev/null +++ b/docs/analysis/pool/pool-pair-2026-10-08.md @@ -0,0 +1,54 @@ +# The devnet-4 pool pair, 8 October 2026 (UX-05, UX-04 evidence; the night's record) + +Pool seat, 8 October 2026, 20:0x to 21:4x UK. Binaries: the node /srv/artefacts/200-12424341/node-lane/igneumd on +build-2 (successor-2.0.1's gate build), the pool daemon from pool-2.0 (8106ba27 then e063fdcd; igneum-pow fingerprint +cbc5bd0aa10585c8, the freeze 1c420786), two CPU members from the fork at 2b1a247a (the same fingerprint; a test +binary, never shipped). Two Vast hosts rented for the pair (i7-5820K, Xeon E5-2609 v3, Haswell) died with Illegal +instruction at the class v5 catch-up on every node build and were destroyed; the pair ran on build-2 under lease +pool class measure and was brought down by pid file at 21:3x UK (0 leases, 0 processes left). + +## UX-05 Keep voting keys with the miner through pooling: PASS in the crate + +| Test (pool crate, build-2) | Known-pass | Known-fail | Result | +|---|---|---|---| +| `verify::tests::a_share_under_another_key_is_refused_before_the_hash` | the member's key on job and share: ok | another key on the share, and a job naming another key: `vote_key`, hash 0, under a tenth of an evaluation | ok (51 of 51 at e063fdcd, 52 of 52 at 986252e7) | +| `sidechain::tests::a_share_whose_keys_disagree_is_refused_before_its_pow` | make_share | the claim swapped, the header's key swapped, a foreign reveal | ok | +| `the same nonce on another template hashes differently` | | a nonce moved to another template: `wrong_hash` | ok | +| the TLS binding (replay refused), the admission bounds, the intents (review B) | | | ok | +| `an_unsynced_node_hands_the_pool_no_state_and_no_job` (986252e7) | a synced node: leaves served, jobs issued | synced false: no leaves, no job | red against 8106ba27 on build-6, green with the guard | + +The live pair reached: authorise with the members' own keys (the pool log names each key beside its payout address), +class v5 seeds issued (epoch 1a87e870..., day 20734, era 7c36b833...), jobs issued with `vote_key_hash` the +member's, shares sent. Every share read `wrong_hash`. + +## The wrong_hash cause, from the retained logs (not a generator skew) + +The node, the pool and the member carry the same igneum-pow (fingerprint cbc5bd0aa10585c8, the freeze; pool-2.0's +tree 93c36844 byte-identical to 1c420786, read back by the pool lane from the mirror and from the binaries). The pool +and the members hashed the identical epoch seed, day, class and era. Build-2's node never reached synced on devnet-4 +(its log loops on "class v5 execution catch-up" against peers that close the connection; `synced=false` on every +read), so the class v5 state leaves the pool and the members fetched by `igneum_getPowStateLeaves` for the epoch's +seed block were the still-settling catch-up state, not the node's state at template time; a class v5 lane hash over +unsettled leaves does not match the node's. The class kept: a class v5 pool needs its node fully synced before it +verifies shares, because the dataset is keyed on settled execution state. The guard by construction is pool-2.0 +986252e7 (the provider refuses leaves while `igneum_getExecStatus` reads unsynced, blocked or reexecuting; the feed +issues no job; the STATUS line says so). + +## Two 2.0 gaps closed by the pair (both on pool-2.0, in the 2.0.2 take) + +1. The pool daemon had no class v5 day-state source and issued no job on a class v5 chain (8ff7a0f4: + `state_provider.rs`, `--exec-rpc` defaulting to `--evm-rpc`). +2. The daemon read amounts at 8 decimals and refused every 18-decimal template as a high-word amount (8106ba27: the + base unit installed from the node's network before any amount is read). + +## UX-04 Pay small operators without hidden custody + +PPLNS distribution, the payout key round trip and the signed transfer decode: PASS in the crate. The live payout path +(a member earns, is paid at the minimum, reconnects, a redirection attempt) is NOT RUN: it needs the pair on a +fully-synced devnet-4 node with the 2.0.2 kit, tomorrow. + +## Consequences per tier + +A home miner on a pool: the key stays theirs on every job and share and a pool naming another key is refused before +the hash; a pool operator: the daemon refuses to issue jobs until its node is synced, so an unsynced start costs idle +minutes, never a wrong_hash storm; the network: no change to consensus from any of this. diff --git a/docs/plans/counter-asic-3-status.md b/docs/plans/counter-asic-3-status.md index de8e083c6..3627be2ae 100644 --- a/docs/plans/counter-asic-3-status.md +++ b/docs/plans/counter-asic-3-status.md @@ -520,6 +520,51 @@ THE 21:00 COPY's THIRD REFUSAL (20:5x BST): the registry's evidence rule 2 (a fi THE SIXTY-THIRD LANDING AND THE 20:5x LINES (BST). The sixty-third landing on master at 3f2050ab. The kit on the generator-6 packs (20:49 to 20:53): CUDA pairs on the fleet's standing RTX 4090 (pod si1xc4yhpakk1v, driver 580.159, at 20:52; the zip's sha 735e1411 read back on the Mac, the worker 804a6f7f): hl-v6-all check PASS e8f4f3289c6ee1fc (31.31 MH/s, 93 registers), hl-v6-foldrw check PASS 6ce3dc344a613500 (62.45 MH/s, 32 registers), both at 2^24 nonces from base 0, equal to the Mac's Metal and Apple OpenCL reads; the all pack at four of six platforms (CPU emulation, CUDA 4090, Metal, Apple OpenCL), the partner at three; the hash lane's last re-export metadata only (kernel texts and vectors byte-identical), the readings final; the 5090 and 7600 (PC 1's held runner, about 04:40 or the founder's desk) and the Arc (PC 2 by 22:00) the morning's rows, the generator 5 record (six of six on the same object before the review) beside them; zip 3 the kit the pin names unless a fourth zip reads back by 22:30; the page row class-v5 a08647f5a. F05 with both sides (the hash lane 20:5x, the adversary lane section 17 bb8cf8c30 at 20:54): the closed prefix form an exact algebraic equivalent (the native test proving it on every source register); on a GPU it buys nothing (the card memory-bound at the dataset on both texts; the prefix's running total costing 61 registers on sm_89 and 148 on sm_120, halving the 5090's occupancy: the 5090 6.20 nJ on the fold against 7.13 on the prefix at the same rate, +14 percent; the 4090 7.60 against 7.41, a wash); on the chip the bound moves with the shape: on the connected-state class's own form (32 loads per 64-instruction iteration, 256 loads and 512 register writes per hash) the literal fold 113 nJ per hash and the prefix form 7.7 nJ (one running-total update and six Fenwick read-modify-writes per write, six prefix reads and three xors per load), the chip paying about 8 nJ, 1.2 percent of its shadow energy, the complete-machine bracket 1.5x to 1.49x same node (not 1.42x, the literal fold's cost the adversary avoids by 14x); on the shipped shape (one load per 800 instructions) the literal fold the chip's cheapest at 56 nJ (0.08x); the register's F05 line: the chip's cost of the whole-window coupling 8 nJ to 56 nJ, 0.02x to 0.08x of the same-node bracket, measured placed on the chip side and on two cards on the honest side; the verifier keeping the fold as the definition; the connected-state KILL standing on the GPU-cost budget, not this term; cases POW-02 and ADV-04. V6-10's repinned ids ahead of 23:30 (the enforced lane): shard 0x51cd8cba314a32b60fac393949a4571714da6d6656717d286011601b2a163fe7, aggregator 0x05b395ec238f67406084f8a449d400f64c87dc62151daebf66695864727ded8a, sha 7d38077df on p22-stage-2 (elf sha256 0ce9e351 and 3ef25e3f; vks 0dbb6605 and 074eb906), deterministic across three builds on build-1 (identical bytes); the manifest with source_commit c45db4420, guest_input_format 3 (the first field, both guests refusing another format before anything else) and the live pair as prior, the 17:58 pair skipped (never activated); the host suite green on build-2 (the format refusal, the fee rounding with the odd wei to the burn, duplicate and paid-before and invalid carried records paying nothing); the host's --mode id read-back, the old-mode and new-mode compressed proofs (the new from a block exported by the D4 node 930b6322 with proving_payment_activation_daa 0) by 23:30; for the 2.0.2 tree. Floor lane 3's batch line to the landing hand inside 21:10 (eco05-20261008-01b at manifest 24883757: model:eco05 FAIL, model:coexist-fixtures RUNNING). The reference-apps lane's F04 pages on master at 2caa033a2 (20:46), the light service restarted by pid at 20:47, the deploy read-back owed before any "done". THE RESEARCH LANE'S 21:00 COPY LANDED (20:55 BST, read back by the landing hand): box master 10aa76d71265629120c78cc00e1e153fca68de15 (Merge counter-asic-4-docs-3 ae5a6868; full gate 87, the evidence rules green): the documents byte-equal to the tip (the 4 GiB decision FROZEN, the three-row chip line, the F05 correction, F09's corrected (c) cells, the B580 at 11.0 MH/s), sim/economy/coexist, the map cell harness:economics-model, the research batch eco-d4-20261008-01 (nine cases RUNNING) and lane 3's batch eco05-20261008-01b (ECO-05 FAIL, ECO-01 and ECO-08 RUNNING at manifest 24883757) replayed on master's registry; thirty landings by the hand through 10aa76d7. The census lane closed (20:55): the registry cell census:class-v6 PASS on master at 2f44f642 (run census-v6-20261008-2034, manifest 9415e9c8, evidence census-packs.md); its record on master (census-w16-mix.md 018a0877; census-packs.md sections 0 to 7 with the logs: 77f0c4b4, cdbdda05, eba774af; the row 2f44f642); the harness commits for the freeze's digest: class-v6-census-fold 5b3486f0, class-v6-census-reg64 b29e690d2, class-v6-census-all 25098c1a2 and b10fe1351 (the A03 command 9893b4c9d), class-v6-census-all3 6debbac1a (the post-review crate); no lease on any box; a re-read of a new all pack fifteen minutes from its line. +THE FREEZE TREE GREEN AND THE REVIEW VERDICTS (20:5x BST, the hash lane, delivered ahead of 22:30; every verdict with its native test on the sha). The freeze tree: class-v6 9c4e4247308acf42c998c1aafddad14afd9703fa on the mirror (ls-remote agreeing), its suite on build-7 147 passed, 0 failed, 8 ignored across 13 binaries, rc 0; the one sha for the node lane's last re-cut of (c). A02 lane connectivity (row 106; POW-02, POW-03): fixed by construction, every class with a v6 flag reserving five non-load slots as shuffles over the five lane dimensions in a drawn order (tests/v6fold.rs v6_draw_connects_all_lanes_and_keeps_mad_bijective_by_construction, 64 seeds x 4 classes); the overnight census POW-03's. A03 per-site concentration (row 107; POW-03, ADV-02): the census hand's, not applicable on v5 and v6 (uniform inside every window; the v2 cause closed by (c') and (a'); cdbdda05). A04 the acceptance's execution model (row 108; POW-02, POW-04): fixed by construction since class v4 sub-version 3 (the acceptance executing the shadow block as the hash does; a zeroing shadow failing acceptance, tests/packs.rs a04_a_zeroing_shadow_fails_acceptance); tonight's two predicate faults found by lane D (a +nowin or +reg64c program, and a multi-width era, judged by the class v2 parts on the chain's path) fixed at 04442d9ca and b24dfc162 under test (every_v6_flag_keeps_the_class_v4_acceptance_shape). A05 (row 109): the attack seat's 5a, fixed by construction. A06 program identity (row 110; POW-01): fixed by construction: the id recipe carrying the era draw ("era/" bytes), the generator byte and every class flag; the pack carrying program, dataset and era identities and identity.json authenticating every kernel text by BLAKE2b-256 (a06_program_json_hashes_every_kernel_text). A07 dispatch alignment (row 111; POW-02): fixed by construction: the verifier's bound hash the lane of the aligned 32-nonce group across tails and the low-32 rollover (a07_bound_hash_is_group_aligned_across_tails_and_rollover); the launchers refusing a job whose nonce_start is not 32-aligned or whose count is not a multiple of 32. A08 bijectivity (row 112; POW-02, POW-06): fixed by construction: a v6 mad never naming its destination as its second source; the fuzz corpus POW-06's. A09 exhaustion (row 113; POW-04): not applicable: no rejection pass for lane connectivity exists; the attempts census r 0.154 at width 4, 0 exhausted in 3,000 eras (lane D's 6.11). Review B on the object: F03 ProgramClass::V6 at generator 6 (program_class_v6_is_generator_6); F05 the closed prefix form proven equal on every source register (reg64_closed_form_equals_the_reference_fold_on_every_source) and measured on the 5090 and 4090 (neutral to worse on a GPU; the chip pricing the adversary lane's), no object change; F06 the liveness rule wired behind the reg64 flag, every v6 flag keeping the full rule and the attempt cap (reg64_liveness_rule_refuses_the_subset_fold_and_passes_the_full_chain through accept::check); V6-02 the executed-load counters with the agreement assert; found and fixed by the corpus: bind::unhex's panic on a multi-byte character (POW-06); GOV-05 the line itself. + +THE SIXTY-FOURTH LANDING AND THE 21:0x LINES (BST). The sixty-fourth landing on master at 1d499ab3. A FAULT OF THE HASH LANE'S, REPORTED ONCE (21:0x): the V6-02 commit (0266c9ec0, in 9c4e42473) made Program::loads_per_hash report the executed count (256 under the window) and accept.rs sizes its per-site arrays from that function, so for every reg64 class the acceptance's verdicts moved: the re-export of hl-v6-all at 43f1b1581 drew program id 0x1626c5853aa84261, not the object's 0x4de7b836cc40a4ea; found in the lane's own read-back of the re-export at 20:57 after naming 9c4e42473 green at 20:55; the node lane held the cut at 20:58 and went back to (c) 1a21d1ec (ac86d7910, whose acceptance is the object's; six suites green, the pair and canaries placed); the fix (the acceptance and the draw reading the drawn count as before; the executed counters separate functions the emitters alone read, program.h and program.json, with the agreement assert) committed and pushing; the re-export on build-5 reading back the id before anything is named; the corrected sha with its green and the id 0x4de7b836cc40a4ea by 21:25, then the one last cut. Nothing in the verdict line changes (the tests the same, the object's pack and id standing); the four P01 references and the fleet's F05 rows were read on packs of the object's draw (6df3d430) and stand. THE CLASS: a reporting change inside a function the acceptance consumes is an object change; a green suite does not catch it when the suite compares against pinned packs of the same tree; the read-back of the exported id against the object's is the gate. V6-10's rows all read back ahead of 23:30 (docs/analysis/v6-10-guest-repin-2026-10-08.md, b5b82c958 on p22-stage-2): the deterministic rebuild (three builds, identical ELF and vk bytes; pin 7d38077df); the rebuilt host reading the pinned ids back on build-2 and build-3 (--mode id; SP1's key setup deriving the manifest's id before each prove); genuine compressed proofs under the new pair: the old fee mode (fixtures/fees-v1-shards2.json on build-3: VERIFIED, prove 111.1 s, 1,272,961 bytes, verify 0.035 s), the new fee mode (a block exported from the D4 node 930b6322 with proving_payment_activation_daa 0, fixtures/d4-block-149-payment-on.json on build-2: VERIFIED, prove 68.5 s, verify 0.079 s); the exporter replaying the D4 node's 149 segments with every state root equal; the tests green on build-2; the stated limit: block 149 carries no transactions, so the new-mode proof exercises the mode, not a moved wei (the transactions row owed with the D4 merge onto 2.0.2); one class found: the exec RPC reading provingPaymentToPool as "some transaction paid" (false on an empty block with the mode on), fixed on node branch proving-payment-flag. V6-08: the Mac a push host from here; both self-tests re-run on build-2 (box-prover: chain_health 11 cases, task_mode 5, shard_candidates, preflight_verdict 8; prover-outcomes: 8 log jobs, 4 state rows, conservation), rc 0; the node side 7d6cbd21 on v608-proving-tasks-node (igneum-exec 94 passed, its 8 among them, one failure the tip's own reannounce_slice test, the node lane's); the final sha at 02:00. Stage A of the 2.0.1 roll closed at 20:56:53: all 24 kept mining boxes on the kit ("igneumd 2.0.1", 013986fe / f922b216 read back on each, the reference stream served, the pack gate PASS), no Illegal instruction, no refusal, no error; hub-1's last 300 EVM blocks after it on 9 distinct keys (lp-4090-12's solo key stopped at 20:48 fading at 127; the fixed miners' keys 0x8db0505b 56, 0xe12b8e7e 44, 0x53fe9802 40, 0xab46e78b 16 and four small): the chain's fresh blocks the fleet's again; stage B (the 40 non-mining nodes) rolling in tens since 20:56:54, then the 23 peerless on empty datadirs, dn2-2 last, hub-1's miner on the shipper's word; no lock yet, the signing climbing; the F05 and F10 pods destroyed on their done lines (evidence read back on build-1 first). The adversary lane's 21:30 delta to the landing hand at 21:05 ("complete bb8cf8c30": the placed 32-lane comparator and band, the ticket reconciliation, the formal memory model, F05 on both shapes, the layer-8-off hybrid row, the transition matrix, the batch adversary-20261008-placed-8lane with eight cases); the placed 32-lane full core at 22:45 its own delta. +FLOOR LANE 2's SLIP, ONCE (21:0x BST): the pod's 60 GB disk filled (24 GB of gate-level VCDs from ten parallel runs); the window core's route written, its row re-run from the routed netlist (parasitics from global routing, the SPEF unwritten, plus or minus 15 percent) landing 21:40; the tile and crossbar rows relaunched and the core8r64 and core32 placements resumed from their last written stage, 22:30; core32r16 and core32all after; read back (22 GB free, the netlist present, the relaunched runs alive); the Makefile deletes VCDs after each power report. +V6-08 AT ITS 02:00 CLOCK, DELIVERED 21:0x: the igneum branch v608-proving-tasks at 7bf33fd1b (the design, box-prover.py, prover-outcomes.py, the gate line in pre-push.sh and checks.txt), the node branch v608-proving-tasks-node at 201d40d2 (off successor-2.0.1's tip ff35cf26, to the node lane's gate); read back on build-2: igneum-exec 95 passed, 0 failed (eight V6-08's, known-failed first); cargo check -p kaspad with the igneum-pow feature rc 0 (the freeze guard off for the check only, master's igneum-pow a65e4c5a not the freeze's cbc5bd0a); the two python self-tests PASS; two 2.0.2 tip faults found and fixed by the node lane (igneum-exec not compiling on a let chain in an edition-2021 crate; the re-announce ring test disagreeing with its function; db096a6e, ff35cf26); the word to land given at 21:1x. +ECO-05 BATCH 02 LANDED AHEAD OF 22:00 (21:07 UK, floor lane 3, read back on build-4 and from the mirror): the driver reading the override back on its first line (the RX 7600 at 5.4 microjoules and 15.79 MH/s, modelled; the B580 at 10.0 microjoules and 11.0 MH/s, watts estimated), 2,592 cells per cube at both proving efficiencies on P12's approved grid; 6 of 48 world-demand cells sustainable (10R at 0.03 and 0.10) against batch 01's 3, the added world on a 1.6 percent margin of the 7600's modelled entry cost; 0 of 108 cells per specialist passing all three envelope lines; RUN, FAIL, unchanged; run eco05-20261008-02 at manifest 3f2050ab written to ECO-05, ECO-01, ECO-08; box master 4c679226, all eight mirrors; master's rows at 21:07: ECO-05 FAIL, ECO-01 RUNNING, ECO-08 RUNNING; one slip once (a 20:57 attempt ran master's driver without the override hook and reproduced batch 01, discarded); batch 03 on the metered 7600 knee the morning's after the AMD-and-Intel lane's row (about 05:30). +F04 ON THE REFERENCE APPS (master 2caa033a2, 20:46 UK): the light service mapping lockKind to lock_state beside every certificate (/light/checkpoint, /balance, /receipt); /light and /receipt printing "recovery lock, not final"; the receipt file carrying lock_state, the one-file verifier printing it and refusing a receipt claiming final under a reported recovery lock; the terms block on all three pages; /oracle and its README saying recovery locks are not accepted by the Sepolia verifiers (two-thirds only, fail closed; no contract change); run ra-20261008T1915-ver recorded (VER-03/04/06/08 RUNNING with the F04 cases in coverage; VER-01/02 FAIL by design; VER-05 in its own cell FAIL on this run: the public read node reporting startedFrom snapshot where it reported genesis at 17:46, the build-server lane asked which restart, default the row FAIL with the reason). Read back: the light service on build-1 restarted by its unit's pid at 20:47, answering checkpoint 270 with no lock_state field yet (the node has none until the 2.0.2 field); the pages not yet served (the edge last-modified 19:47:18 GMT: the rolling deploy retired under the founder's word, master's pages riding the site lane's next deploy). +THE F04 PAGES LIVE (read back by the reference-apps lane at 21:1x UK): at the edge since 21:08:23 (last-modified 20:08:23 GMT on /lc/core.js, fetched past the cache): /light and /receipt carrying the Recovery lock definition and the lock-state code (lockStateOf in the served core.js), /oracle the "recovery locks are not accepted by this verifier" line; the site lane's deploy took master 2caa033a2 on its own landing; the served text unchanged until the node's 2.0.2 field pairs. +BATCH 02's MEANING (the research lane, 21:1x UK): the added world (10R at 10 cents: USD 316 M a year, 14 to 16 classes across 2 vendors) rests on a 1.6 percent margin (the 7600's modelled entry cost 1,206 against the 1,225 equilibrium), kept by a metered knee at or below 5.4 microjoules and taken away above about 5.6, so the morning's batch 03 decides one sustainable world and no verdict; the served ECO-05 sentence made exact now on the coordinator's word ("one world on the measured cards; a second on the RX 7600's modelled knee, which the metered row keeps at or below 5.4 and takes away above about 5.6; FAIL either way"), in the design's 10.0x for the amendment landing. +THE KIT LANE's TREES (21:09 UK): master 2caa033a2 (the counter-asic-4 code revert 98976b31 in) merged clean into class-v5 (025978de3, page tip 534078bb9) and class-v6-kits (43ed8d9e9), one full gate each GREEN in ci mode on box 2 (87 checks; tools/class-v5/gate-remote.sh running the gate in ci mode on box 2 after local-mode runs read environment reds only), both on both mirrors; class-v5 (the (c''') floor, the AP-F4-1 agreed form, the verified last resort, spec 1.4.7 and 1.8.6, the harness and the kits) told to land itself through the merge tool (its Mac gate the push host's text checks, no cargo; rule 24's crate gate on the box). + +THE MASTER-LANDING LOCK AND THE REGISTRY ON 12b5cf42 (21:11 UK, the steward). The lock live on build-1 (/srv/builds/_locks/master-landing, holder "pid host branch utc"; self-tested: taken and released, a stale holder reaped, a fresh holder queued to the cap, master merged into the branch under the lock stamped as the union of two gated parents; two classes found and fixed with self-tests: the reap threshold was the wait cap, now IGNEUM_LOCK_STALE 1200 s separate from IGNEUM_LOCK_CAP 1200 s; the lock's globals shadowed by locals inside lock_acquire, the first live run queuing behind an empty holder for 7 minutes, stopped by its pid file); the INT landing under it: box mirror master 12b5cf42 (merge of ci-int-suite 79f7f78c, 88 checks, pushed on try 1 at 21:11, every mirror); the F03 landing queued behind it (refused only for its own text conflicts in three append-only check lists, resolved; its gate on c0516d3a, landing about 21:20). THE REGISTRY on 12b5cf42 (written only through test-record.mjs): 18 suites, 190 cases: NOT RUN 182, FAIL 6, PASS 2, 73 in progress; GOV 8 NOT RUN; GPU 8 NOT RUN; POW 8 (FAIL 2: the two kills, NOT RUN 6); ADV 8 NOT RUN; ROT 8 NOT RUN; ECO 8 (FAIL 1: ECO-05); EVM, ZKP, CAP, INC 8 NOT RUN each; FIN 8 (PASS 2: FIN-02, FIN-07); VER 8 (FAIL 3: VER-01, VER-02 by design, VER-05 on the snapshot restart); OPS, UX, COM, LEAD 8 NOT RUN each; REV 44 NOT RUN; INT 18 NOT RUN. The served /acceptance embed the site lane's copy from before the landing (17 suites, 172 cases, no INT), the regeneration from 12b5cf42 asked of the site lane for its next landing. On master since 21:1x: the shipper's per-case batch form (UX-06), the energy lane's GPU-03 batch (NOT RUN in progress), the census lane's census:class-v6 PASS. +V6-08 LANDED (21:12 UK, read back from the mirror): box master f8b7883ee (Merge v608-proving-tasks 7bf33fd1 into master; the branch's full gate 85 checks in 377 s, the merge's light gate 7 in 52 s; rule 26 touching no moved path; no registry rows); the node side 201d40d2 on v608-proving-tasks-node with the node lane for its gate onto successor-2.0.1; nothing open on V6-08. +THE WORKERS PAGE's MINI ROW AND THE DL HOST (21:1x UK, the build-server lane): merge-workers.mjs on build-1 emitting a "mini" row ("igneum-mini, the Mac build box, M6") from /srv/workers/sources/mini.json, down after 120 s, appearing when the mini's own launchd collector pushes mini.json to build-1 (the relay lane's step; the founder's LAN unreachable from the boxes); the page 9 of 9 up at build.igneum.network. A blocker, not a slip: the Arc kit for PC 2 (the gen6 zip 735e1411) and its jobs cannot reach PC 2 tonight because the OTA feed and the kit publish through the dl host (igneum-dlsite on Vercel) and three concurrent Vercel deploys are running (the 17 GB class the founder flagged); the kit staged and the jobs signed locally, publishing on the first clean dl deploy (the dl-publish-from-build-1 migration at 21:30); the Arc row the morning's. +THE DL BLOCKER WITHDRAWN (21:14 BST, the build-server lane, read by pid): one deploy of igneum-dlsite, a single process tree (pids 68043 to 68784, cwd igneum-dlsite, about 1.5 minutes elapsed) from publish-jobs.sh add --kind update-now --title "2.0.1 is published urgent" (the OTA notice following the shipper's Windows 2.0.1 entry live at 20:56), an owner and an in-tree pid; the "three concurrent" was that tree's descendants miscounted; the 17 GB class not recurring (one deploy about 6 GB); the Arc kit's dl deploy serialized behind it, the Arc able to run tonight; the dl-publish-from-build-1 migration the durable path, its first clean deploy's time the line carried. The shipper's Windows 2.0.1 entry live at 20:56 (read in the build-server lane's line). +THE SUCCESSION CASE ON THE F01/F02 FIX NODE (3f672661; the two-record shape, floor 360, window 300; build-8, 20:56 to 21:14 UK): five of six refusals read, each on the right ground (below H the next pair refused on its pair and the prior pair on its statement; in the window both on their statements, none on a pair; after H+W the prior pair on its pair), nothing paid, no stale refusal replayed (the F01 shape gone); the sixth (the next-pair proof after the window) unread for a harness reason (the attacker's one mining thread keeping its dead chain ahead of the honest pair for over 150 s, its last carrier never read); the harness pausing the attacker's miner for the rejoin (58c4bda99), the rerun from 21:15; one slip once: the harness RESULT and the registry batch 21:30 to 21:40. The p22-stage-2 landing in flight on the box master (the stages' code and documents, elf/ unchanged at the served pair; pin C held on p22-stage-2-pin since the 2.0.1 kits embed master's elf/). +THE POOL PAIR's FINDING (21:1x UK, the pool seat, the cause CORRECTED at 21:2x): UX-05 PASS in the crate (suite 51 of 51 on build-2 at pool-2.0 e063fdcd: the vote-key known-pass and known-fail, the open pool's sidechain key check, the same-nonce-other-template wrong_hash, the TLS binding; batch pool-2.0-20261008-02); the live pair (build-2's devnet-4 node, the pool daemon, two CPU members) reaching authorise, class v5 seeds, jobs and shares, then every share read wrong_hash. The first reading (a class v5 igneum-pow version skew between pool-2.0's release-2.0.0 base and the node) was WRONG and is struck: the pool lane read back and the pool seat confirmed from the three binaries that pool-2.0 e063fdcd's igneum-pow is byte-identical to the freeze 1c420786 (tree 93c36844, fingerprint cbc5bd0aa10585c8 on the node, the pool and the member alike), with the identical epoch seed 1a87e870, day 20734, class v5, era 7c36b833. THE REAL CAUSE from the retained logs: build-2's node never reached synced on devnet-4 (looping on "class v5 execution catch-up" against peers that keep closing the connection), so the class v5 state leaves the pool and members fetched by igneum_getPowStateLeaves for the epoch's seed block were the still-settling catch-up state, not the node's state at template time; a class v5 lane hash over unsettled leaves does not match the node's. THE CLASS TO KEEP: a class v5 pool needs its node fully synced before it verifies shares, because the dataset is keyed on settled execution state. The pair brought down by pid; the live UX-05 and UX-04 rows re-run on a synced devnet-4 node with the 2.0.2 kit (pool-review-b-202 off release-2.0.2); the registry batch 03 carrying the corrected cause; two real pool fixes landed on pool-2.0 and in the 2.0.2 take (the class v5 day-state provider, the base unit from the node's network), gaps any 2.0 pool would hit. +The build-2 pool pair down by pid file (m1 lease 1483266, m2 1484824, the pool daemon 1398825, the node 993506; 0 attack-pass leases, 0 pool2 processes), read back 21:1x; nothing of the pool seat's on any box. +CLASS-V5 LANDED (21:17 UK, read back from build-1's mirror): class-v5 14febf3b5 on master at dcc59359a through the merge tool (its gate GREEN on 14febf3b5, 87 checks; rule 26 and the evidence check passed; the five mirrors the same sha): the (c''') floor and its census, the AP-F4-1 agreed form, the verified last resort, spec 1.4.7 and 1.8.6, the harness with the attempt-3 check, the class v5 kits and the page; one slip once (a page row committed on the branch while the first merge run gated it, so the stamp missed the tip and the tool stopped; the class: no commit on a branch while the merge tool gates it); class-v6-kits 8da8ed574 landing the same way from 21:18. +THE HOUSE BAN AGAIN (21:17:45 UK, the shipper, from the mini's 2.0.1 node log): the mini's node refused a relay block (f09df69b, the carried-proofs path above the fork in its kept datadir), dropped build-1's seed as "misbehaving: re-advertised the refused block", and the two public seeds answer "peer is banned": the house IP banned again as at 19:30, so neither the mini nor PC 2 (same house, no peers since 21:05) can peer (the inbound cap not the cause: build-1's seed accepted the mini at 21:17:45 before the mini dropped it); the fix: the fleet lane unbans the house IP on the seeds with the ban list read back; the mini and PC 2 restart their nodes on EMPTY datadirs (their kept branches above the fork trigger the refusal; the fleet's rule for the peerless boxes), the mini by the shipper's hand after a line to main, PC 2 by the relay agent; the rejoin class (an IBD marking a proof-wait block refused, then the N6 ban) the node lane's e9ab052f on 2.0.2; the seed topology (the fleet's nodes stop dialling the public seeds; build-7 and build-8 at 128 inbound; the RunPod seeds at 32; seeds.igneum.network from 2.0.2) the second fix; lp-4090-07 (213.192.2.71:40045, a packaged dial target) reading closed at 21:18, its restore asked. +THE ACCEPTANCE FIX HOLDS (21:2x BST, the hash lane): the re-export of hl-v6-all at class-v6 bc2deb208 reading back program id 0x4de7b836cc40a4ea (generator 6, attempt 0, loads_per_hash 256) on build-5, the acceptance the object's again; one slip once: bc2deb208's suite one red (the pinned-pack byte-identity test, a comment on program.h's define lines), fixed in the next commit; the final sha with its green by 21:40 (was 21:25), then the node lane's one cut and the reg64 packs' re-export at that sha with their tarball shas by 21:55; registry batch 2 (bench:fleet-pods with the F05 rows on the post-review object, suite:pow on the review-fix tree) on master at d8a2fa323 through the recorder's replay. +THE F03 LANDING, ONE SLIP (21:20 to 21:28 UK, the steward): refused behind the INT landing on three append-only check lists, then three evidence-rule refusals of its own batch (prose in an evidence field reading as a path; the F0 page GOV-01's evidence, so GOV-01 moving with it through the recorder) until the check read clean at 21:20; the gate on 75bc6f5e, landing under the lock on green: release-manifest-check with the provenance rule (the proving manifest's source_commit an ancestor of HEAD), build-from-manifest, the same-work spec page, p01-vectors --job-context/--phase (self-tested on a fake worker across the boundary), the map cells harness:release-manifest and harness:same-work (R2-F03-R01 to R03 off the not-run list), the batch f03-manifest-20261008-01: R2-F03-R01 PASS on release-2.0.1's final tip c30ab32c (every component building from the manifest on build-4 at 21:14, the pool included now that the release tree carries pool-review-b; the log on build-1 under /srv/artefacts/tas/f03-manifest-20261008-01); the F0 page's cut-tip row naming c30ab32c beside the shipped tip aa0e0f45. +THE AMD/INTEL REGISTRY LANDED (21:20 BST, the landing hand): box master c170cf4e0604ae98c497ec8aa57de1542fd2551e (Merge amd-intel-energy-registry-docs 92dc857d; 88 checks, the evidence rules green): the cell bench:amd-intel-energy, the batch amd-intel-energy-20261008-01 replayed (GPU-03 NOT RUN in progress), the harness page regenerated; thirty-one landings. The adversary lane's delta (bb8cf8c30): gate GREEN, master merged, then refused by the evidence rules: multi-family-adversary.md changes and master's rows ADV-01 to ADV-05, POW-03 and POW-07 name it as evidence; the lane's batch covering ADV-01, 02, 03, 04, 07, 08, POW-03, 04, so ADV-05 (the adversary lane's) and POW-07 (the steward's kill cell) must move in the same landing by their owners' batches; the sha 21:45 if the owners answer by 21:35. +The adversary delta's two rows: run_id team-2026-10-08 is the coordinator's (the approved registry's tonight-evidence records at 50ff1611f, before the recorder); the landing hand records the one-cell repeat batch for ADV-05 and POW-07 at the delta's manifest, named as the coordinator's register landing, master's current statuses kept, and lands the delta. +THE SEED TOPOLOGY READ (21:16 BST, the build-server lane): all three box seeds already at the 128 inbound cap (build-7 and build-8 igneum-dn4-seed --maxinpeers=128 on 0.0.0.0:26631, active; build-1's seed and hand the same on 26631 and 26671), the 32 cap the RunPod containers' only; the fleet's own nodes dropping the public seeds from their dial list the fleet lane's env change; the public seeds' 128 slots for home miners. LANE D's 6.12 ON THE FROZEN OBJECT'S TREE: PASS (21:2x BST; class-v6 ac86d7910, the full class string mx8+sh256x27+state+reg64c+nowin+fold+rw, the packs' draw read back equal on both ids): under the harness's predicate, width 4, 3,000 eras: r = 0.144, mean attempt 0.17, max 4, 0 exhausted, (c''') 0.46 percent of reaching candidates; under the crate's OWN predicate, width 4, 1,500 eras: r = 0.137, (a') 8.4 percent against the harness's 8.8, (c''') 0.40 percent: THE TWO PREDICATES AGREE on b24dfc162's fix (on 04442d9ca the same column read r 0.05 and (a') 0), the row the freeze asked for; width 1, 1,500 eras, r = 0.134, 0 refused by either floor; the bit-R read at 6.11's level (over 300 sigma in 3.9 percent of eras, the fold's half); attack-f8 on the mirror-valid subset, 64 of 64 seeds at 2^20: 0 hot sets, 0 over 1.2x, 1 of 64 on the bucket class; the verdict PASS on the acceptance's side of the frozen object under the full rule and under the rule the chain runs, now the same; the landing held by the evidence rule (the steward's kills-20261008 batch citing lane D's then-unlanded log post-w4-family_gate_era_census-0-3000.tsv as POW-03's and POW-07's evidence), the fix a repeat batch for the two rows named as the steward's inside lane D's landing (not a rename, which would leave a stale citation); the class for the recorder: a cell citing an unlanded file should be refused at record time; the hash lane's V6-02 slip named in 6.12 as theirs, lane D's rows binding ac86d7910 only. +THE THREE EVIDENCE CLASSES CLOSED IN ONE STEWARD LANDING (ci-evidence-added off master c170cf4e under the lock, by 21:40 UK): the kills-20261008 batch re-recorded with POW-03's and POW-07's evidence narrowed from the class-v6 directory to their own files (FAIL kept; the directory citation the steward's, firing on every class-v6 landing); rule 2 binding modified and deleted evidence files only, so a file first appearing in the tree lands free (lane D's 6.12 needing no repeat batch); the recorder refusing at --record a relative evidence path that is a directory or not in the tree, with self-tests. THE WINDOWS 2.0.1 ENTRY live at 20:56:34 BST (Setup ce6bb00c, both token folders and the public alias, read back from the dl host; the manifest urgent since 21:11:28, which the mini applied on at 21:11:42); the combined exes the build-server lane's about 21:30. THE HOME RESTARTS HELD on one fact (the shipper, by 21:35): PC 2's 2.0.1 IBD from the seed ended at block 3847 on "the proofs of 10 carried records are not held yet", and no empty-datadir node on 2.0.1 has synced past 3847 since the outage (the fleet's peerless boxes starting their fresh syncs; four that tried across the fork reading the same class); the first one past 3847 and mining the word for the mini and PC 2; a stall at 3847 means the home machines wait for the 2.0.2 node entry (the record store following the block store with the IBD skip, the top of 2.0.2) rather than churn. +A coordinator order withdrawn once (21:2x): the "PC 2 restarts on an empty datadir by 21:45 regardless" conflicted with the shipper's hold on the 3847 fact; the relay lane refused to pick by guess and asked; the hold stands (the first fleet box past 3847 on 2.0.1 or the 2.0.2 node entry is the word). +THE ADVERSARY DELTA's ROWS (21:3x, the landing hand): the repeat batch first proposed (ADV-05's owner_lane on master reading the k lane with no map cell) dropped once both owners answered: the adversary lane recording method "model" on adversary:mf-placed and adversary:d2b with ADV-05 added to adversary:mf-placed as RUNNING; the steward's kills-20261008 batch re-recorded with POW-03's and POW-07's evidence narrowed to their files (docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md among them) in its evidence-classes landing by 21:40; the delta holding for that sha (polled to 21:45), rebasing on it and landing with no repeat of anyone's rows (past 21:45 without it, the POW-07 repeat alone); the sha about 21:55. +THE 3847 PROBE (21:3x, the shipper's word to the relay lane): PC 2 restarts fresh now as the probe for the one unproven fact (whether a fresh 2.0.1 node syncs past block 3847 or stalls on the carried records), its after-line (peers, synced, the tip, the first accepted block or the stall) within four minutes; the mini restarts fresh by the shipper's hand the minute PC 2 reads past 3847 and mining; a stall means the home machines wait for the 2.0.2 node entry, the clock said once. P22 STAGES 1 AND 2 LANDED (21:22 UK): box master 24896a8f5 (merge of p22-stage-2 fca720ec, gate green, all seven mirrors): the guest code (the inputs and derivation commitments, the carried fixtures, the versioned guest input format 3, the pin tool's provenance fields merged with the node lane's succession block), docs/design/consensus-proof-stages.md with stage 2 as shipped, docs/analysis/v6-10-guest-repin-2026-10-08.md with every row, proving/igneum-prove/elf/ unchanged at the served pair (pin C held on p22-stage-2-pin b5b82c958 until main names H). +THE HOUSE UNBAN AND THE TOPOLOGY (21:18 to 21:24 BST, the fleet): the house IP unbanned on dn4-seed (39 addresses cleared, one stuck entry re-cleared at 21:19), lp-4090-01 (34) and lp-4090-02 (4) at 21:18:55, lp-4090-07 holding none; the seed guard re-running the unban on those four every five minutes until the 2.0.2 rejoin fix (no allow-list on the node's ban path); build-1's seed and hand the build-server lane's (the same ask, 21:40 default). lp-4090-07's node died at 21:03:21 in stage B's restart on "While lock file .../meta/LOCK: Resource temporarily unavailable" (the new node started while the old one still held the datadir lock; the only such death among 65 rolled boxes), restored by pid files on its kept datadir at 21:20:01; the roll script now waiting for the lock's release before each start; the panic a 2.0.2 item. The seed topology: SEED on every non-hub fleet box rewritten to the seven hubs (lp-4090-02 to -08, every port open from the Mac), the 64 rolled boxes restarting in tens on kept datadirs since 21:24, stage C and dn2-2 taking it at their roll restart; the fleet-slot counts on dn4-seed and lp-4090-01 read at 21:35, 21:50 and 22:05. hub-1's lock line held: at 21:04:59 and 21:06:01 it logged checkpoints 263 then 262 LOCKED with "signed 0 = 0.0 percent" (pre-outage checkpoints at blue score 7,784 and 7,754, taken from relayed certificates, no local tally) while its own signing read 25.86 percent; whether rule v4 counts those as locks is the node lane's; the lock the register wants (two thirds of the weight signing on the fleet's chain) not yet come. The pid-only fleet tree (every stop through a pid file, 17 devnet-2-era scripts retired) in the merge gate. +THE ARC READ (21:3x UK, the kit lane): PC 2's job ran zip 2's kit, not zip 3's (every file sha in its RESULT lines zip 2's: kernel_bound.cl b2729b52 and fd550b21, worker 90aaa1f8, against zip 3's c97e630a, d83e6f14, be169903; the fetch not replacing the dir's old extraction), against the generator 6 expected value, so the "mismatch" was the kit and script pairing, not the device; the reading standing as the generator 5 Arc read: the B580 computing 59e6708e46f1e87c on the generator 5 all pack, equal to the other six, so the generator 5 record on the window object is SEVEN of seven (CPU, CUDA 4090, RTX 5090, Metal, Apple OpenCL, RX 7600, Arc B580); the generator 6 record at four of six (the Arc re-run with zip 3 in a fresh dir asked of the build-server lane, 22:30 default the morning's; the 5090 and 7600 the morning's); the identical leaves.bin across the two packs by construction (one era state, 93 leaves); the freeze wording gaining the Arc on the generator 5 side. +PC 2 at 21:23:14 BST: the restart run found the 2.0.1 node already with 1 peer (the unban reaching it between 21:19 and 21:23; "behind", not synced) and left it alone by the shipper's guard, the datadir in place; whether that peer carries it past 3847 with the kept datadir or hits the carried-records wall read within two minutes; the shipper deciding on the empty-datadir probe. +PC 2's read at 21:23:33 to 21:23:35: peers back to 0 ("waiting for a peer on our chain"); the node on its kept datadir refusing the network's block 4c51b17a by rule, banning two peers of its own for re-advertising it, its IBD ending "parent 306271ad is invalid": the kept datadir on the old branch rejecting the fork's chain, the stall case the shipper pre-authorised; the empty-datadir restart running on PC 2 from 21:24 through the relay agent (api/quit, the devnet-4 datadir moved aside and kept, the app started, 20-second reads for 150 s, the probe's verdict verbatim about 21:29); PC 2 the fleet's probe for the 3847 question. +THE NODE LANE's 21:40 LINE. (1) The fresh-sync read: one empty-datadir 7cfa422a node on build-1 dialling the seed, the hand and both hubs, 21:20:36 to 21:23:07: IBD taking the first 3,600 blocks with one-block reorgs, then the executor stopping at tip 1,943 (DAA 3,600, the epoch-1 boundary) for 90 s with "IBD: a class v5 header chunk waits for this node's execution state (class v5 needs the execution state after the epoch's seed block 92137840)", the seed at 6,162 by then; the 90 s window inside the class v5 catch-up's own wait at an epoch boundary, so not yet the fact (it stopped before the 3,685 to 3,847 band where PC 2 and four fleet boxes stall on "not held yet"); the rerun with a five-minute window from 21:34, the result about 21:50 (past 3,847 means the fleet's boxes stalled on their syncer's record gap, not the chain; a stop at 3,847 names the record). (2) The fix's clock: the record store following the block store (the proof bytes of every carried record persisted under the block store, surviving restart and reorg, pruned with the block); IBD taking a block whose carried records nobody can supply only when it lies under a locked checkpoint (the certificate's two thirds standing for the records; the node's exec state following the network's; logged once per block), never an endless wait and never a ban; above the last lock the rule standing; on successor-2.0.1 by 22:45 with its known-failed test (a joiner against a peer whose record store lacks a served block's records syncing past it under the lock), the 2.0.2 pair with read-backs by 23:15, the shipper cutting the 2.0.2 node entry from that sha. (3) hub-1's 262 and 263 at 21:05: locks under the rule (the certificate arm, a received certificate re-tested against both tables, as 270 was), not recovery locks and not an artefact; the LOCKED line printing the node's OWN vote tally (0 on a node partitioned at those indices); backfill of pre-outage indices, so not "the first lock after the roll" (that line: the first lock at an index determined after the roll with two thirds signing on the fleet's chain, still owed). (4) lp-4090-07's LOCK panic a 2.0.2 item: the datadir lock refusal naming the holder, waiting up to 60 s, then exiting clean. +THE FLEET's PID-ONLY TREE (21:33 BST, the fleet): box-prover.py's stops through pids (the SP1 server by the pid owning its unix socket, read with ss -xlp and written to /root/fleet/pids/sp1-server.pid; the worker through its parent miner's pid); 27 files in tools/fleet carrying pkill or killall: the live ones converted to the shared helper tools/fleet/lib/pidkill.sh (kill_pidfile, kill_children by parent pid, kill_sock_owner) and fleet-stop.sh (lib/box.py's three stops, box-kill.sh, kill-node.sh, box-ember.sh, box-rig.sh, box-matrix.sh, box-floor-v5.sh, lib/standing.py, publish-2-move.py), 17 devnet-2-era scripts moved to tools/fleet/retired/ with a README (git grep over the live tree zero); sha 34d4d45e on fleet-pidkill-20261008 in the merge gate since 21:33; read back on tas-p01-3090 under the shim (pkill -0 sleep exits 97; kill_pidfile and kill_children on demo processes rc 0) and on lp-4090-02 (the eight live files zero non-comment pkill lines); the push to all 102 fleet boxes with /root/fleet. +PC 2's PROBE, ONE SLIP (21:24:18 BST): the guard-off run sent api/quit and 90 s later the window host, the engine, igneumd and a miner were still up, so the run stopped by its own rule (no kill by name, no datadir moved under a live node); the cause read on PC 2 (the engine's quitting flag, the app log's quit and node-stop lines, the processes with start times); the restart again the minute the cause names the step (the quit through the engine's own path or by pid); the verdict by 21:40. +F03's second slip (21:28 to 21:36 UK): the gate red on one check (the REV suite's generated form moving under the R2-F03 record, the generator and the recorder writing different key orders; the merge regenerating REV, the gate reading the tree), regenerated and amended as 1fd509d5; the evidence-classes landing (d7689a43) unaffected, 21:33 to 21:35. +The node lane at 21:3x: candidate (c) re-cut as e8773ff5 on the hash lane's final sha (core 184 and consensus 143 green so far on build-1, the rest, the pair and the canaries following), the freeze's last node-side condition; ff35cf26's placement and canary as the 2.0.2 node entry's sha for the shipper, then the 45e7b910 gate (V6-08 plus the finality-backed take) as the next cut; the 2.0.2 items: the proof-bytes persistence half (the archive at inclusion whether or not the pool held the bytes, served to joiners), igneum_getProofRecord, the relay and body-rule size caps against consensus-core's MAX_PROOF_BYTES, the LOCKED line's fractions, the datadir LOCK refusal, F01's two-node test, INT-15's v2 session binding, D4's merge (67912c80) after the freeze with the repinned next ids. + +THE FREEZE TREE FINAL (21:3x BST, the hash lane's closing line): class-v6 1a938abe408eacabf293e675cc30d8fafa03bc8f on the mirror, the suite on build-7 147 passed, 0 failed, 8 ignored; the five packs re-exported at it and read back on build-1 /srv/artefacts/packs/ at 21:26: hl-v6-all 0x4de7b836cc40a4ea (tgz 91314310...9858b1), hl-v6-all-nowin 0xbe1d6f48928cef4a (38ace2a8...9641b1), hl-v6-all-prefix 0x4de7b836cc40a4ea (d4b07747...cd87ce), hl-v6-foldrw 0xd7eba30115d26dd4 (02eff590...46cf8f), hl-v5-nowin 0xdace2893e7653830 (a321c79b...5e721b), all generator 6, each with identity.json; the node lane's one cut on it; the v5 lane's shas for zip 4; the word on nowin given (layer 8 stays on in the frozen object; the knee rows on PC 1 when the runner frees, the cap about 04:40); the hash lane's line closed. + +THE NIGHT RULE (main's words, the founder to bed, 21:3x UK): (1) From this line until 08:00 UK no lane takes a turn that is not a real change: a landed sha read back, a verdict, a measured row, a fault with its fix and clock. The "privately listed, what I need next" close-outs stop entirely; a lane with nothing to report ends its turn with no message. (2) Released, their clocks spent: the worker lane, the census lane, the finality lane, the Ember lane, the DEX lane, the reference-apps lane, the AMD and Intel energy lane, the V6-08 lane, the app lane once its 23:00 quit fix is pushed, the pool lane once the 23:30 binding switch is in, the comparative lane after its 02:30 first landing (its 09:00 chip model is tomorrow's). Staying: shipper, node, hash, fleet, steward, build-server, the research landing hand, adversary and floor lane 2 until their placed cores land, V6-07 until 01:00, relay until PC 1 and PC 2 read MINING-ON. (3) The coordinator sends main at most one consolidated line an hour, only if something changed; otherwise nothing until the 08:00 read-back (the freeze: which candidate, the sha, the object id, D1's status; 2.0.2: the cut sha, the entries, the fleet roll, the home machines; the chain: locks overnight, the key count, any stall; the census; the board: PASS, FAIL, BLOCKED counts; spend; faults with fixes; the morning items for the founder). (4) The freeze at 23:30 is decided under main's rule by the shipper, not held for main: candidate (c) if every suite, the pair and the canaries are green with the object id 0x4de7b836cc40a4ea read back; otherwise candidate (a) fixed; one slide to 00:30 at most; never the unfixed (a); if neither is green by 00:30 the register reads "frozen pending" with the cause and the freeze is the morning's first item. (5) 2.0.2 cuts tonight on ship-on-green by the shipper with the same authority; the home machines take it on the urgent manifest; nothing is done by hand on the chain overnight, the guards do their work, and a stall is recorded for the morning, not rescued. (6) No lane spawns a new lane overnight; a new fault is recorded with its evidence and a morning clock unless it blocks the freeze or 2.0.2. +A FAULT FOUND AT 21:28 (the build-server lane by pid; the coordinator's read at 21:29): a dl-host deploy running from the Mac with no recorded pid, `node /Users/joshm/Projects/igneum/tools/workers/push.mjs` (pid 47013, parent launchd, started 21:28:00; npm exec vercel 50070, cwd igneum-dlsite), re-hashing the 5.9 GB dl/ tree each fire: the launchd agent com.igneum.workers-push (~/Library/LaunchAgents/com.igneum.workers-push.plist), the old Mac workers publish still scheduled after the hand-over to build-1; the class the founder named; the fix: the agent unloaded by its label and its plist retired by whoever installed it (the site lane), never a kill by name; the build-server lane's one Arc deploy serialized behind it with its pid recorded. +THE LAUNCHD AGENT UNLOADED (21:30 UK, the site lane, read back): com.igneum.workers-push unloaded by its label (launchctl bootout gui/501/com.igneum.workers-push), its plist renamed .retired-20261008, launchctl list no longer showing it; its running upload (pid 47013) had ended on its own, nothing killed; nothing publishes from the Mac now but tools/site-deploy-from-mirror.sh with its pid file. Candidate (c) e8773ff5 all six suites green on the final tree (184/143/74/30/38/19), the pair building, then the canaries and the id read-back. The night rule broadcast to every lane at 21:3x with its stay or release. + A SHARED-DEVNET FACT FROM THE FLEET (not this lane's, with the shipper and the infra lane): the Hetzner live seed 188.245.5.161:26611 is still on the old override object (digest eada4bda) 1 h 40 min after the 0.3.20 sweep (the fleet never touches Hetzner nodes, so it was outside the sweep); the 0.3.21 wipe canary c22-1 took five digest-mismatch rejects from it; an app with the packaged peers is refused at the seed and syncs through node1 and the hub only, a fresh joiner with only the seed cannot join, the 14 voters and the hub are unaffected; the owner puts the floor file ov16-floor-900000.json (sha 294f1f80) and the c4459193 pin on it. 0.3.21's STAGING (the node lane): the order dry-merges onto 55768f88 with nothing moving to 0.3.22; the late-join fix is 52e96c94 (70e4601e rebased onto 55768f88, exec suite 33 green with both new tests); f067f7c1, b0444f51 and 437f0438 merge clean in order; 2e32d5f6's one conflict (DST_ADDRESS beside pool-finish's DST_BINDING in consensus/core/src/finality.rs) kept both; the live-file digest eada4bda after each (every switch at never); the staging waits on the shipper's sweep-end word; the re-pin held. PC 2 DOWN AGAIN (main, 16:5x UK): the founder takes PC 2 down for cable work (PC 1 back but his desk); both PCs out of the sweep's waves, each updates on its poller on return; no PC job to PC 1; the Windows G1 completed before the outage, nothing reruns. 0.3.21's SECOND GATE LINE on 55768f88 (sha256 279b1b690e854fc9): the ten-minute mixed-version gate beside the 5899f603 pair, 13:37:40Z to 13:47:52Z, SUMMARY PASS (one digest b0afb2ee on five nodes; 223 new and 381 old blocks accepted by the old hub, 0 rejected; counts equal at 319, 486 and 604 through both clean joins and the restart step at 13:45:22Z; no panic); the node lane's two lines on 0.3.21's first candidate complete, in plan 6.9 on ca3-v4-node; the fleet's set on it (the bare-child 12 GB line, the wipe, the kept read, the cases) is the fleet's. 0.3.21's FIRST GATE LINE on 55768f88 (sha256 279b1b690e854fc9, the string read back; pairing igneum-pow 8c728ca3 at byte 5): the digest gate 13:35:41Z to 13:37:19Z SUMMARY PASS (a89be8a7 on both binaries with the peers; db9a85f9 refused, no peer; the live file's eada4bda unmoved); the ten-minute mixed-version gate from 13:37:40Z, line about 13:50Z. The 0.3.21 order as the shipper sent it: 55768f88; f067f7c1 and 70e4601e; b0444f51; 6eb21fc9; db28d331; then the re-pin from 8bdcbdd8 on the coordinator's word; suites between, the digest read after every one; the mirror's release-0.3.20-node back at the pin c4459193, release-0.3.21-node open at 55768f88. THE LATE-JOIN COMMIT (N9's second half, the node lane): 70e4601e on the box mirror as branch proof-hold-fix, from c4459193, two files (igneum/exec/src/proving.rs, protocol/flows/src/v10/proving.rs); the gap was the fetch side on the joiner (the served record ran the native check against the joiner's trailing exec state before anything was stored, the check refused it, the proof was never held, the body rule read "not held" for 20 s and failed the IBD); the fix holds the proof by hash before the checks (the pool entry still needs them) and the serve side says when it holds fewer than asked; the exec suite 32 passed at 13:26Z with the known-failed shape first, the flows check green 13:28Z, igneumd on build-1 at the 0321 worktree path built 13:32Z, sha256 17649eeb2f7d1290, string read back; with the testnet lane (the resume form, B alone); it joins the 0.3.21 staging as its own commit. THE WIPE CANARY ON c19-1, c4459193 (sha 45be9b02d1b002f5, string read back): FORM END rc 0 at 13:50:53Z. Wipe synced 13:35:50Z (57 minutes, inside the 98-minute class); mining 13:36:00Z to 13:47:07Z, 66 mined, 66 accepted, 0 rejected, isSynced true at the tip throughout; the hub holds 41 of its blocks in its last 700 with 0 rejects (13:47:09Z); the restart on its kept datadir at 13:47:15Z: the old process stopped at once (the new process's first lock line seven seconds after the marker; the watchdog held nothing, the b7cc37e7 fault closed), synced again at 13:48:39Z after 84 s, 109 templates read with max 3,432 ms and 0 timeouts; the kept read on pool-1's 0.3.17 copy on the same pod passed at 13:38Z (the rewrite line once, a clean second start). The pin's set on c4459193: the digest gate PASS, the mixed-version gate PASS, the wipe canary PASS, the kept read PASS, the restart PASS, the 12 GB line proves and verifies (paid is a race, not a gate); CASES END from c20-1 (about 14:50Z) is the last pin line. THE INTEROP FACT stands from the void run: the 5899f603 hub accepted 235 object-byte-5 blocks from the 8097d600 node with 0 rejected, one digest on all five nodes on the live sixteen-field file. The gates: the digest test and the kaspa-pow vector test (the amended devnet epoch-0 id 1a4230699a6b9c60 must equal, c120d7963abdcd96 must differ, the v3 control unchanged) on the box; the mixed-version Devnet 2 gate (the amended 0.3.20 node beside a 5899f603 node for ten minutes on the live file without the v4 fields) after the Mac build; the fresh-join canary the 0.3.20 cut's | | Main's rulings (7 October, morning) | no generator change to v4 on the live devnet; the record's null is the window model with numbers, sent by the hash lane to the attack-pass lane so AP-F8-1 re-gates against it; a fault beyond the model (a low-entropy source at site 15) stops at the coordinator with the two options priced (a 0.3.19 class amendment before the flip, or the flip held at the floor), nothing shipping without the founder's word; the tighter tail, an acceptance bound on the hot-set share, is a CLASS V5 item (sent to the v5 lane a6410f3b8abefb762 with the 64-seed census as its gate; the bound's number follows from the model) | diff --git a/docs/plans/evidence/UX-01-20261008.md b/docs/plans/evidence/UX-01-20261008.md new file mode 100644 index 000000000..c7340e6b2 --- /dev/null +++ b/docs/plans/evidence/UX-01-20261008.md @@ -0,0 +1,21 @@ +# UX-01 evidence, run ux-01-20261008-win-2.0.0 (8 October 2026) + +Onboard ordinary owners on native desktop apps, the team-run half: the two Windows PCs take the Igneum Miner 2.0.x Windows +entry (2.0.0 live 18:44 UK, Setup exe 793a631d, manifest aa354ed5; 2.0.1 about 20:55 UK with the coinbase-over-u64 fix) +through the app's own update path, with no click after the install and mining on every card (the founder's rule of +18:0x UK), observed through the apps' own intake uploads and the relay agents. The P10 study (30 unaffiliated +participants) is NOT RUN: not yet recruited; never a staff run. + +## Shape +- PC 1 (ae432dc7): Igneum Miner 0.3.26 since 17:26 UK, RTX 5090, RTX 5080, RX 7600; mining off under the Devnet 3 off order; the 2.0.0 installer downloaded and verified 18:58 UK, the install queued in the hash lane's order (update-now-20261008-181124). +- PC 2 (1ccfe586): RTX 5090, RTX 5060 Ti, Arc B580; mining off since the Devnet 3 off jobs (#1519 api/pause 16:38 UK, #1521 17:15 UK). +- The read-back: the engine's "update-return: app up after the update from " line at intake, then the relay run "mining on after the 2.0.1 install" (mining-on-after-200.ps1: wait for 2.0.1 or later, clear a stale install-running.flag, api/resume, start the app if silent) printing `RESULT MINING-ON ok|partial version=... network=... node=... synced=... paused=false cards= rates=...`. + +## Read-back lines (time UK, PC, the line or the fault and its class) +- 19:22:01 PC 2: `update-return: app 2.0.0 up after the update from 0.3.26 (node igneumd 2.0.0, machine 1ccfe586)`; node started 19:22:05; `[ok] mining resumed` 19:22:13 (no click: relay run #1742 cleared the pause through api/resume). +- 19:28 PC 2 (#1754): `RESULT MINING-ON partial version=2.0.0 network=devnet node=no peers synced=False paused=False cards=3` (every card waiting). Class: the devnet-4 hubs' outage (every dial target down 19:15 to 19:17, back by 19:29), not the install. +- 19:41 PC 2 (#1757): `RESULT MINING-ON partial ... node=behind peers=1 synced=False tip_age_s=1289`; IBD completed 19:29:16, the node then "behind". Class: the 2.0.0 miner refuses every template whose coinbase exceeds a u64 (the shipper, 19:5x UK), fixed in node 777214af as the 2.0.1 entry. +- PC 1: pending (0.3.26; the install runs when its queue reaches update-now, then 2.0.1 through the same path). + +## Status +- 20:36 UK: RUNNING; the 2.0.1 read-backs (relay runs #1762 PC 2, #1763 PC 1) are the next lines. diff --git a/docs/plans/finality-native-runs-2026-10-09.md b/docs/plans/finality-native-runs-2026-10-09.md new file mode 100644 index 000000000..d4b4a5c57 --- /dev/null +++ b/docs/plans/finality-native-runs-2026-10-09.md @@ -0,0 +1,48 @@ +# The native finality runs of 9 October 2026 (the node lane; Review B F04 and I03; V6-09 beside them) + +Status: PLANNED (written 8 October 2026, 21:4x UK, main's order under the night rule). The runs start from 00:30 UK on build-8 and build-9 (build-7 for the two-node cache case), under the lease pool, every process under a pid watcher that restarts it and records the restart; the rows land in `sim/results_v2.md` under "Rule v4" with their result files under `sim/finality-attacks-results/` and the registry batch (FIN-08, FIN-02's native half, ROT-05, VER-08's recovery row) through `tools/ci/test-record.mjs`, by 07:00 UK. An accelerated simulation is evidence of the rule's shape, never operating history: every row below runs on real nodes with the 60x file's windows (W = 120 DAA s at 1 block/s) and says so. + +The harness: `tools/finality-attacks/v3.mjs` (three nodes on the 60x file, six voters, one-way delay 300 ms per proxied link, the pass lines of `finality-guarantees.md` 6.7), extended for the rows that need a third island, an equivocating key, a stopped voter, a succession item and a backfilled checkpoint history; the extensions land with the rows. The node: the 2.0.2 line (successor-2.0.1 at or after 45e7b910: rule v4 with the pause fix 6872db13, the lock kind of F04, the finality-backed take of a9ff0a25) in the gate pair under `/srv/artefacts/200-/node-lane` (gate evidence; never a fleet binary). + +## 1. The 40/40/20 case past the window with equivocation (F04, the reviewer's hard FAIL line) + +Three islands by weight 40 / 40 / 20 (keys p0,p1 on n0; p2,p3 on n1; q0 on n2, shares 0.2 / 0.2 / 0.2 / 0.2 / 0.2 across five keys, the sixth key e the equivocator at 0.2 placed by the row), WARM 230 s, SPLIT 420 s (longer than the window after the last lock), HEAL 400 s, 1 block/s in all, rule v4 with the recovery on. + +| row | the equivocator e | expected under 6.2 and 6.5 | the FAIL line | +|---|---|---|---| +| 1a | absent (honest three-way) | no side locks during the split (no island holds more than half of the anchored table), every node pauses, the heal locks within two intervals, 0 conflicts | any lock during the split; any conflicting certificate | +| 1b | mines on island A only (reaching 60 of the anchored table on A) | A recovers once a full window has passed (the recovery lock at the first index past the window), B and C pause, the heal brings B and C onto A's chain, 0 conflicts | a lock on B or C; two certificates at one index | +| 1c | mines dust-valid on A AND B (the 6.5 bound: both at 60 percent) | BOTH A and B recover after the window: two recovery certificates at one index, the measured conflict the spec names; the heal strips e (3.6) and reports the pair under 3.11.4; the history through the anchored lock untouched | a recovery lock presented as final on any surface (lockKind must read "recovery" on both); the anchored history moving | +| 1d | 1c under the pause-only variant (recovery off) | no lock on any side during the split, the pause until the heal, 0 conflicts (the strictly stronger guarantee of 6.5) | any lock during the split | + +Measured per row: new locks per side during the split (index and DAA), the lock kind on `igneum_getFinalityCheckpoints` (final or recovery), conflicting certificates logged, disagreeing locked indices after the heal, the equivocator's strip at the heal (the ban line), every pre-heal lock kept, the first lock after the heal (s). + +## 2. The pause-only alternative with backfill, missing history and the old keys returning (I03) + +Rule v4, recovery off. A chain is run 230 s with six voters, then split 3/3 for 420 s (no lock on either side, the pause), then healed; during the heal window: (a) a fresh node joins from genesis and must backfill every checkpoint and certificate (the historical-checkpoint backfill; it reads the same latest lock as the three), (b) one node restarts from a datadir with its finality state removed (missing historical data: it rebuilds from the chain's carried certificates and must agree), (c) every old key returns and signs (the pause ends on two thirds, the lock within two intervals). Measured: the backfilled node's locked indices equal the others' (0 disagreement), the restarted node's, the first lock after the return, 0 conflicts. + +## 3. Authority succession and strip, restart, certificate arrival order, seed boundaries (I03) + +| row | shape | expected | +|---|---|---| +| 3a | a voter leaves (W7, the leave item carried) mid-window, the rest sign | the frozen table reduces by the leaver (frozen_floor), the next lock at two thirds of the remaining; the leaver's weight never counts | +| 3b | a key is stripped (an equivocation evidence item carried) one interval before a lock | the strip applies before the lock's test; a certificate carrying the stripped key's signature counts it as 0 | +| 3c | a voter restarts between determination and lock (kept datadir) | it votes once (no double vote), the lock forms on time, its locks equal the others' after the restart | +| 3d | two certificates for consecutive indices arrive in reverse order at a node (the fleet's 263-then-262 read on hub-1) | both lock under the rule, the LOCKED line prints the certificate's fractions (the 2.0.2 log fix), no "signed 0" artefact | +| 3e | a seed boundary (the epoch's reference block) inside a pause | the seed is drawn from the chain block below the lead whether or not it is locked (section 8); mining continues; the first lock after the pause names a block past the boundary | + +## 4. The inter-chain verifier and a recovery lock (I03) + +The Sepolia certificate verifier (dex lane's bridge, 0x874D8Be5… on igneum-devnet-4's voter table) given (i) a final certificate, (ii) a recovery certificate from row 1b: it accepts (i) as final; for (ii) it must read lockKind "recovery" and never present it as final (the bridge doc 829b839ec: recovery locks fail closed on the verifier, which reads only weight). The row records what the verifier answers for each and the receipt page's word; a recovery certificate accepted as final is the FAIL line. + +## 5. V6-09, cold compressed verification on the minimum validator (with the enforced-proving lane) + +One validator node pinned to one core (taskset), no warm relay cache (fresh datadir, the pool empty), fed by a relay peer: (a) ten cold valid shard proofs in one block's carried records (the measured 0.668 to 0.710 s a proof on one loaded core); (b) ten expensive-invalid payloads (proofs whose bytes deserialise to the largest accepted shape and fail at the last check) with the relay's pre-deserialise cap (MAX_PROOF_BYTES in consensus-core, V6-08's constant) and the in-flight bound of 8; (c) a forged record under each pair across the key and fee transitions, before, at and after activation, on an unmodified validator. Measured per row: verify seconds per proof, block-validation time against the deadline, the NotReady retries, memory before and after deserialising, the carrier paid exactly once (igneum_getProofRecords on the validator against the relay's), the forged record refused with its reason. The rows go to the test map cell `harness:v6-09-cold-verify` with the F0 fixture, by 12:00 UK 9 October. + +## 6. The two-node cache-history test (F01, with the proving lane's fix 3f672661) + +Two nodes, one with a warm verdict cache built on carriers 1..k, one cold, both fed the same blocks in a different order (the cold one sees the later carrier first): identical block-validity verdicts and identical payouts (igneum_getProofRecords and the paid map equal on both), with the known-failed shape first (the pre-fix node's cached context refusal replayed under a later carrier, the 19:49 UK reproduction). On build-7 by 03:00 UK. + +## Boxes and clocks + +build-8: sections 1 and 2 (the 40/40/20 rows 1a to 1d, then the backfill case), from 00:30, rows by 05:00. build-9: sections 3 and 4 (after the 2.0.2 line's gate ends), from 01:00, rows by 06:00; section 5 with the enforced-proving lane's fixtures, rows by 12:00. build-7: section 6, by 03:00. Every run under `lease pool N --class release` with a pid file and a watcher; a stall is recorded for the 08:00 read-back, never a hand-made lock. The rows land in sim/results_v2.md with their result files and the registry batch by 07:00 UK, the plan's status moved to MEASURED per section as each lands. diff --git a/docs/plans/igneum-2.0-f0-manifest.md b/docs/plans/igneum-2.0-f0-manifest.md index 0c8a05d3e..79e194e89 100644 --- a/docs/plans/igneum-2.0-f0-manifest.md +++ b/docs/plans/igneum-2.0-f0-manifest.md @@ -6,7 +6,7 @@ The fixture every case of the Test and Acceptance Standard (docs/plans/igneum-2. | Field | Value | Read from | Owner | |---|---|---|---| -| Miner cut tip (release-2.0.1) | aa0e0f45 (9c844503 + the pin file; 9c844503 = 2826f37e + the six-target packaged peer list; the app crate byte-identical to 2ea7b43f's throughout, so the pow and app cells read on 9c844503 cover it by content) | the shipper's line 19:5x | shipper (ae892a8b0f78fe31c) | +| Miner cut tip (release-2.0.1) | aa0e0f45 is the shipped tip (the entries stand on its binaries); release-2.0.1's final tip is c30ab32c (aa0e0f45 + the pool lane's pool/ and docs a54dffa3 + the release manifest f03-manifest-201 7437a31a merged at 800faaf7 + the hand-merged spec 09; no app, node pin or packaging change). The clean build from the manifest (R2-F03-R01) read green on c30ab32c at 21:14 UK on build-4: kaspad with igneum-pow, igneum-miner, igneum-pool, igneum-app, igneum-prove-host, and every component's own pin equals packaging/release-manifest.json. (9c844503 + the pin file; 9c844503 = 2826f37e + the six-target packaged peer list; the app crate byte-identical to 2ea7b43f's throughout, so the pow and app cells read on 9c844503 cover it by content) | the shipper's lines 19:5x and 21:0x; the steward's build 21:14 | shipper (ae892a8b0f78fe31c) | | Node sha for the roll | 7cfa422a = ef0f2ed8 (291ee6ae + the 2.0.1 version) + the miner base-unit fix, amended (777214af did not compile: a self-recursive connect, caught by the steward's read at 19:50) | the node lane's and shipper's lines 19:5x | node lane (a283f5f0d364ceef0) | | Node line read green tonight | 4cdcc488, d5981514, bee41b5e, 9fc9f42a, 5713d547, 417c4a57, 291ee6ae (ef0f2ed8 = 291ee6ae + the version bump, its own read on build-4 recorded as the literal) | the steward's matrices on build-2 and build-4 | CI steward | | Network | igneum-devnet-4, a fresh genesis; EVM chain id 4465 (0x1171), set in devnet4_params, read by the canaries as eth_chainId on every candidate tonight, pinned by the digest be5f4068; every 2.0 devnet node, pool and reference app signs with it; mainnet's and the testnet's ids unchanged from the 0.3 line | the node lane's line 19:3x | node lane | diff --git a/docs/plans/igneum-2.0-same-work-test.md b/docs/plans/igneum-2.0-same-work-test.md new file mode 100644 index 000000000..6ecfdd5c8 --- /dev/null +++ b/docs/plans/igneum-2.0-same-work-test.md @@ -0,0 +1,48 @@ +# The cross-backend same-work test (F03, Review B; specified by the CI steward, 8 October 2026, 20:1x UK) + +One job context, six readers, three phases around a transition, bit-for-bit agreement. Run by the fleet lane with the freeze object; the evidence recorded against POW-01 through the batch tools. + +## The job context (one file, `job-context.json`, written once by the steward or the hash lane and copied to every reader) + +| Field | Value tonight | Source | +|---|---|---| +| object | the class v5 freeze: igneum-pow 1c420786, fingerprint cbc5bd0aa10585c8576e71e37a8ee47a045ae51754e9ddf749d0c21e6a535f88 | packaging/release-manifest.json (generator) | +| epoch seed bytes | edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 (the genesis seeds) | the hash lane's P01 line | +| day bytes | 69676e65756d2d6461792ffa50000000000000 | the hash lane's P01 line | +| class | 5; era 0: | the pack's program.json | +| prehash | 0000000000000000000000000000000000000000000000000000000000000001 | the P01 convention | +| nonce range | 0 .. 2^20 per phase (three phases, three ranges: [0, 2^20), [2^20, 2^21), [2^21, 2^22)) | this page | +| transition | the day boundary: day D for phase 1, the boundary block for phase 2 (the last 2^12 nonces of day D and the first 2^12 of day D+1 as the engine sees them under fast-time 60x), day D+1 for phase 3 | infra/fast-time/override-60x.json | + +The transition is a dataset-day rotation (the class and era unchanged, the day bytes change), the one transition every live network crosses hourly at 60x; a class rotation (v5 to v6) is the same test with `class` and the second `day bytes` changed and runs only on a research object until a v6 floor is set. + +## The six readers (every one writes `-.json` of the P01 driver's evidence shape: nonce, hash per line in the raw file; agree, disagree, missing, the first ten disagreements, the device line, the pack and program ids, the manifest sha in the JSON) + +1. **node**: `igneumd` at the manifest's node sha, the engine's own re-check (`IgneumEngine::epoch_for` then `hash_bound` per nonce) through `igneum-miner --recheck-vectors` on the node binary's CPU path (the pool.rs seam), on build-2. +2. **CPU reference**: `igneum-pow hash-bound --count 2^20 --nonce ` from the manifest's generator commit, on build-2 (the hash lane's reference files are this reader). +3. **CUDA**: the kit's `igneum-worker-cuda --serve` driven by `tools/ci/p01-vectors.py` with the job context, on a 5090, 4090 and 3090 pod. +4. **OpenCL**: `igneum-worker-opencl --serve` the same way, on the AMD pod when one exists, else PC 1's RX 7600 (the only OpenCL retail cell tonight). +5. **Metal**: the Mac's own worker, the same driver, on the mini (the morning's run; never on this Mac). +6. **pool**: `igneum-pool` at the manifest's sha with its vendored node at the manifest's node sha, the member-side re-check (`pool/src/node.rs` → `kaspa_pow::igneum::IgneumEngine`) on the same job lines, on build-2. + +## The three phases + +Phase 1 (before): every reader on range 1 under day D. Phase 2 (during): every reader on range 2 where the engine's day moves from D to D+1 inside the range at the boundary nonce the fast-time clock sets; every reader must switch program and dataset at the same nonce. Phase 3 (after): every reader on range 3 under day D+1. + +## Acceptance (the registry row POW-01, cell `harness:same-work`, PASS only when all hold) + +- every reader's hash equals the CPU reference's for every nonce of every phase (zero disagreements, zero missing); +- the program id and the day each reader reports at phase 2's boundary are the same across readers (the transition is seen at one nonce); +- the pool's accepted-share verdict for a sample of 64 nonces per phase equals the node's (the seam closes by a build: `release-manifest-check.sh` refuses a redefined EpochSeeds and an unpinned vendored node); +- the run names the manifest sha (packaging/release-manifest.json), and the evidence sits at build-1:/srv/artefacts/tas/same-work-/. + +A disagreement on any reader is a red to main within fifteen minutes (the coordinator's rule for the cross-checks). + +## What is still to build (owners, defaults) + +| Piece | Owner | Clock | Default if silent | +|---|---|---|---| +| `p01-vectors.py --job-context --phase N` (the three ranges and the boundary assertion; the driver already drives the workers) | CI steward | 21:30 | built as specified | +| the node reader (`igneum-miner --recheck-vectors`, the seam with a count) | node lane | 22:00 | the steward builds it on a branch of release-2.0.0-node under rule 24 | +| the pool reader (the member-side re-check over a job-lines file) | pool lane | 22:30 | the pool's existing recheck_pack path over the context, driven by the steward | +| the pods and the mini | fleet lane | on the artefact line | as P01 | diff --git a/docs/plans/igneum-2.0-test-harness-map.md b/docs/plans/igneum-2.0-test-harness-map.md index 572138472..26d5bf651 100644 --- a/docs/plans/igneum-2.0-test-harness-map.md +++ b/docs/plans/igneum-2.0-test-harness-map.md @@ -133,8 +133,8 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover ### bench:pc1-packs -- Command: `tools/ca3-v4-amend/pc1-ca4-packs.ps1 on PC 1 (RTX 5090, the signed-jobs channel, --cards-off, the Power Helper lock at 1,300 MHz, nvidia-smi at 1 Hz, 250 x 2^24 per row, the kit's CUDA worker --bench)` -- Box class: PC 1 bench +- Command: `tools/ca3-v4-amend/pc1-ca4-packs.ps1 on the project's own rig (RTX 5090, the signed-jobs channel, --cards-off, the Power Helper lock at 1,300 MHz, nvidia-smi at 1 Hz, 250 x 2^24 per row, the kit's CUDA worker --bench)` +- Box class: the project's own rig bench - Fixtures: F0, F1 - Cases: - GPU-02 Reproduce Ember clock-lock savings: partial: the paired stock and locked rows on the same board, host and workload (the rate held, 2.37 against 3.26 microjoules per hash on the class v5 pack); the historical 34 to 41 percent claim's full configuration set is owed @@ -144,8 +144,8 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover ### bench:pc1-amd -- Command: `tools/ca3-v4-amend/pc1-amd-cardin-bench.ps1 on PC 1 (RX 7600, OpenCL, gfx1102, 30 dispatches of 2^24 per size, rate only)` -- Box class: PC 1 bench +- Command: `tools/ca3-v4-amend/pc1-amd-cardin-bench.ps1 on the project's own rig (RX 7600, OpenCL, gfx1102, 30 dispatches of 2^24 per size, rate only)` +- Box class: the project's own rig bench - Fixtures: F0, F1 - Cases: - GPU-01 Cover the declared commodity population: partial: the 8 GB AMD cell, fingerprints and rates at 1, 2, 4 and 5.5 GiB; one cell of P02's twelve @@ -243,7 +243,7 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover - Box class: harness (network run on the 2.0 devnet plus Sepolia reads) - Fixtures: none - Cases: - - VER-03 Prove successful payment rather than inclusion: partial: a payment receipt is labelled payment only when the receipt is proven against the segment statement's receipts commitment under the finality certificate, inclusion-only receipts read inclusion, a tampered receipt and a flipped status fail; run on the Devnet 3 fixtures, the 2.0 devnet re-run waits for its first paid segment whose last block carries a transaction + - VER-03 Prove successful payment rather than inclusion: partial: a payment receipt is labelled payment only when the receipt is proven against the segment statement's receipts commitment under the finality certificate, inclusion-only receipts read inclusion, a tampered receipt and a flipped status fail; run on the the earlier devnet fixtures, the 2.0 devnet re-run waits for its first paid segment whose last block carries a transaction - VER-04 Bound cross-chain oracle trust and replay: partial: the oracle's trust() names the deployer-installed table and the unchecked aggregator signature, a second hash at a stored certificate index is refused, a root claim at an unknown index is refused, another chain id is refused (tools/reference-apps/oracle/test.mjs on Sepolia); Review B F04: the Sepolia verifiers apply the two-thirds rule only, carry no lock-kind field, and an under-threshold (recovery-rule) certificate fails closed in submitCertificate, so no stored root is a recovery lock (the DEX lane's docs/bridge/light-client-bridge.md paragraph); the claimed-option review of the trust model is the reviewer's - VER-05 Reconstruct required state without founder storage: partial: the public node and the reference reader on build-1 executed the 2.0 devnet from genesis with no snapshot (unit ExecStart without --igneum-exec-snapshot), and the pages fetch headers, certificates, coinbase bodies and account proofs from the public read RPC only; the archive and availability model beyond the 2,048-block state ring is the OPS no-founder exercise - VER-06 Detect withholding, corruption and stale data: partial: the read service never serves a certificate from another network, a withheld header in the path and a corrupted proof node are detected, stale data prints its lock age beside every balance, and a client told no certificate never falls back to a trusted RPC balance; detection on the fault network F4 is not run @@ -336,24 +336,80 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover ### bench:amd-intel-energy -- Command: `the AMD-and-Intel energy lane jobs on the signed-jobs channel: run-ae-pc2-b580-energy-20261008 (PC 2, Arc B580, the class v6 kit worker --memprobe and --bench-pack on hl-v6-foldrw and hl-v6-all, Level Zero energy counter) and run-ae-pc1-7600-energy-20261008 (PC 1, RX 7600, the same with ADLX watts at stock, plimit -30, gmax -500 + plimit -30); scripts in docs/analysis/class-v6/amd-intel-energy/` -- Box class: PC 1 and PC 2 bench (OpenCL) +- Command: `the AMD-and-Intel energy lane jobs on the signed-jobs channel: run-ae-pc2-b580-energy-20261008 (PC 2, Arc B580, the class v6 kit worker --memprobe and --bench-pack on hl-v6-foldrw and hl-v6-all, Level Zero energy counter) and run-ae-pc1-7600-energy-20261008 (the project's own rig, RX 7600, the same with ADLX watts at stock, plimit -30, gmax -500 + plimit -30); scripts in docs/analysis/class-v6/amd-intel-energy/` +- Box class: the project's own rig and PC 2 bench (OpenCL) - Fixtures: F0, F1 - Cases: - GPU-03 Measure the real 64-register GPU cost: partial: the 64-register window on AMD and Intel, rate per unit of work (RX 7600 0 percent, Arc B580 -0.3 percent, kernel throughput, quiet) with the B580 fingerprints equal on both packs and the offline RDNA allocation (160 VGPRs, no spill); energy owed (the 7600 job queued, the B580 counter unsupported unelevated); team-run, not under the standard's paired protocol or a wall meter +### adversary:mf-placed + +- Command: `cd tools/chip-model/mf && make flow- power- (inside openroad/orfs:latest on a rented CPU host; never the Mac); python3 flow/collect.py results; python3 flow/board.py ; python3 flow/hash.py results power` +- Box class: rented CPU host (Vast, 48 to 72 cores) on the ORFS image +- Fixtures: F0, F1 +- Cases: + - ADV-01 Build a multi-family programmable opponent: the mf core (18 families, SRAM window and imem macros, operand isolation, 5-phase port) placed and routed on ASAP7 with SPEF and a gate-level VCD; k per family and per draw; outputs checked against the RTL simulation's checksums per tag, not yet against the POW vectors (owed) + - ADV-03 Attack with data-local and hybrid execution: the data-local, hybrid and companion-host rows of multi-family-adversary.md sections 11, 13 and 14 (bit counts closed-form; the hop and wire energies claimed) + - ADV-07 Evaluate lifetime without forced obsolescence: the lifetime table and the six-row transition matrix (sections 7 and 14) on the placed rows; the 32-lane genesis comparator placed on adv-g + - ADV-08 Independently challenge the best-cost envelope: the best-cost envelope on the complete machine (section 6) and the SRAM-die ticket reconciliation with lane B (section 15); the unaffiliated second reviewer is not this lane's + - POW-03 Test whether live state is unavoidable: partial: the live state held in an SRAM macro per 8 lanes with a time-multiplexed single port and operand isolation, the complete-system cost in section 6; the liveness trace itself is the connected-state lane's tool + - POW-04 Evaluate connected-resource restructuring: partial: the 64-register window's cost on the macro core (sections 4 and 5, agreeing with the k lane's gated-flop row within 5 percent); the restructured candidate's GPU cost is the invention lane's + - ADV-05 Validate physical and complete-board costs: partial: the SRAM macros, ports, wiring, clocking and the complete-board terms are modelled in sections 2.3, 5 and 6 with the unmodelled items carried as uncertainty; the calibration against an existing hardware block is the k lane's bare-lane row beside it, owed as a named comparison + +### adversary:d2b + +- Command: `cd tools/chip-model/mf && python3 flow/d2b.py results N5 1 && python3 flow/d2b.py results N3 1 (on a build box under lease pool or a rented host; reads results/table.csv)` +- Box class: any box (a one-minute Python model on the placed rows) +- Fixtures: F0 +- Cases: + - ADV-02 Price shared, reduced and reconstructed memory: memory sharing, partial stores at the measured window-layer hit rates (and hit 0.50 with layer 8 off), recomputation and the set-up amortisation on the complete board (section 13); the formal memory model's terms (section 16) + - ADV-04 Measure profitable selective participation: selective participation over 2,000 era draws under the layer 1 band with two reserve families live per epoch, the specialist's revenue against its ratio gain (section 13); downtime and re-entry priced as the DAA window's lag, not simulated against the difficulty rule itself (owed) + +### pc:install-update + +- Command: `signed jobs and relay runs on the project's own rig (ae432dc7) and PC 2 (1ccfe586): the installer over the running app (rule 14: installed versions, the process set, the first upload), the OTA path (update-now, the update-return read-back line), and the no-click resume after the install (relay run mining-on-after-200.ps1)` +- Box class: PC (the two Windows PCs; nothing on the Mac) +- Fixtures: F2 +- Cases: + - UX-01 Onboard ordinary owners on native desktop apps: team-run evidence only (the two PCs taking the Windows entry through the app's own update path with no click and mining on every card); the P10 study is NOT RUN until unaffiliated participants are recruited + - UX-07 Expose actionable failures and safe updates: partial: the install classes (the payload's stop step, the detached installer under a job, the installer refusing a wrong-version name, a stale install flag) and the no-click resume; the recovery-instruction and canary rows are the shipper's + - OPS-03 Separate update distribution from consensus authority: partial: the update path's read-back and the signed manifest's verify on the PC (a tampered or rolled-back payload is refused by the engine's manifest check); authority separation is the shipper's review + +### harness:release-manifest + +- Command: `bash tools/ci/release-manifest-check.sh (self-test, then the tree; the pins, the proof guests' hashes on disk, the fork freeze, the pool's vendored node, the proving manifest's source_commit provenance) and bash tools/ci/build-from-manifest.sh --box N on the release tree (kaspad with igneum-pow, igneum-miner, igneum-pool, igneum-app, igneum-prove-host from packaging/release-manifest.json with the node vendored at the manifest's sha)` +- Box class: gate +- Fixtures: F0 +- Cases: + - R2-F03-R01 Clean build from one manifest, including the pool and workers, with no unpublished vendor tree.: full for the components the tree builds (node, miner, pool, app, prove-host from one manifest, no unpublished vendor tree); the GPU workers are the kit's cross-build and read under the shipper's kit-isa line, not this cell + +### harness:same-work + +- Command: `tools/ci/p01-vectors.py --job-context --phase 1|2|3 per reader (CUDA, OpenCL, Metal workers), igneum-miner --recheck-vectors (node), igneum-pow hash-bound (CPU reference), the pool's member-side re-check; docs/plans/igneum-2.0-same-work-test.md` +- Box class: release (the readers on their pods and boxes) +- Fixtures: F0 +- Cases: + - R2-F03-R02 Same job context produces identical accepted work in node, CPU reference, CUDA, Metal, OpenCL and pool.: partial until every reader has run: bit-for-bit agreement per nonce across node, CPU reference, CUDA, OpenCL, Metal and pool on one job context + - R2-F03-R03 Cross every scheduled transition with old/new client behavior documented and identical rule identities.: partial: the day-boundary transition in phase 2 (the one transition every live network crosses hourly at 60x); a class rotation is the same test on a research object until a v6 floor is set + +### canary:fresh-install + +- Command: `the rule 33 fresh-install canary on a release tip (the founder, 8 October 2026): install from the published artefact on a non-AVX-512 box with an empty datadir, sync genesis to tip, five minutes mining with zero refusals and an accepted block, one shard claimed, proved and paid or queued, a bounded quit, every line read back; the record tools/ci/canary/.json (one file per sha, an artefacts list: fleet, windows, mac, hive) read by tools/ci/canary-check.sh [--artefact kind], which publish-manifest.sh and publish-public.sh refuse without` +- Box class: release (a non-AVX-512 box with an empty datadir) +- Fixtures: F0 +- Cases: + - INT-07 One v6 object agrees in node, pool, CPU verifier and each supported GPU host across activation.: partial: V6-12's clean-install half (one published object installed fresh, synced, mined, proved and paid on one host per artefact); the cross-host agreement half (node, pool, CPU verifier, each GPU host across activation) is harness:same-work's + ## Automated cases with no harness in the matrix (NOT RUN, the reason) - GOV-02 Approve thresholds before results: the approval is recorded in the registry's approval field; the automated half (thresholds frozen before any run_status) is the gate rule landing by 21:00 - GOV-04 Preserve raw and negative evidence: the evidence vault F9 (raw and negative evidence preserved) is the gate rule landing by 21:00: a PASS must carry its evidence file - GOV-08 Invalidate stale evidence and control public status: the stale-evidence rule (evidence older than the manifest sha reads NOT RUN) is the gate rule landing by 21:00 -- GPU-04 Find the memory-clock operating ladder: the memory-clock ladder has no harness tonight: PC 1 mines nothing under the Devnet 3 off order +- GPU-04 Find the memory-clock operating ladder: the memory-clock ladder has no harness tonight: the project's own rig mines nothing under the the earlier devnet off order - GPU-06 Measure accepted work under ordinary connectivity: accepted work under ordinary connectivity needs the fault network F4 -- GPU-07 Survive sustained thermal and power operation: the sustained thermal and power soak has no harness tonight: PC 1 mines nothing under the Devnet 3 off order +- GPU-07 Survive sustained thermal and power operation: the sustained thermal and power soak has no harness tonight: the project's own rig mines nothing under the the earlier devnet off order - POW-05 Prevent amortised cheap winning attempts: amortised cheap winning attempts are the attack lanes' grind and era harnesses (tools/attack/f7-era, f9-grind), not in the release matrix; their rows come from those lanes -- ADV-04 Measure profitable selective participation: selective participation needs the economic model F7 and a live chain window - ADV-06 Separate process advantage from specialisation: process-advantage separation is the adversary lanes' chip study -- ADV-07 Evaluate lifetime without forced obsolescence: lifetime rows are the adversary lanes' models - ROT-03 Test miner-voted bring-forward governance: miner-voted bring-forward needs a vote harness on the fault network F4 - ROT-04 Resist seed selection and faster evaluators: seed-selection resistance is the census harness (the class v6 invention lane), not yet in the matrix - EVM-01 Match the selected EVM semantics: no EVM conformance-vector harness is mapped tonight; the exec suite does not run the reference test vectors @@ -440,7 +496,6 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover - INT-04 Corrupt existing pool state, disk-full and reorg cause safe recovery, not silent empty balances.: INT-04 (R1 integration gate): the gate's harness is the owner lane's (pool lane (a1c484c48a62948c2)); not yet named in the map - INT-05 The supplied finality implementation matches the approved anchor rule after >window healing.: INT-05 (R1 integration gate): the gate's harness is the owner lane's (node lane (a283f5f0d364ceef0)); not yet named in the map - INT-06 Recovery tests state and preserve their weaker fault bound; interfaces never label it as a stronger guarantee.: INT-06 (R1 integration gate): the gate's harness is the owner lane's (node lane (a283f5f0d364ceef0)); not yet named in the map -- INT-07 One v6 object agrees in node, pool, CPU verifier and each supported GPU host across activation.: INT-07 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map - INT-08 Census, production acceptance, schedule counters and live-dataset tests use the identical frozen contract.: INT-08 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map - INT-09 Eight- and twelve-GiB epoch transitions recover deliberately without relying on repeated OOM/watchdog cycles.: INT-09 (R1 integration gate): the gate's harness is the owner lane's (app lane); not yet named in the map - INT-10 Metal/CUDA/OpenCL exact dataset geometry agrees; unsupported packs reject before launch.: INT-10 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map @@ -459,9 +514,6 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover - R2-F02-R01 Release build cannot activate test bypass.: R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map - R2-F02-R02 Missing oracle or pinned keys prevents service readiness after enforcement activation.: R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map - R2-F02-R03 Missing proof bytes retry without incorrectly marking a valid block permanently invalid.: R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map -- R2-F03-R01 Clean build from one manifest, including the pool and workers, with no unpublished vendor tree.: R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map -- R2-F03-R02 Same job context produces identical accepted work in node, CPU reference, CUDA, Metal, OpenCL and pool.: R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map -- R2-F03-R03 Cross every scheduled transition with old/new client behavior documented and identical rule identities.: R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map - R2-F04-R01 Native multi-node 40/40/20 partition, equivocation and dust-valid mining on both sides past the window.: R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map - R2-F04-R02 Proof that the chosen recovery guarantee matches the public finality claim; two valid contradictory certificates are a hard failure for strong finality.: R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map - R2-F04-R03 Pause-only resume with historical backfill, missing historical data and all old keys returning.: R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map @@ -499,4 +551,4 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover ## Count -171 automated cases: 69 mapped to a cell, 152 NOT RUN with a reason. +171 automated cases: 82 mapped to a cell, 146 NOT RUN with a reason. diff --git a/docs/plans/igneum-2.0-test-registry.json b/docs/plans/igneum-2.0-test-registry.json index c8ca528f2..014a6c01e 100644 --- a/docs/plans/igneum-2.0-test-registry.json +++ b/docs/plans/igneum-2.0-test-registry.json @@ -50,29 +50,30 @@ "manual_page": 18, "owner_lane": "node lane (a283f5f0d364ceef0)", "run_status": "NOT RUN", - "evidence_path": "docs/plans/igneum-2.0-f0-manifest.md; packaging/pow-freeze.txt; build-1:/srv/artefacts/tas/201-7cfa422a-aa0e0f45/node-7cfa422a/box4-kaspad-check.log", - "run_id": "201-7cfa422a-aa0e0f45", - "updated": "2026-10-08T19:32:50.856Z", + "evidence_path": "docs/plans/igneum-2.0-f0-manifest.md; build-4:/srv/builds/igneum-wt-f03-201/vendor/igneum-node/packaging/pow-freeze.txt; build-4:/srv/builds/igneum-wt-f03-201", + "run_id": "f03-manifest-20261008-01", + "updated": "2026-10-08T20:51:07.788Z", "evidence_record": { - "cell": "check:freeze", - "manifest_sha": "7cfa422a", - "coverage": { - "GOV-01": "partial: the linked igneum-pow tree's fingerprint must match a listed freeze or the build fails, the binary prints which; the manifest (igneum_getManifest) names the object digest; the signed F0 manifest is the node lane's row tonight" - }, - "at": "2026-10-08T19:32:50.856Z", - "method": "native", "requirement_id": "GOV-01", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "static", + "cell": "check:freeze", + "manifest_sha": "c30ab32c", + "run_id": "f03-manifest-20261008-01", + "evidence": "docs/plans/igneum-2.0-f0-manifest.md; build-4:/srv/builds/igneum-wt-f03-201/vendor/igneum-node/packaging/pow-freeze.txt; build-4:/srv/builds/igneum-wt-f03-201", + "in_progress": false, + "coverage": "partial: the linked igneum-pow tree's fingerprint must match a listed freeze or the build fails, the binary prints which; the manifest (igneum_getManifest) names the object digest; the signed F0 manifest is the node lane's row tonight", "release_identity": { - "commit": "7cfa422a", - "lockfile": "", - "binary": "", - "network_object": "", - "activation": "", - "profile_hashes": "" - } + "commit": "c30ab32c", + "lockfile": "the release tree's Cargo.lock files at c30ab32c", + "binary": "cargo check only: no binary is claimed by this cell; the fleet binaries are the shipper's kit on aa0e0f45", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "none (a build fact)", + "profile_hashes": "release-manifest-check: every component's own pin equals packaging/release-manifest.json" + }, + "claim_impact": "F03's first rung: one manifest, one build, no unpublished vendor tree; the same-work rung (R02) and the transition rung (R03) stay NOT RUN until the readers run on the job context", + "reviewer": "", + "at": "2026-10-08T20:51:07.788Z" }, "in_progress_since": "2026-10-08T19:32:50.856Z", "approvals": { @@ -83,28 +84,26 @@ }, "evidence_records": { "check:freeze": { - "cell": "check:freeze", - "manifest_sha": "7cfa422a", - "coverage": { - "GOV-01": "partial: the linked igneum-pow tree's fingerprint must match a listed freeze or the build fails, the binary prints which; the manifest (igneum_getManifest) names the object digest; the signed F0 manifest is the node lane's row tonight" - }, - "at": "2026-10-08T19:32:50.856Z", - "method": "native", "requirement_id": "GOV-01", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "static", + "cell": "check:freeze", + "manifest_sha": "c30ab32c", + "run_id": "f03-manifest-20261008-01", + "evidence": "docs/plans/igneum-2.0-f0-manifest.md; build-4:/srv/builds/igneum-wt-f03-201/vendor/igneum-node/packaging/pow-freeze.txt; build-4:/srv/builds/igneum-wt-f03-201", + "in_progress": false, + "coverage": "partial: the linked igneum-pow tree's fingerprint must match a listed freeze or the build fails, the binary prints which; the manifest (igneum_getManifest) names the object digest; the signed F0 manifest is the node lane's row tonight", "release_identity": { - "commit": "7cfa422a", - "lockfile": "", - "binary": "", - "network_object": "", - "activation": "", - "profile_hashes": "" + "commit": "c30ab32c", + "lockfile": "the release tree's Cargo.lock files at c30ab32c", + "binary": "cargo check only: no binary is claimed by this cell; the fleet binaries are the shipper's kit on aa0e0f45", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "none (a build fact)", + "profile_hashes": "release-manifest-check: every component's own pin equals packaging/release-manifest.json" }, - "run_id": "201-7cfa422a-aa0e0f45", - "evidence": "docs/plans/igneum-2.0-f0-manifest.md; packaging/pow-freeze.txt; build-1:/srv/artefacts/tas/201-7cfa422a-aa0e0f45/node-7cfa422a/box4-kaspad-check.log", - "in_progress": true + "claim_impact": "F03's first rung: one manifest, one build, no unpublished vendor tree; the same-work rung (R02) and the transition rung (R03) stay NOT RUN until the readers run on the job context", + "reviewer": "", + "at": "2026-10-08T20:51:07.788Z" } } }, @@ -950,23 +949,10 @@ "run_status": "NOT RUN", "evidence_path": "docs/analysis/class-v6/floor/sm-sparse.md", "run_id": "team-2026-10-08", - "updated": "2026-10-08T20:10:24.556Z", + "updated": "2026-10-08T21:00:32.846Z", "evidence_record": { - "reason": "the memory-clock ladder has no harness tonight: PC 1 mines nothing under the Devnet 3 off order", - "at": "2026-10-08T20:10:24.556Z", - "method": "static", - "requirement_id": "GPU-04", - "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", - "release_identity": { - "commit": "", - "lockfile": "", - "binary": "", - "network_object": "", - "activation": "", - "profile_hashes": "" - } + "reason": "the memory-clock ladder has no harness tonight: the project's own rig mines nothing under the the earlier devnet off order", + "at": "2026-10-08T21:00:32.846Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -1198,23 +1184,10 @@ "run_status": "NOT RUN", "evidence_path": "docs/analysis/class-v6/floor/sm-sparse.md", "run_id": "team-2026-10-08", - "updated": "2026-10-08T20:10:24.556Z", + "updated": "2026-10-08T21:00:32.846Z", "evidence_record": { - "reason": "the sustained thermal and power soak has no harness tonight: PC 1 mines nothing under the Devnet 3 off order", - "at": "2026-10-08T20:10:24.556Z", - "method": "static", - "requirement_id": "GPU-07", - "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", - "release_identity": { - "commit": "", - "lockfile": "", - "binary": "", - "network_object": "", - "activation": "", - "profile_hashes": "" - } + "reason": "the sustained thermal and power soak has no harness tonight: the project's own rig mines nothing under the the earlier devnet off order", + "at": "2026-10-08T21:00:32.846Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -1547,9 +1520,9 @@ "manual_page": 24, "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "FAIL", - "evidence_path": "docs/analysis/class-v6/connected-state.md; docs/analysis/class-v6", + "evidence_path": "docs/analysis/class-v6/connected-state.md; docs/analysis/class-v6/multi-family-adversary.md", "run_id": "kills-20261008", - "updated": "2026-10-08T20:10:24.624Z", + "updated": "2026-10-08T21:00:32.906Z", "evidence_record": { "requirement_id": "POW-03", "decision": "FAIL", @@ -1557,7 +1530,7 @@ "cell": "experiment:connected-state", "manifest_sha": "7cfa422a", "run_id": "kills-20261008", - "evidence": "docs/analysis/class-v6", + "evidence": "docs/analysis/class-v6/connected-state.md", "in_progress": false, "coverage": "the experiment ran and its claim failed: live state does not make the work unavoidable for a chip; the master's register row 18 reads FAIL, published, never PASSED", "release_identity": { @@ -1570,7 +1543,7 @@ }, "claim_impact": "the claims the two experiments carried are withdrawn: live state as an ASIC barrier (connected state), a cheaper mixed-FP32 lane", "reviewer": "", - "at": "2026-10-08T20:10:24.624Z" + "at": "2026-10-08T21:00:32.906Z" }, "approvals": { "scope_approved": null, @@ -1608,7 +1581,7 @@ "cell": "experiment:connected-state", "manifest_sha": "7cfa422a", "run_id": "kills-20261008", - "evidence": "docs/analysis/class-v6", + "evidence": "docs/analysis/class-v6/connected-state.md", "in_progress": false, "coverage": "the experiment ran and its claim failed: live state does not make the work unavoidable for a chip; the master's register row 18 reads FAIL, published, never PASSED", "release_identity": { @@ -1621,7 +1594,29 @@ }, "claim_impact": "the claims the two experiments carried are withdrawn: live state as an ASIC barrier (connected state), a cheaper mixed-FP32 lane", "reviewer": "", - "at": "2026-10-08T20:10:24.624Z" + "at": "2026-10-08T21:00:32.906Z" + }, + "adversary:mf-placed": { + "requirement_id": "POW-03", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "partial: the live state held in an SRAM macro per 8 lanes with a time-multiplexed single port and operand isolation, the complete-system cost in section 6; the liveness trace itself is the connected-state lane's tool", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -1652,27 +1647,30 @@ "manual_page": 25, "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", - "evidence_path": "docs/analysis/class-v6/connected-state.md", - "run_id": "team-2026-10-08", - "updated": "2026-10-08 18:3x UK", + "evidence_path": "docs/analysis/class-v6/connected-state.md; docs/analysis/class-v6/multi-family-adversary.md", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "what_was_run": "the same experiment, D2(a) closed", - "run_by": "adversary lane (a1a9876a88f5a72fc)", - "under_the_standard": "no: team-run before the standard's procedure; the status is RUNNING until the case is re-run under its steps with the profile's numbers and an independent run where the profile asks one", - "pass_or_fail_today": "not judged under the standard yet", - "method": "static", "requirement_id": "POW-04", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "partial: the 64-register window's cost on the macro core (sections 4 and 5, agreeing with the k lane's gated-flop row within 5 percent); the restructured candidate's GPU cost is the invention lane's", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -1703,6 +1701,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/connected-state.md", "in_progress": true + }, + "adversary:mf-placed": { + "requirement_id": "POW-04", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "partial: the 64-register window's cost on the macro core (sections 4 and 5, agreeing with the k lane's gated-flop row within 5 percent); the restructured candidate's GPU cost is the invention lane's", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -1897,9 +1917,9 @@ "manual_page": 26, "owner_lane": "hash lane (a690540514aa453d7)", "run_status": "FAIL", - "evidence_path": "docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md; docs/analysis/class-v6", + "evidence_path": "docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md; docs/analysis/class-v6/mixed-fp32.md; docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md", "run_id": "kills-20261008", - "updated": "2026-10-08T20:10:24.624Z", + "updated": "2026-10-08T21:00:32.906Z", "evidence_record": { "requirement_id": "POW-07", "decision": "FAIL", @@ -1907,7 +1927,7 @@ "cell": "experiment:mixed-fp32", "manifest_sha": "7cfa422a", "run_id": "kills-20261008", - "evidence": "docs/analysis/class-v6", + "evidence": "docs/analysis/class-v6/mixed-fp32.md; docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md", "in_progress": false, "coverage": "the branch ran and its claim failed: the FP32 branch costs the card more energy and widens the chip edge; the master's register row 12 reads FAIL, published; the branch is excluded and unreachable on master (the grep evidence stays)", "release_identity": { @@ -1920,7 +1940,7 @@ }, "claim_impact": "the claims the two experiments carried are withdrawn: live state as an ASIC barrier (connected state), a cheaper mixed-FP32 lane", "reviewer": "", - "at": "2026-10-08T20:10:24.624Z" + "at": "2026-10-08T21:00:32.906Z" }, "approvals": { "scope_approved": null, @@ -1962,7 +1982,7 @@ "cell": "experiment:mixed-fp32", "manifest_sha": "7cfa422a", "run_id": "kills-20261008", - "evidence": "docs/analysis/class-v6", + "evidence": "docs/analysis/class-v6/mixed-fp32.md; docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md", "in_progress": false, "coverage": "the branch ran and its claim failed: the FP32 branch costs the card more energy and widens the chip edge; the master's register row 12 reads FAIL, published; the branch is excluded and unreachable on master (the grep evidence stays)", "release_identity": { @@ -1975,7 +1995,7 @@ }, "claim_impact": "the claims the two experiments carried are withdrawn: live state as an ASIC barrier (connected state), a cheaper mixed-FP32 lane", "reviewer": "", - "at": "2026-10-08T20:10:24.624Z" + "at": "2026-10-08T21:00:32.906Z" } } }, @@ -2114,26 +2134,29 @@ "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md", - "run_id": "team-2026-10-08", - "updated": "2026-10-08 18:3x UK", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "what_was_run": "the 18-family programmable core placed and routed (9.36 pJ per lane-op, k 0.64 same-node)", - "run_by": "adversary lane (a1a9876a88f5a72fc)", - "under_the_standard": "no: team-run before the standard's procedure; the status is RUNNING until the case is re-run under its steps with the profile's numbers and an independent run where the profile asks one", - "pass_or_fail_today": "not judged under the standard yet", - "method": "static", "requirement_id": "ADV-01", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the mf core (18 families, SRAM window and imem macros, operand isolation, 5-phase port) placed and routed on ASAP7 with SPEF and a gate-level VCD; k per family and per draw; outputs checked against the RTL simulation's checksums per tag, not yet against the POW vectors (owed)", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -2164,6 +2187,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/multi-family-adversary.md", "in_progress": true + }, + "adversary:mf-placed": { + "requirement_id": "ADV-01", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the mf core (18 families, SRAM window and imem macros, operand isolation, 5-phase port) placed and routed on ASAP7 with SPEF and a gate-level VCD; k per family and per draw; outputs checked against the RTL simulation's checksums per tag, not yet against the POW vectors (owed)", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -2196,26 +2241,29 @@ "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md", - "run_id": "team-2026-10-08", - "updated": "2026-10-08 18:3x UK", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "what_was_run": "D2(b): the stored-half hybrid, memory sharing, recomputation priced (the placed hybrid 1.93x same-node at the mean hit)", - "run_by": "adversary lane (a1a9876a88f5a72fc)", - "under_the_standard": "no: team-run before the standard's procedure; the status is RUNNING until the case is re-run under its steps with the profile's numbers and an independent run where the profile asks one", - "pass_or_fail_today": "not judged under the standard yet", - "method": "static", "requirement_id": "ADV-02", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:d2b", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "memory sharing, partial stores at the measured window-layer hit rates (and hit 0.50 with layer 8 off), recomputation and the set-up amortisation on the complete board (section 13); the formal memory model's terms (section 16)", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -2246,6 +2294,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/multi-family-adversary.md", "in_progress": true + }, + "adversary:d2b": { + "requirement_id": "ADV-02", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:d2b", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "memory sharing, partial stores at the measured window-layer hit rates (and hit 0.50 with layer 8 off), recomputation and the set-up amortisation on the complete board (section 13); the formal memory model's terms (section 16)", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -2278,26 +2348,29 @@ "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md", - "run_id": "team-2026-10-08", - "updated": "2026-10-08 18:3x UK", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "what_was_run": "data-local execution moves nothing (2,112 bits of live state against an 80-bit read)", - "run_by": "adversary lane (a1a9876a88f5a72fc)", - "under_the_standard": "no: team-run before the standard's procedure; the status is RUNNING until the case is re-run under its steps with the profile's numbers and an independent run where the profile asks one", - "pass_or_fail_today": "not judged under the standard yet", - "method": "static", "requirement_id": "ADV-03", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the data-local, hybrid and companion-host rows of multi-family-adversary.md sections 11, 13 and 14 (bit counts closed-form; the hop and wire energies claimed)", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -2328,6 +2401,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/multi-family-adversary.md", "in_progress": true + }, + "adversary:mf-placed": { + "requirement_id": "ADV-03", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the data-local, hybrid and companion-host rows of multi-family-adversary.md sections 11, 13 and 14 (bit counts closed-form; the hop and wire energies claimed)", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -2360,24 +2455,29 @@ "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md", - "run_id": "team-2026-10-08", - "updated": "2026-10-08T20:10:24.556Z", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "reason": "selective participation needs the economic model F7 and a live chain window", - "at": "2026-10-08T20:10:24.556Z", - "method": "static", "requirement_id": "ADV-04", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:d2b", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "selective participation over 2,000 era draws under the layer 1 band with two reserve families live per epoch, the specialist's revenue against its ratio gain (section 13); downtime and re-entry priced as the DAA window's lag, not simulated against the difficulty rule itself (owed)", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -2408,6 +2508,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/multi-family-adversary.md", "in_progress": true + }, + "adversary:d2b": { + "requirement_id": "ADV-04", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:d2b", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "selective participation over 2,000 era draws under the layer 1 band with two reserve families live per epoch, the specialist's revenue against its ratio gain (section 13); downtime and re-entry priced as the DAA window's lag, not simulated against the difficulty rule itself (owed)", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -2439,27 +2561,30 @@ "manual_page": 28, "owner_lane": "k lane (a3c9601a6d4686fe1)", "run_status": "NOT RUN", - "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md; docs/analysis/class-v6/floor/shadow-k.md", - "run_id": "team-2026-10-08", - "updated": "2026-10-08 18:3x UK", + "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md; docs/analysis/class-v6/floor/shadow-k.md; docs/analysis/class-v6/multi-family-adversary.md", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "what_was_run": "the complete GDDR7 machine 1.5x same-node, 1.8x a node ahead at the placed energy; the honest same-node bracket 1.5x to 2.1x: FAIL against P04 at R_E 1.5, served as such", - "run_by": "k lane (a3c9601a6d4686fe1)", - "under_the_standard": "no: team-run before the standard's procedure; the status is RUNNING until the case is re-run under its steps with the profile's numbers and an independent run where the profile asks one", - "pass_or_fail_today": "FAIL against P04 at R_E 1.5", - "method": "static", "requirement_id": "ADV-05", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "partial: the SRAM macros, ports, wiring, clocking and the complete-board terms are modelled in sections 2.3, 5 and 6 with the unmodelled items carried as uncertainty; the calibration against an existing hardware block is the k lane's bare-lane row beside it, owed as a named comparison", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -2490,6 +2615,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/multi-family-adversary.md; docs/analysis/class-v6/floor/shadow-k.md", "in_progress": true + }, + "adversary:mf-placed": { + "requirement_id": "ADV-05", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "partial: the SRAM macros, ports, wiring, clocking and the complete-board terms are modelled in sections 2.3, 5 and 6 with the unmodelled items carried as uncertainty; the calibration against an existing hardware block is the k lane's bare-lane row beside it, owed as a named comparison", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -2601,25 +2748,30 @@ "manual_page": 29, "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", - "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md section 14", - "run_id": "team-2026-10-08", - "updated": "2026-10-08T20:10:24.556Z", + "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md section 14; docs/analysis/class-v6/multi-family-adversary.md", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "evidence_record": { - "reason": "lifetime rows are the adversary lanes' models", - "at": "2026-10-08T20:10:24.556Z", - "method": "static", "requirement_id": "ADV-07", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the lifetime table and the six-row transition matrix (sections 7 and 14) on the placed rows; the 32-lane genesis comparator placed on adv-g", "release_identity": { - "commit": "", + "commit": "3a8874fef", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" }, "in_progress_since": "2026-10-08 18:3x UK", "approvals": { @@ -2650,6 +2802,28 @@ "run_id": "team-2026-10-08", "evidence": "docs/analysis/class-v6/multi-family-adversary.md section 14", "in_progress": true + }, + "adversary:mf-placed": { + "requirement_id": "ADV-07", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the lifetime table and the six-row transition matrix (sections 7 and 14) on the placed rows; the 32-lane genesis comparator placed on adv-g", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" } } }, @@ -2681,15 +2855,62 @@ "manual_page": 29, "owner_lane": "adversary lane (a1a9876a88f5a72fc)", "run_status": "NOT RUN", - "evidence_path": "", - "run_id": "", - "updated": "2026-10-08 18:3x UK", + "evidence_path": "docs/analysis/class-v6/multi-family-adversary.md", + "run_id": "adversary-20261008-placed-8lane", + "updated": "2026-10-08T20:41:20.327Z", "approvals": { "scope_approved": null, "implementation_complete": null, "evidence_reproduced": null, "claim_authorised": null - } + }, + "evidence_records": { + "adversary:mf-placed": { + "requirement_id": "ADV-08", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the best-cost envelope on the complete machine (section 6) and the SRAM-die ticket reconciliation with lane B (section 15); the unaffiliated second reviewer is not this lane's", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" + } + }, + "evidence_record": { + "requirement_id": "ADV-08", + "decision": "NOT RUN", + "method": "model", + "cell": "adversary:mf-placed", + "manifest_sha": "3a8874fef", + "run_id": "adversary-20261008-placed-8lane", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "in_progress": true, + "coverage": "the best-cost envelope on the complete machine (section 6) and the SRAM-die ticket reconciliation with lane B (section 15); the unaffiliated second reviewer is not this lane's", + "release_identity": { + "commit": "3a8874fef", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:41:20.327Z" + }, + "in_progress_since": "2026-10-08T20:41:20.327Z" } ] }, @@ -8730,29 +8951,30 @@ "manual_page": 54, "owner_lane": "shipper (ae892a8b0f78fe31c)", "run_status": "NOT RUN", - "evidence_path": "site/release-manifest.json at the landing sha", - "run_id": "site-manifest-20261008-01", - "updated": "2026-10-08T20:07:45.894Z", + "evidence_path": "site/release-manifest.json at the landing sha; docs/plans/evidence/UX-01-20261008.md", + "run_id": "ux-01-20261008-win-2.0.0", + "updated": "2026-10-08T20:42:25.701Z", "evidence_record": { - "cell": "site:manifest", - "manifest_sha": "3f1a3f332", - "coverage": { - "OPS-03": "partial: the manifest's versions and network blocks regenerate from their sources; the separation itself is the node suites' (suite:exec, suite:p2p-flows)" - }, - "at": "2026-10-08T20:07:45.894Z", - "method": "static", "requirement_id": "OPS-03", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "team-reported", + "cell": "pc:install-update", + "manifest_sha": "aa354ed5", + "run_id": "ux-01-20261008-win-2.0.0", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "in_progress": true, + "coverage": "partial: the update path's read-back and the signed manifest's verify on the PC (a tampered or rolled-back payload is refused by the engine's manifest check); authority separation is the shipper's review", "release_identity": { - "commit": "3f1a3f332", + "commit": "aa354ed5", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:42:25.701Z" }, "approvals": { "scope_approved": null, @@ -8784,8 +9006,31 @@ "run_id": "site-manifest-20261008-01", "evidence": "site/release-manifest.json at the landing sha", "in_progress": false + }, + "pc:install-update": { + "requirement_id": "OPS-03", + "decision": "NOT RUN", + "method": "team-reported", + "cell": "pc:install-update", + "manifest_sha": "aa354ed5", + "run_id": "ux-01-20261008-win-2.0.0", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "in_progress": true, + "coverage": "partial: the update path's read-back and the signed manifest's verify on the PC (a tampered or rolled-back payload is refused by the engine's manifest check); authority separation is the shipper's review", + "release_identity": { + "commit": "aa354ed5", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:42:25.701Z" } - } + }, + "in_progress_since": "2026-10-08T20:42:25.701Z" }, { "id": "OPS-04", @@ -9242,15 +9487,62 @@ "manual_page": 57, "owner_lane": "update-return lane (a22d765a2e0355a9f)", "run_status": "NOT RUN", - "evidence_path": "", - "run_id": "", - "updated": "2026-10-08 18:3x UK", + "evidence_path": "docs/plans/evidence/UX-01-20261008.md", + "run_id": "ux-01-20261008-win-2.0.0", + "updated": "2026-10-08T20:42:25.701Z", "approvals": { "scope_approved": null, "implementation_complete": null, "evidence_reproduced": null, "claim_authorised": null - } + }, + "evidence_records": { + "pc:install-update": { + "requirement_id": "UX-01", + "decision": "NOT RUN", + "method": "team-reported", + "cell": "pc:install-update", + "manifest_sha": "aa354ed5", + "run_id": "ux-01-20261008-win-2.0.0", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "in_progress": true, + "coverage": "team-run evidence only (the two PCs taking the Windows entry through the app's own update path with no click and mining on every card); the P10 study is NOT RUN until unaffiliated participants are recruited", + "release_identity": { + "commit": "aa354ed5", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:42:25.701Z" + } + }, + "evidence_record": { + "requirement_id": "UX-01", + "decision": "NOT RUN", + "method": "team-reported", + "cell": "pc:install-update", + "manifest_sha": "aa354ed5", + "run_id": "ux-01-20261008-win-2.0.0", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "in_progress": true, + "coverage": "team-run evidence only (the two PCs taking the Windows entry through the app's own update path with no click and mining on every card); the P10 study is NOT RUN until unaffiliated participants are recruited", + "release_identity": { + "commit": "aa354ed5", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:42:25.701Z" + }, + "in_progress_since": "2026-10-08T20:42:25.701Z" }, { "id": "UX-02", @@ -9461,30 +9753,30 @@ "manual_page": 58, "owner_lane": "pool design seat (a3832b1c3b274b310)", "run_status": "NOT RUN", - "evidence_path": "build-1:/srv/artefacts/tas/pool-review-b-20261008-01/pool-suite-build-6.log", - "run_id": "pool-review-b-20261008-01", - "updated": "2026-10-08T19:58:33.370Z", + "evidence_path": "docs/analysis/pool/pool-pair-2026-10-08.md", + "run_id": "pool-2.0-20261008-03", + "updated": "2026-10-08T20:48:42.741Z", "evidence_record": { - "cell": "suite:pool", - "manifest_sha": "7cfa422a", - "coverage": { - "UX-05": "partial: the vote-key commitment on the wire (verify::tests::a_share_under_another_key_is_refused_before_the_hash: known-pass the member's key, known-fail another key on the share and a job naming another key, refused with code vote_key before the hash), the open pool's sidechain::check_key (the claimed key, the header's key and the reveal are one key), the same-nonce-other-template wrong_hash test, the TLS binding test; the malicious-pool run and the leave-and-retain run on the devnet-4 pair are the UX-05 batch's other evidence; review B INT-15 (8 October 2026): server::authorise_tests::a_replayed_proof_of_possession_is_not_an_authorisation (a public PoP replayed into another session, payout, pool or chain, or under another key, refused; only the challenge-bound binding v2 admits on a public network) and the_binding_policy_follows_the_network", - "UX-04": "partial: PPLNS distribution tests (fee first, never overpays, late joiner), the payout key file round trip, the signed transfer decode; the live payout path on the devnet-4 pair is the UX-04 batch's evidence; review B F12 and INT-03/INT-04 (8 October 2026): payout::intent_tests (a restart from the pre-broadcast snapshot, a lost send response, a crash on either side of the broadcast, a stuck transaction replaced under the same intent, a reorged receipt and a finality pause, a failed transaction, a ledger that cannot be written: never a duplicate or a lost obligation, finalised only under the chain's final lock) and state::ledger_tests (an unreadable ledger is refused or restored, never emptied); F13/INT-16: frame, admission and pool::share_tests (the frame bound while reading, the bounded outgoing queue, one membership per session, the nonce and in-flight bounds, the share and connection budgets)" - }, - "at": "2026-10-08T19:58:33.370Z", - "method": "native", "requirement_id": "UX-04", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "native", + "cell": "suite:pool", + "manifest_sha": "986252e73", + "run_id": "pool-2.0-20261008-03", + "evidence": "docs/analysis/pool/pool-pair-2026-10-08.md", + "in_progress": true, + "coverage": "partial: PPLNS distribution tests (fee first, never overpays, late joiner), the payout key file round trip, the signed transfer decode; the live payout path on the devnet-4 pair is the UX-04 batch's evidence; review B F12 and INT-03/INT-04 (8 October 2026): payout::intent_tests (a restart from the pre-broadcast snapshot, a lost send response, a crash on either side of the broadcast, a stuck transaction replaced under the same intent, a reorged receipt and a finality pause, a failed transaction, a ledger that cannot be written: never a duplicate or a lost obligation, finalised only under the chain's final lock) and state::ledger_tests (an unreadable ledger is refused or restored, never emptied); F13/INT-16: frame, admission and pool::share_tests (the frame bound while reading, the bounded outgoing queue, one membership per session, the nonce and in-flight bounds, the share and connection budgets); the devnet-4 pair's class (8 October 2026 21:24 UK): state_provider::tests::an_unsynced_node_hands_the_pool_no_state_and_no_job (no job on a node whose igneum_getExecStatus reads synced false, blocked or re-executing; known-failed first against the 8ff7a0f4 provider; pool-synced-guard-202 f3e99e9c on release-2.0.2, 986252e7 on pool-2.0, 52 of 52 on build-6)", "release_identity": { - "commit": "7cfa422a", + "commit": "986252e73", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:48:42.741Z" }, "in_progress_since": "2026-10-08T19:58:33.370Z", "approvals": { @@ -9495,29 +9787,26 @@ }, "evidence_records": { "suite:pool": { - "cell": "suite:pool", - "manifest_sha": "7cfa422a", - "coverage": { - "UX-05": "partial: the vote-key commitment on the wire (verify::tests::a_share_under_another_key_is_refused_before_the_hash: known-pass the member's key, known-fail another key on the share and a job naming another key, refused with code vote_key before the hash), the open pool's sidechain::check_key (the claimed key, the header's key and the reveal are one key), the same-nonce-other-template wrong_hash test, the TLS binding test; the malicious-pool run and the leave-and-retain run on the devnet-4 pair are the UX-05 batch's other evidence; review B INT-15 (8 October 2026): server::authorise_tests::a_replayed_proof_of_possession_is_not_an_authorisation (a public PoP replayed into another session, payout, pool or chain, or under another key, refused; only the challenge-bound binding v2 admits on a public network) and the_binding_policy_follows_the_network", - "UX-04": "partial: PPLNS distribution tests (fee first, never overpays, late joiner), the payout key file round trip, the signed transfer decode; the live payout path on the devnet-4 pair is the UX-04 batch's evidence; review B F12 and INT-03/INT-04 (8 October 2026): payout::intent_tests (a restart from the pre-broadcast snapshot, a lost send response, a crash on either side of the broadcast, a stuck transaction replaced under the same intent, a reorged receipt and a finality pause, a failed transaction, a ledger that cannot be written: never a duplicate or a lost obligation, finalised only under the chain's final lock) and state::ledger_tests (an unreadable ledger is refused or restored, never emptied); F13/INT-16: frame, admission and pool::share_tests (the frame bound while reading, the bounded outgoing queue, one membership per session, the nonce and in-flight bounds, the share and connection budgets)" - }, - "at": "2026-10-08T19:58:33.370Z", - "method": "native", "requirement_id": "UX-04", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "native", + "cell": "suite:pool", + "manifest_sha": "986252e73", + "run_id": "pool-2.0-20261008-03", + "evidence": "docs/analysis/pool/pool-pair-2026-10-08.md", + "in_progress": true, + "coverage": "partial: PPLNS distribution tests (fee first, never overpays, late joiner), the payout key file round trip, the signed transfer decode; the live payout path on the devnet-4 pair is the UX-04 batch's evidence; review B F12 and INT-03/INT-04 (8 October 2026): payout::intent_tests (a restart from the pre-broadcast snapshot, a lost send response, a crash on either side of the broadcast, a stuck transaction replaced under the same intent, a reorged receipt and a finality pause, a failed transaction, a ledger that cannot be written: never a duplicate or a lost obligation, finalised only under the chain's final lock) and state::ledger_tests (an unreadable ledger is refused or restored, never emptied); F13/INT-16: frame, admission and pool::share_tests (the frame bound while reading, the bounded outgoing queue, one membership per session, the nonce and in-flight bounds, the share and connection budgets); the devnet-4 pair's class (8 October 2026 21:24 UK): state_provider::tests::an_unsynced_node_hands_the_pool_no_state_and_no_job (no job on a node whose igneum_getExecStatus reads synced false, blocked or re-executing; known-failed first against the 8ff7a0f4 provider; pool-synced-guard-202 f3e99e9c on release-2.0.2, 986252e7 on pool-2.0, 52 of 52 on build-6)", "release_identity": { - "commit": "7cfa422a", + "commit": "986252e73", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" }, - "run_id": "pool-review-b-20261008-01", - "evidence": "build-1:/srv/artefacts/tas/pool-review-b-20261008-01/pool-suite-build-6.log", - "in_progress": true + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:48:42.741Z" } } }, @@ -9548,12 +9837,12 @@ "manual_page": 58, "owner_lane": "pool design seat (a3832b1c3b274b310)", "run_status": "NOT RUN", - "evidence_path": "build-1:/srv/artefacts/tas/pool-review-b-20261008-01/pool-suite-build-6.log", - "run_id": "pool-review-b-20261008-01", - "updated": "2026-10-08T20:10:24.556Z", + "evidence_path": "docs/analysis/pool/pool-pair-2026-10-08.md", + "run_id": "pool-2.0-20261008-03", + "updated": "2026-10-08T20:48:42.741Z", "evidence_record": { "reason": "voting keys through pooling is the pool lane's row", - "at": "2026-10-08T20:10:24.556Z", + "at": "2026-10-08T20:48:42.741Z", "method": "static", "requirement_id": "UX-05", "decision": "NOT RUN", @@ -9577,29 +9866,26 @@ }, "evidence_records": { "suite:pool": { - "cell": "suite:pool", - "manifest_sha": "7cfa422a", - "coverage": { - "UX-05": "partial: the vote-key commitment on the wire (verify::tests::a_share_under_another_key_is_refused_before_the_hash: known-pass the member's key, known-fail another key on the share and a job naming another key, refused with code vote_key before the hash), the open pool's sidechain::check_key (the claimed key, the header's key and the reveal are one key), the same-nonce-other-template wrong_hash test, the TLS binding test; the malicious-pool run and the leave-and-retain run on the devnet-4 pair are the UX-05 batch's other evidence; review B INT-15 (8 October 2026): server::authorise_tests::a_replayed_proof_of_possession_is_not_an_authorisation (a public PoP replayed into another session, payout, pool or chain, or under another key, refused; only the challenge-bound binding v2 admits on a public network) and the_binding_policy_follows_the_network", - "UX-04": "partial: PPLNS distribution tests (fee first, never overpays, late joiner), the payout key file round trip, the signed transfer decode; the live payout path on the devnet-4 pair is the UX-04 batch's evidence; review B F12 and INT-03/INT-04 (8 October 2026): payout::intent_tests (a restart from the pre-broadcast snapshot, a lost send response, a crash on either side of the broadcast, a stuck transaction replaced under the same intent, a reorged receipt and a finality pause, a failed transaction, a ledger that cannot be written: never a duplicate or a lost obligation, finalised only under the chain's final lock) and state::ledger_tests (an unreadable ledger is refused or restored, never emptied); F13/INT-16: frame, admission and pool::share_tests (the frame bound while reading, the bounded outgoing queue, one membership per session, the nonce and in-flight bounds, the share and connection budgets)" - }, - "at": "2026-10-08T19:58:33.370Z", - "method": "native", "requirement_id": "UX-05", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "native", + "cell": "suite:pool", + "manifest_sha": "986252e73", + "run_id": "pool-2.0-20261008-03", + "evidence": "docs/analysis/pool/pool-pair-2026-10-08.md", + "in_progress": true, + "coverage": "partial: the vote-key commitment on the wire (verify::tests::a_share_under_another_key_is_refused_before_the_hash: known-pass the member's key, known-fail another key on the share and a job naming another key, refused with code vote_key before the hash), the open pool's sidechain::check_key (the claimed key, the header's key and the reveal are one key), the same-nonce-other-template wrong_hash test, the TLS binding test; the malicious-pool run and the leave-and-retain run on the devnet-4 pair are the UX-05 batch's other evidence; review B INT-15 (8 October 2026): server::authorise_tests::a_replayed_proof_of_possession_is_not_an_authorisation (a public PoP replayed into another session, payout, pool or chain, or under another key, refused; only the challenge-bound binding v2 admits on a public network) and the_binding_policy_follows_the_network", "release_identity": { - "commit": "7cfa422a", + "commit": "986252e73", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" }, - "run_id": "pool-review-b-20261008-01", - "evidence": "build-1:/srv/artefacts/tas/pool-review-b-20261008-01/pool-suite-build-6.log", - "in_progress": true + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:48:42.741Z" } } }, @@ -9719,32 +10005,30 @@ "manual_page": 59, "owner_lane": "shipper (ae892a8b0f78fe31c)", "run_status": "NOT RUN", - "evidence_path": "build-1:/srv/artefacts/tas/201-7cfa422a-aa0e0f45/miner-9c844503/box2-app.log", - "run_id": "201-7cfa422a-aa0e0f45", - "updated": "2026-10-08T19:32:50.856Z", + "evidence_path": "build-1:/srv/artefacts/tas/201-7cfa422a-aa0e0f45/miner-9c844503/box2-app.log; docs/plans/evidence/UX-01-20261008.md", + "run_id": "ux-01-20261008-win-2.0.0", + "updated": "2026-10-08T20:42:25.701Z", "evidence_record": { - "cell": "suite:app", - "manifest_sha": "7cfa422a", - "coverage": { - "UX-02": "partial: the engine state machine's pause, stop and knob tests; the operator study is UX-01's", - "UX-03": "partial: the card and earnings rendering tests; the net-earnings model against real bills is COM/ECO evidence", - "UX-07": "partial: the refused-update and manifest tests; the update-return lane's install classes are its own rows", - "VER-08": "partial: the app's own state-word tests; the wallet and light client rows are VER-01 to VER-03" - }, - "at": "2026-10-08T19:32:50.856Z", - "method": "native", "requirement_id": "UX-07", "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "method": "team-reported", + "cell": "pc:install-update", + "manifest_sha": "aa354ed5", + "run_id": "ux-01-20261008-win-2.0.0", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "in_progress": true, + "coverage": "partial: the install classes (the payload's stop step, the detached installer under a job, the installer refusing a wrong-version name, a stale install flag) and the no-click resume; the recovery-instruction and canary rows are the shipper's", "release_identity": { - "commit": "7cfa422a", + "commit": "aa354ed5", "lockfile": "", "binary": "", "network_object": "", "activation": "", "profile_hashes": "" - } + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:42:25.701Z" }, "in_progress_since": "2026-10-08T19:32:50.856Z", "approvals": { @@ -9780,6 +10064,28 @@ "run_id": "201-7cfa422a-aa0e0f45", "evidence": "build-1:/srv/artefacts/tas/201-7cfa422a-aa0e0f45/miner-9c844503/box2-app.log", "in_progress": true + }, + "pc:install-update": { + "requirement_id": "UX-07", + "decision": "NOT RUN", + "method": "team-reported", + "cell": "pc:install-update", + "manifest_sha": "aa354ed5", + "run_id": "ux-01-20261008-win-2.0.0", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "in_progress": true, + "coverage": "partial: the install classes (the payload's stop step, the detached installer under a job, the installer refusing a wrong-version name, a stale install flag) and the no-click resume; the recovery-instruction and canary rows are the shipper's", + "release_identity": { + "commit": "aa354ed5", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "", + "reviewer": "", + "at": "2026-10-08T20:42:25.701Z" } } }, @@ -11145,6 +11451,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F01 (P0, the external review): the regression's harness is the owner lane's (proving lane, node lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F01-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11201,6 +11527,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F01 (P0, the external review): the regression's harness is the owner lane's (proving lane, node lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F01-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11257,6 +11603,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F01 (P0, the external review): the regression's harness is the owner lane's (proving lane, node lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F01-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11313,6 +11679,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F01 (P0, the external review): the regression's harness is the owner lane's (proving lane, node lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F01-R04", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11369,6 +11755,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F02-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11425,6 +11831,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F02-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11481,6 +11907,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F02-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11519,22 +11965,71 @@ "POW-01", "ROT-02" ], - "updated": "2026-10-08T20:10:24.556Z", + "run_id": "f03-manifest-20261008-01", + "evidence_path": "build-1:/srv/artefacts/tas/f03-manifest-20261008-01/build-from-manifest-c30ab32c-build4.log; build-4:/srv/builds/igneum-wt-f03-201; tools/ci/build-from-manifest.sh; tools/ci/release-manifest-check.sh", + "updated": "2026-10-08T20:51:07.788Z", "evidence_record": { - "reason": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", - "at": "2026-10-08T20:10:24.556Z", - "method": "static", "requirement_id": "R2-F03-R01", - "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "decision": "PASS", + "method": "static", + "cell": "harness:release-manifest", + "manifest_sha": "c30ab32c", + "run_id": "f03-manifest-20261008-01", + "evidence": "build-1:/srv/artefacts/tas/f03-manifest-20261008-01/build-from-manifest-c30ab32c-build4.log; build-4:/srv/builds/igneum-wt-f03-201; tools/ci/build-from-manifest.sh; tools/ci/release-manifest-check.sh", + "in_progress": false, + "coverage": "full for the components the tree builds (node, miner, pool, app, prove-host from one manifest, no unpublished vendor tree); the GPU workers are the kit's cross-build and read under the shipper's kit-isa line, not this cell", "release_identity": { - "commit": "", - "lockfile": "", - "binary": "", - "network_object": "", - "activation": "", - "profile_hashes": "" + "commit": "c30ab32c", + "lockfile": "the release tree's Cargo.lock files at c30ab32c", + "binary": "cargo check only: no binary is claimed by this cell; the fleet binaries are the shipper's kit on aa0e0f45", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "none (a build fact)", + "profile_hashes": "release-manifest-check: every component's own pin equals packaging/release-manifest.json" + }, + "claim_impact": "F03's first rung: one manifest, one build, no unpublished vendor tree; the same-work rung (R02) and the transition rung (R03) stay NOT RUN until the readers run on the job context", + "reviewer": "", + "at": "2026-10-08T20:51:07.788Z" + }, + "evidence_records": { + "record": { + "reason": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F03-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + }, + "harness:release-manifest": { + "requirement_id": "R2-F03-R01", + "decision": "PASS", + "method": "static", + "cell": "harness:release-manifest", + "manifest_sha": "c30ab32c", + "run_id": "f03-manifest-20261008-01", + "evidence": "build-1:/srv/artefacts/tas/f03-manifest-20261008-01/build-from-manifest-c30ab32c-build4.log; build-4:/srv/builds/igneum-wt-f03-201; tools/ci/build-from-manifest.sh; tools/ci/release-manifest-check.sh", + "in_progress": false, + "coverage": "full for the components the tree builds (node, miner, pool, app, prove-host from one manifest, no unpublished vendor tree); the GPU workers are the kit's cross-build and read under the shipper's kit-isa line, not this cell", + "release_identity": { + "commit": "c30ab32c", + "lockfile": "the release tree's Cargo.lock files at c30ab32c", + "binary": "cargo check only: no binary is claimed by this cell; the fleet binaries are the shipper's kit on aa0e0f45", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "none (a build fact)", + "profile_hashes": "release-manifest-check: every component's own pin equals packaging/release-manifest.json" + }, + "claim_impact": "F03's first rung: one manifest, one build, no unpublished vendor tree; the same-work rung (R02) and the transition rung (R03) stay NOT RUN until the readers run on the job context", + "reviewer": "", + "at": "2026-10-08T20:51:07.788Z" } }, "approvals": { @@ -11593,6 +12088,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F03-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11649,6 +12164,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F03-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11706,6 +12241,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F04-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11763,6 +12318,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F04-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11820,6 +12395,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F04-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11877,6 +12472,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F04-R04", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11933,6 +12548,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F05 (P1, the external review): the regression's harness is the owner lane's (hash lane, adversary lane, floor lane 3); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F05-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -11989,6 +12624,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F05 (P1, the external review): the regression's harness is the owner lane's (hash lane, adversary lane, floor lane 3); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F05-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12045,6 +12700,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F05 (P1, the external review): the regression's harness is the owner lane's (hash lane, adversary lane, floor lane 3); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F05-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12101,6 +12776,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F06 (P1, the external review): the regression's harness is the owner lane's (hash lane, research lane D); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F06-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12157,6 +12852,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F06 (P1, the external review): the regression's harness is the owner lane's (hash lane, research lane D); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F06-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12213,6 +12928,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F06 (P1, the external review): the regression's harness is the owner lane's (hash lane, research lane D); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F06-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12269,6 +13004,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F07 (P1, the external review): the regression's harness is the owner lane's (app lane, fleet lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F07-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12325,6 +13080,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F07 (P1, the external review): the regression's harness is the owner lane's (app lane, fleet lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F07-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12381,6 +13156,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F07 (P1, the external review): the regression's harness is the owner lane's (app lane, fleet lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F07-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12437,6 +13232,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F08 (P1, the external review): the regression's harness is the owner lane's (proving lane, fleet lane, site (ops page)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F08-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12493,6 +13308,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F08 (P1, the external review): the regression's harness is the owner lane's (proving lane, fleet lane, site (ops page)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F08-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12549,6 +13384,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F08 (P1, the external review): the regression's harness is the owner lane's (proving lane, fleet lane, site (ops page)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F08-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12606,6 +13461,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F09 (P1, the external review): the regression's harness is the owner lane's (research lane, floor lane 3, coordinator); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F09-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12663,6 +13538,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F09 (P1, the external review): the regression's harness is the owner lane's (research lane, floor lane 3, coordinator); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F09-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12720,6 +13615,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F09 (P1, the external review): the regression's harness is the owner lane's (research lane, floor lane 3, coordinator); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F09-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12776,6 +13691,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F10 (P2, the external review): the regression's harness is the owner lane's (worker lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F10-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12832,6 +13767,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F10 (P2, the external review): the regression's harness is the owner lane's (worker lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F10-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12888,6 +13843,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F10 (P2, the external review): the regression's harness is the owner lane's (worker lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F10-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -12944,6 +13919,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F11 (P2, the external review): the regression's harness is the owner lane's (Ember lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F11-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13000,6 +13995,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F11 (P2, the external review): the regression's harness is the owner lane's (Ember lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F11-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13056,6 +14071,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F11 (P2, the external review): the regression's harness is the owner lane's (Ember lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F11-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13111,6 +14146,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F12 (P1, the external review): the regression's harness is the owner lane's (pool lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F12-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13166,6 +14221,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F12 (P1, the external review): the regression's harness is the owner lane's (pool lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F12-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13221,6 +14296,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F12 (P1, the external review): the regression's harness is the owner lane's (pool lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F12-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13277,6 +14372,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F13 (P1, the external review): the regression's harness is the owner lane's (pool lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F13-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13333,6 +14448,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F13 (P1, the external review): the regression's harness is the owner lane's (pool lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F13-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13389,6 +14524,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F13 (P1, the external review): the regression's harness is the owner lane's (pool lane (new)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F13-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13445,6 +14600,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F14 (P1, the external review): the regression's harness is the owner lane's (app lane, relay lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F14-R01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13501,6 +14676,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F14 (P1, the external review): the regression's harness is the owner lane's (app lane, relay lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F14-R02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13557,6 +14752,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "R2-F14 (P1, the external review): the regression's harness is the owner lane's (app lane, relay lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "R2-F14-R03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13619,6 +14834,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-01 (R1 integration gate): the gate's harness is the owner lane's (proving lane (a6e8f84588b809d62)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-01", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13667,6 +14902,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-02 (R1 integration gate): the gate's harness is the owner lane's (proving lane (a6e8f84588b809d62)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-02", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13715,6 +14970,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-03 (R1 integration gate): the gate's harness is the owner lane's (pool lane (a1c484c48a62948c2)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-03", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13763,6 +15038,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-04 (R1 integration gate): the gate's harness is the owner lane's (pool lane (a1c484c48a62948c2)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-04", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13811,6 +15106,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-05 (R1 integration gate): the gate's harness is the owner lane's (node lane (a283f5f0d364ceef0)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-05", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13859,6 +15174,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-06 (R1 integration gate): the gate's harness is the owner lane's (node lane (a283f5f0d364ceef0)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-06", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -13888,24 +15223,71 @@ "owner": "CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)", "manual_page": null, "owner_lane": "CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)", - "run_status": "NOT RUN", + "run_status": "BLOCKED", "master_status": "PROPOSED / NOT RUN", - "updated": "2026-10-08T20:10:24.556Z", + "updated": "2026-10-08T21:00:32.846Z", "evidence_record": { - "reason": "INT-07 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", - "at": "2026-10-08T20:10:24.556Z", - "method": "static", "requirement_id": "INT-07", - "decision": "NOT RUN", - "reviewer": "", - "claim_impact": "", + "decision": "BLOCKED", + "method": "team-reported", + "cell": "canary:fresh-install", + "manifest_sha": "c30ab32c", + "run_id": "canary-20261008-01", + "evidence": "tools/ci/canary-check.sh; packaging/ota/publish-manifest.sh; packaging/ota/publish-public.sh", + "in_progress": false, + "coverage": "partial: V6-12's clean-install half (one published object installed fresh, synced, mined, proved and paid on one host per artefact); the cross-host agreement half (node, pool, CPU verifier, each GPU host across activation) is harness:same-work's", "release_identity": { - "commit": "", + "commit": "c30ab32c (release-2.0.1 final tip; 2.0.2 open at c608b341)", "lockfile": "", - "binary": "", - "network_object": "", + "binary": "the shipped 2.0.1 entries on aa0e0f45's binaries", + "network_object": "igneum-devnet-4, chain id 4465", "activation": "", "profile_hashes": "" + }, + "claim_impact": "INT-07's clean-install half reads BLOCKED until a release tip carries a PASS fresh-install canary record; no published entry may move before one does (rule 33)", + "reviewer": "", + "at": "2026-10-08T21:00:32.846Z" + }, + "evidence_records": { + "record": { + "reason": "INT-07 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-07", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + }, + "canary:fresh-install": { + "requirement_id": "INT-07", + "decision": "BLOCKED", + "method": "team-reported", + "cell": "canary:fresh-install", + "manifest_sha": "c30ab32c", + "run_id": "canary-20261008-01", + "evidence": "tools/ci/canary-check.sh; packaging/ota/publish-manifest.sh; packaging/ota/publish-public.sh", + "in_progress": false, + "coverage": "partial: V6-12's clean-install half (one published object installed fresh, synced, mined, proved and paid on one host per artefact); the cross-host agreement half (node, pool, CPU verifier, each GPU host across activation) is harness:same-work's", + "release_identity": { + "commit": "c30ab32c (release-2.0.1 final tip; 2.0.2 open at c608b341)", + "lockfile": "", + "binary": "the shipped 2.0.1 entries on aa0e0f45's binaries", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "INT-07's clean-install half reads BLOCKED until a release tip carries a PASS fresh-install canary record; no published entry may move before one does (rule 33)", + "reviewer": "", + "at": "2026-10-08T21:00:32.846Z" } }, "approvals": { @@ -13913,7 +15295,9 @@ "implementation_complete": null, "evidence_reproduced": null, "claim_authorised": null - } + }, + "run_id": "canary-20261008-01", + "evidence_path": "tools/ci/canary-check.sh; packaging/ota/publish-manifest.sh; packaging/ota/publish-public.sh" }, { "id": "INT-08", @@ -13956,6 +15340,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-08 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-08", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14004,6 +15408,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-09 (R1 integration gate): the gate's harness is the owner lane's (app lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-09", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14052,6 +15476,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-10 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-10", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14100,6 +15544,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-11 (R1 integration gate): the gate's harness is the owner lane's (app lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-11", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14148,6 +15612,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-12 (R1 integration gate): the gate's harness is the owner lane's (proving lane (a6e8f84588b809d62) and fleet lane (ac055d60427caab99)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-12", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14196,6 +15680,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-13 (R1 integration gate): the gate's harness is the owner lane's (Ember lane (a04fe3451877e2ff0) with the app lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-13", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14244,6 +15748,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-14 (R1 integration gate): the gate's harness is the owner lane's (Ember lane (a04fe3451877e2ff0) with the app lane); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-14", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14292,6 +15816,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-15 (R1 integration gate): the gate's harness is the owner lane's (pool lane (a1c484c48a62948c2)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-15", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14340,6 +15884,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-16 (R1 integration gate): the gate's harness is the owner lane's (pool lane (a1c484c48a62948c2)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-16", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14388,6 +15952,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-17 (R1 integration gate): the gate's harness is the owner lane's (proving lane (a6e8f84588b809d62)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-17", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, @@ -14436,6 +16020,26 @@ "profile_hashes": "" } }, + "evidence_records": { + "record": { + "reason": "INT-18 (R1 integration gate): the gate's harness is the owner lane's (research lane (ad6a2bd47d4a46105)); not yet named in the map", + "at": "2026-10-08T20:10:24.556Z", + "method": "static", + "requirement_id": "INT-18", + "decision": "NOT RUN", + "reviewer": "", + "claim_impact": "", + "release_identity": { + "commit": "", + "lockfile": "", + "binary": "", + "network_object": "", + "activation": "", + "profile_hashes": "" + }, + "in_progress": false + } + }, "approvals": { "scope_approved": null, "implementation_complete": null, diff --git a/packaging/ota/publish-manifest.sh b/packaging/ota/publish-manifest.sh index d9e88deff..c99b5def3 100755 --- a/packaging/ota/publish-manifest.sh +++ b/packaging/ota/publish-manifest.sh @@ -53,7 +53,7 @@ TOKEN_FILE="$HOME/.config/igneum/dl-token" SIGNER="$ROOT/app/igneum-app/target/release/igneum-ota-sign" PRODUCT="app" -VERSION="" MAC="" WIN="" NOTES="" ACTIVATION="" DEADLINE="" MIN_SUPPORTED="" CHANNEL="devnet" BASE="" DEST="" DEPLOY=0 VERIFY_ONLY=0 TRIES=12 +RELEASE_SHA="" CANARY_GUARD_SELF_TEST=0 VERSION="" MAC="" WIN="" NOTES="" ACTIVATION="" DEADLINE="" MIN_SUPPORTED="" CHANNEL="devnet" BASE="" DEST="" DEPLOY=0 VERIFY_ONLY=0 TRIES=12 NODE_BIN="" NET_DIGEST="" MOVE_CLOCK="" DIGEST_GUARD_SELF_TEST=0 OVERRIDE="" TUNING_FILE="" NO_TUNING=0 PUBLIC=0 while [ $# -gt 0 ]; do @@ -83,6 +83,8 @@ while [ $# -gt 0 ]; do --network-digest) NET_DIGEST="$2"; shift 2 ;; # the network's CURRENT digest (a 64-hex, or "log:[@user@host]" read by tools/digest-read.sh on the hub) --move-clock) MOVE_CLOCK="$2"; shift 2 ;; # "HH:MM UTC, ": the move's clock, when the entry's digest differs by design; logged in the notes --self-test-digest-guard) DIGEST_GUARD_SELF_TEST=1; shift ;; + --release-sha) RELEASE_SHA="$2"; shift 2 ;; # rule 33 (8 Oct 2026): the release tip's commit; its fresh-install canary record must read PASS (tools/ci/canary-check.sh) + --self-test-canary-guard) CANARY_GUARD_SELF_TEST=1; shift ;; *) echo "unknown argument: $1" >&2; exit 2 ;; esac done @@ -109,6 +111,47 @@ if [ "$DIGEST_GUARD_SELF_TEST" = 1 ]; then echo "digest guard self-test: a differing digest is refused without a move clock and accepted with one; an equal digest passes; the digest reader parses" exit 0 fi +# Rule 33 (the founder, 8 October 2026, 21:3x UK, "no more lost time"): a release entry may not publish without a fresh-install +# canary record for its sha (install from the artefact on a non-AVX-512 box with an empty datadir, genesis to tip, five minutes +# mining with zero refusals and an accepted block, one shard claimed, proved and paid or queued, a bounded quit, every line read +# back): tools/ci/canary/.json read by tools/ci/canary-check.sh. A write to a real downloads folder needs --release-sha and a +# PASS record; a loopback --base-url test write and --verify-only do not publish and are not gated. +canary_guard() { # [kind ...] -> 0 pass (the lines printed), 1 refused; a kind (mac, windows) asks for that entry's own artefact block + local sha="$1"; shift; local k + [ -n "$sha" ] || { echo "canary guard: REFUSED: a release entry needs --release-sha with a fresh-install canary record (rule 33; tools/ci/canary-check.sh --form)"; return 1; } + if [ $# -eq 0 ]; then bash "$TOOLS/ci/canary-check.sh" "$sha" || { echo "canary guard: REFUSED: the entry's sha ${sha:0:12} has no PASS fresh-install canary record (rule 33)"; return 1; }; return 0; fi + for k in "$@"; do bash "$TOOLS/ci/canary-check.sh" "$sha" --artefact "$k" || { echo "canary guard: REFUSED: the $k entry's sha ${sha:0:12} has no PASS fresh-install canary record for its own artefact (rule 33)"; return 1; }; done +} +if [ "$CANARY_GUARD_SELF_TEST" = 1 ]; then + bash "$TOOLS/ci/canary-check.sh" --self-test >/dev/null || exit 1 + d=$(mktemp -d); trap 'rm -rf "$d"' EXIT; S=0123456789abcdef0123456789abcdef01234567 + canary_guard "" >/dev/null 2>&1 && { echo "canary guard self-test: FAIL: an entry with no --release-sha was accepted"; exit 1; } + CANARY_DIR="$d" canary_guard "$S" >/dev/null 2>&1 && { echo "canary guard self-test: FAIL: a sha with no record was accepted"; exit 1; } + bash "$TOOLS/ci/canary-check.sh" --form | python3 -c " +import json,sys; r=json.load(sys.stdin); r['sha']='$S'; r['artefact']['sha256']='ab'*32; r['box']={'host':'build-4','isa_line':'kit-isa: clean'} +r['sync'].update(tip_height=10, seconds=1); r['quit']['seconds']=1; r['recorded_at']='t'; r['recorded_by']='self-test'; json.dump(r, open('$d/$S.json','w'))" + CANARY_DIR="$d" canary_guard "$S" >/dev/null 2>&1 || { echo "canary guard self-test: FAIL: a sha with a PASS record was refused"; exit 1; } + python3 -c "import json; p='$d/$S.json'; r=json.load(open(p)); r['mining']['refusals']=1; json.dump(r, open(p,'w'))" + CANARY_GUARD_FAILED=0; CANARY_DIR="$d" canary_guard "$S" >/dev/null 2>&1 && { echo "canary guard self-test: FAIL: a record with a refusal was accepted"; exit 1; } + python3 -c " +import json,copy; p='$d/$S.json'; r=json.load(open(p)); blk={k:r[k] for k in ('artefact','box','datadir','sync','mining','shard','quit','lines_read_back')}; blk['mining']['refusals']=0; arts=[] +for kind in ('fleet','windows','mac'): + b=copy.deepcopy(blk); b['kind']=kind; arts.append(b) +arts[1]['mining']['refusals']=3 +json.dump({'sha':r['sha'],'artefacts':arts,'verdict':'PASS','recorded_at':'t','recorded_by':'self-test'}, open(p,'w'))" + CANARY_DIR="$d" canary_guard "$S" mac >/dev/null 2>&1 || { echo "canary guard self-test: FAIL: the mac entry was refused though its own block passes"; exit 1; } + CANARY_DIR="$d" canary_guard "$S" windows >/dev/null 2>&1 && { echo "canary guard self-test: FAIL: the windows entry passed on a failing windows block"; exit 1; } + CANARY_DIR="$d" canary_guard "$S" mac windows >/dev/null 2>&1 && { echo "canary guard self-test: FAIL: a mac plus windows publish passed with the windows block failing"; exit 1; } + echo "canary guard self-test: no sha, no record and a failing record are refused; a PASS record passes; an entry publishes on its own artefact's block (mac passes, windows refused on its own failing block); the record check's own self-test passes" + exit 0 +fi +case "${BASE:-}" in http://127.0.0.1*|http://localhost*|http://\[::1\]*) CANARY_GATED=0 ;; *) CANARY_GATED=1 ;; esac +if [ "$VERIFY_ONLY" != 1 ] && [ "$CANARY_GATED" = 1 ] && [ -z "${IGNEUM_CANARY_SELF_TEST_SKIP:-}" ]; then + KINDS=""; [ -n "$MAC" ] && KINDS="$KINDS mac"; [ -n "$WIN" ] && KINDS="$KINDS windows" + # shellcheck disable=SC2086 + canary_guard "$RELEASE_SHA" $KINDS || exit 1 + NOTES="${NOTES:+$NOTES; }release: $RELEASE_SHA (fresh-install canary PASS)" +fi if [ -n "$NODE_BIN" ] || [ -n "$NET_DIGEST" ]; then [ -n "$NODE_BIN" ] && [ -n "$NET_DIGEST" ] || { echo "the digest guard needs both --node-bin and --network-digest" >&2; exit 2; } ENTRY_DIGEST=$(bash "$TOOLS/digest-read.sh" binary "$NODE_BIN") || exit 1 diff --git a/packaging/ota/publish-public.sh b/packaging/ota/publish-public.sh index 0f8b67b00..bd63d98a6 100755 --- a/packaging/ota/publish-public.sh +++ b/packaging/ota/publish-public.sh @@ -32,10 +32,11 @@ KEY="$CFG/ota-signing-key"; PUB_KEY="$CFG/ota-signing-key.pub" SIGNER="${IGNEUM_OTA_SIGN:-$ROOT/app/igneum-app/target/release/igneum-ota-sign}" # a built signer elsewhere (another worktree) HOST="https://dl.igneum.network" -DO_APP=0 DO_WALLET=0 HIVE="" DO_ALIASES=0 DRY=0 DEPLOY=0 VERIFY=0 PRUNE=1 DEST="" BASE="" TRIES=12 +RELEASE_SHA="" DO_APP=0 DO_WALLET=0 HIVE="" DO_ALIASES=0 DRY=0 DEPLOY=0 VERIFY=0 PRUNE=1 DEST="" BASE="" TRIES=12 while [ $# -gt 0 ]; do case "$1" in --app) DO_APP=1; shift ;; + --release-sha) RELEASE_SHA="$2"; shift 2 ;; # rule 33: the release tip's commit (read from the app manifest's notes when not given); its canary record must read PASS --wallet) DO_WALLET=1; shift ;; --hive) HIVE="$2"; shift 2 ;; --aliases) DO_ALIASES=1; shift ;; @@ -107,6 +108,26 @@ PY log " $name: $(python3 -c 'import json,sys; m=json.load(open(sys.argv[1])); print(m.get("version"), "+".join(m.get("platforms",{})))' "$out") written, signed, verified; URLs under $BASE_PUB" } +# Rule 33 (8 October 2026): nothing goes to dl/public/ for a release whose sha has no PASS fresh-install canary record. The app +# manifest carries "release: " in its notes (publish-manifest.sh --release-sha writes it); --release-sha overrides or supplies it +# for the HiveOS package. tools/ci/canary-check.sh reads tools/ci/canary/.json. +canary_gate() { # [kind ...]: each kind (mac, windows, hive) must have its own PASS artefact block; no kind = the whole record + local sha="$1" what="$2"; shift 2; local k; local chk; chk="$(cd "$(dirname "$0")/../.." && pwd)/tools/ci/canary-check.sh" + [ -n "$sha" ] || { echo "canary guard: REFUSED: $what names no release sha (publish the token entry with publish-manifest.sh --release-sha, or pass --release-sha here); rule 33" >&2; return 1; } + if [ $# -eq 0 ]; then bash "$chk" "$sha" | scrub || { echo "canary guard: REFUSED: ${sha:0:12} has no PASS fresh-install canary record; rule 33" >&2; return 1; }; return 0; fi + for k in "$@"; do bash "$chk" "$sha" --artefact "$k" | scrub || { echo "canary guard: REFUSED: $what: the $k entry's sha ${sha:0:12} has no PASS fresh-install canary record for its own artefact; rule 33" >&2; return 1; }; done +} +if [ "$DO_APP" = 1 ] && [ -z "${IGNEUM_CANARY_SELF_TEST_SKIP:-}" ]; then + sha="$RELEASE_SHA" + [ -n "$sha" ] || sha=$(python3 -c 'import json,re,sys; m=json.load(open(sys.argv[1])); x=re.search(r"release: ([0-9a-f]{8,40})", str(m.get("notes",""))); print(x.group(1) if x else "")' "$SRC/igneum-app-latest.json" 2>/dev/null || true) + kinds=$(python3 -c 'import json,sys; m=json.load(open(sys.argv[1])); ks=[] +for p in m.get("platforms",{}): + ks.append("mac" if "mac" in p.lower() or "darwin" in p.lower() else "windows" if "win" in p.lower() else p) +print(" ".join(ks))' "$SRC/igneum-app-latest.json" 2>/dev/null || true) + # shellcheck disable=SC2086 + canary_gate "$sha" "the app manifest" $kinds || exit 1 +fi +if [ -n "$HIVE" ] && [ -z "${IGNEUM_CANARY_SELF_TEST_SKIP:-}" ]; then canary_gate "$RELEASE_SHA" "the HiveOS package" hive || exit 1; fi if [ "$DO_APP" = 1 ]; then log "app manifest -> dl/public/"; publish_manifest igneum-app-latest.json; fi if [ "$DO_WALLET" = 1 ]; then log "wallet manifest -> dl/public/"; publish_manifest igneum-wallet-latest.json; fi if [ -n "$HIVE" ]; then diff --git a/tools/ci/README.md b/tools/ci/README.md index 4557b253a..5b658bd91 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -16,8 +16,10 @@ | the kit ISA check (`kit-isa-check.sh`; in the gate as a self-test, in `merge-to-master.sh` over any executable a landing adds under packaging/kits or bin, and in the shipper's cut gate over the kit) | Any binary whose disassembly carries a zmm register, an EVEX opmask or an EVEX-only mnemonic (AVX-512): a fleet binary comes only from the cross-build kit at the x86-64-v3 baseline, never from a box's native gate build (79 fleet hosts died on one, 8 Oct 2026) | 8 Oct 2026 | | kill-by-name rule 5 and the no-kill shim (`kill-by-name-check.sh`, `no-kill-shim/{pkill,killall}` exit 97 when first in PATH) | A `pgrep -f`/`pkill -f` pattern that is a bare path, a log name or an unanchored word; only `^`-anchored command patterns, the bracket form, a variable, -x or -F pass (fifteen Mac processes died to a grep, 8 Oct 2026) | 8 Oct 2026 | | a "cut" batch needs its read-back (`test-record.mjs`) | A batch declaring `cut` without the binary's build-N:/srv path, its commit string read back equal to the manifest sha, and the kit ISA check's clean line; a sha is cut only when its binary exists on build-1 with its commit string read back | 8 Oct 2026 | +| rule 33: a release entry publishes only with a PASS fresh-install canary record for its sha (`canary-check.sh`; `tools/ci/canary/.json`, the form from `--form`; refused without by `packaging/ota/publish-manifest.sh --release-sha`, `publish-public.sh` and deploy-win.sh; the canary cell maps INT-07) | A release entry published before its sha had run a fresh install on a non-AVX-512 box with an empty datadir, synced, mined five minutes with zero refusals, claimed, proved and paid or queued one shard and quit inside a bound, every line read back (the founder's "no more lost time", 8 October 2026) | | the INT suite is generated from the master edition's integration gates (`int-suite.mjs`; the owner per the coordinator's crosswalk) and INT-17 is a rule of the writer: a cell declaring a missing oracle, pinned keys or mandatory real-proof fixture writes BLOCKED, never PASS | A registry whose INT suite drifts from traceability.json; a batch cell with `prereqs` where any is not "present" written as anything but BLOCKED | 8 Oct 2026 | | the REV suite is generated from an external review's findings.json and dispatch.md (`review-suite.mjs`; one case per required regression, NOT RUN, the owner from the dispatch table) | A registry whose REV suite differs from the generator's output (--check) | 8 Oct 2026 | +| F03 (Review B): one release manifest (`packaging/release-manifest.json` on a release branch) and every component built from it (`release-manifest-check.sh`, `build-from-manifest.sh`) | A tree whose own pins disagree with the manifest: the Windows node-source pin, the proving manifest's elf and vk sha256s and the files' hashes, the node fork's freeze fingerprint, the pool's vendored node checkout, a redefined EpochSeeds in the pool (the shadow_reps seam closes by a build against the pinned node); the build script puts the fork at the manifest's node sha and checks kaspad with igneum-pow (rule 19), the miner, the pool, the app and the prove host on a box | 8 Oct 2026 | | a registry landing carries its batches (`tools/ci/batches/.json`; `merge-to-master.sh` replays them onto master's copy at the merge) | Nothing by itself: the registry is a hot file, and a branch whose own copy of it was recorded during a seven-minute gate lost the race to another lane's rows three times in a row (8 Oct 2026, 19:1x UK). A branch that adds batch files is merged with master's registry, every added batch replayed through `test-record.mjs --record` (idempotent), and the evidence rules run on the merged result; rule 26 does not bind the registry path for such a branch | 8 Oct 2026 | | the registry's evidence rules (`registry-evidence-check.sh`, called by `merge-to-master.sh` after rule 26) | A landing that sets a case's run_status to PASS without an evidence_path that exists (in the tree at the landing, or on a build box over ssh; a box that does not answer is a line, not a refusal); a landing that changes a file under docs/analysis/ or a path a registry row names without moving that row's `updated` (the row and its evidence move together, GOV-04); a PASS whose evidence record pins another manifest than the registry's pinned_manifest_sha (stale evidence reads NOT RUN, GOV-08); a run_status written while the registry carries no approval block (thresholds before results, GOV-02) | 8 Oct 2026 | | the acceptance layer (`test-record.mjs`, `test-map.json`, `test-map-doc.mjs`; the founder's Test and Acceptance Standard, docs/plans/igneum-2.0-test-registry.json) | An automated case of the registry with no cell in the map and no NOT RUN reason; a map naming an unknown case; a stale harness-map page (generated from the JSON); the recorder's self-test: a run batch writes run_status, run_id, evidence_path, updated and the evidence record to the mapped cases only, never an accept text, and a case with no harness reads NOT RUN with its reason, never PASS by inference | 8 Oct 2026 | diff --git a/tools/ci/batches/adversary-20261008-placed-8lane.json b/tools/ci/batches/adversary-20261008-placed-8lane.json new file mode 100644 index 000000000..cc7243925 --- /dev/null +++ b/tools/ci/batches/adversary-20261008-placed-8lane.json @@ -0,0 +1,20 @@ +{ + "run_id": "adversary-20261008-placed-8lane", + "manifest_sha": "3a8874fef", + "evidence_dir": "docs/analysis/class-v6/multi-family-adversary.md", + "cells": [ + { + "cell": "adversary:mf-placed", + "status": "RUNNING", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "method": "model" + }, + { + "cell": "adversary:d2b", + "status": "RUNNING", + "evidence": "docs/analysis/class-v6/multi-family-adversary.md", + "method": "model", + "note": "evidence cited at the document (section 13, D2(b) restated on the placed rows); the mf flow's results file tools/chip-model/mf/results/d2b-n5.md stays on the branch and is cited after it lands (the recorder cites only files in the tree)" + } + ] +} diff --git a/tools/ci/batches/canary-20261008-01.json b/tools/ci/batches/canary-20261008-01.json new file mode 100644 index 000000000..171d3cdc4 --- /dev/null +++ b/tools/ci/batches/canary-20261008-01.json @@ -0,0 +1,28 @@ +{ + "run_id": "canary-20261008-01", + "manifest_sha": "c30ab32c", + "method": "team-reported", + "evidence_dir": "tools/ci/canary (no record yet)", + "boxes": [], + "release_identity": { + "commit": "c30ab32c (release-2.0.1 final tip; 2.0.2 open at c608b341)", + "lockfile": "", + "binary": "the shipped 2.0.1 entries on aa0e0f45's binaries", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "", + "profile_hashes": "" + }, + "claim_impact": "INT-07's clean-install half reads BLOCKED until a release tip carries a PASS fresh-install canary record; no published entry may move before one does (rule 33)", + "note": "Rule 33 recorded at 21:4x UK: the canary cell exists, its check and the publish-path guards are in the tree; no 2.0.x sha has a record yet, so the case is BLOCKED (prerequisite: the shipper's 2.0.2 canary tonight writes tools/ci/canary/.json).", + "cells": [ + { + "cell": "canary:fresh-install", + "cases": [ + "INT-07" + ], + "status": "BLOCKED", + "evidence": "tools/ci/canary-check.sh; packaging/ota/publish-manifest.sh; packaging/ota/publish-public.sh", + "note": "no fresh-install canary record exists for any 2.0.x release tip; the 2.0.2 canary is the shipper's tonight" + } + ] +} diff --git a/tools/ci/batches/f03-manifest-20261008-01.json b/tools/ci/batches/f03-manifest-20261008-01.json new file mode 100644 index 000000000..8c3e8570d --- /dev/null +++ b/tools/ci/batches/f03-manifest-20261008-01.json @@ -0,0 +1,39 @@ +{ + "run_id": "f03-manifest-20261008-01", + "manifest_sha": "c30ab32c", + "method": "static", + "evidence_dir": "build-1:/srv/artefacts/tas/f03-manifest-20261008-01 (build-from-manifest-c30ab32c-build4.log, sha256 6040ded8e99f0871\u2026); the build tree build-4:/srv/builds/igneum-wt-f03-201 at c30ab32c", + "boxes": [ + "build-4" + ], + "release_identity": { + "commit": "c30ab32c", + "lockfile": "the release tree's Cargo.lock files at c30ab32c", + "binary": "cargo check only: no binary is claimed by this cell; the fleet binaries are the shipper's kit on aa0e0f45", + "network_object": "igneum-devnet-4, chain id 4465", + "activation": "none (a build fact)", + "profile_hashes": "release-manifest-check: every component's own pin equals packaging/release-manifest.json" + }, + "claim_impact": "F03's first rung: one manifest, one build, no unpublished vendor tree; the same-work rung (R02) and the transition rung (R03) stay NOT RUN until the readers run on the job context", + "note": "R2-F03-R01, the clean build from one manifest: every component builds from packaging/release-manifest.json on release-2.0.1's final tip c30ab32c with the node vendored at the manifest's sha 7cfa422a as a real checkout (vendor/igneum-node and vendor/igneum-node-exec; a link ships as nothing); the pool builds only from the release tree (its igneum-pow is the class v5 freeze cbc5bd0a, rule 19; master's a65e4c5a refuses it). Earlier runs on aa0e0f45 were red on the pool (KeyReveal.sig_scheme, the pool-review-b shape not yet in the tree) and on prove-host (the exec vendor missing); both closed by the tree, not by the script. The workers are the kit's cross-build (the shipper's kit-isa line), not this build. The build: tools/ci/build-from-manifest.sh --box 4 on release-2.0.1 c30ab32c, 21:11 to 21:14 UK, kaspad with igneum-pow, igneum-miner, igneum-pool, igneum-app and igneum-prove-host all check green from packaging/release-manifest.json, and release-manifest-check reads every component's own pin equal to the manifest; the log on build-1 (sha256 6040ded8e99f0871...).", + "cells": [ + { + "cell": "harness:release-manifest", + "cases": [ + "R2-F03-R01" + ], + "status": "PASS", + "evidence": "build-1:/srv/artefacts/tas/f03-manifest-20261008-01/build-from-manifest-c30ab32c-build4.log; build-4:/srv/builds/igneum-wt-f03-201; tools/ci/build-from-manifest.sh; tools/ci/release-manifest-check.sh" + }, + { + "cell": "check:freeze", + "cases": [ + "GOV-01" + ], + "status": "NOT RUN", + "in_progress": true, + "evidence": "docs/plans/igneum-2.0-f0-manifest.md; build-4:/srv/builds/igneum-wt-f03-201/vendor/igneum-node/packaging/pow-freeze.txt; build-4:/srv/builds/igneum-wt-f03-201", + "note": "GOV-01 moves with its evidence page: the F0 manifest's cut-tip row gained the final tip c30ab32c and the 21:14 UK build-from-manifest fact; the freeze itself is unchanged (class v5 1c420786, fingerprint cbc5bd0a) and GOV-01 stays NOT RUN in progress until the signing block at 23:30" + } + ] +} diff --git a/tools/ci/batches/kills-20261008.json b/tools/ci/batches/kills-20261008.json index f2484f46e..153cb4ed3 100644 --- a/tools/ci/batches/kills-20261008.json +++ b/tools/ci/batches/kills-20261008.json @@ -15,5 +15,5 @@ "evidence": "docs/analysis/class-v6/mixed-fp32.md; docs/analysis/class-v6/rows/pow-07-fp32-unreachable.md" } ], - "note": " Evidence narrowed at 21:3x UK from the class-v6 directory to the two experiments' own files (a directory citation bound every file beneath it to these rows and refused every other lane's class-v6 landing); the verdicts are unchanged." + "note": " Evidence narrowed at 21:3x UK from the class-v6 directory to the two experiments' own files (a directory citation bound every file beneath it to these rows and refused every other lane's class-v6 landing); the verdicts are unchanged. Replayed at the rule 33 landing (21:4x UK): the narrowed evidence reaches master's rows only through a replay, and the merge replayed added batches alone until this landing." } diff --git a/tools/ci/batches/pool-2.0-20261008-03.json b/tools/ci/batches/pool-2.0-20261008-03.json new file mode 100644 index 000000000..f0fce2d7b --- /dev/null +++ b/tools/ci/batches/pool-2.0-20261008-03.json @@ -0,0 +1,14 @@ +{ + "run_id": "pool-2.0-20261008-03", + "manifest_sha": "986252e73", + "method": "native", + "evidence_dir": "docs/analysis/pool", + "cells": [ + { + "cell": "suite:pool", + "status": "RUNNING", + "method": "native", + "evidence": "docs/analysis/pool/pool-pair-2026-10-08.md" + } + ] +} \ No newline at end of file diff --git a/tools/ci/batches/ux-01-20261008-win-2.0.0.json b/tools/ci/batches/ux-01-20261008-win-2.0.0.json new file mode 100644 index 000000000..32acdab3d --- /dev/null +++ b/tools/ci/batches/ux-01-20261008-win-2.0.0.json @@ -0,0 +1,14 @@ +{ + "run_id": "ux-01-20261008-win-2.0.0", + "manifest_sha": "aa354ed5", + "evidence_dir": "docs/plans/evidence/UX-01-20261008.md", + "cells": [ + { + "cell": "pc:install-update", + "status": "RUNNING", + "evidence": "docs/plans/evidence/UX-01-20261008.md", + "method": "team-reported" + } + ], + "method": "team-reported" +} diff --git a/tools/ci/build-from-manifest.sh b/tools/ci/build-from-manifest.sh new file mode 100755 index 000000000..41c27259e --- /dev/null +++ b/tools/ci/build-from-manifest.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# F03 (Review B): every component is built from the one release manifest. Reads packaging/release-manifest.json of this tree, +# puts the node fork at the manifest's node sha under vendor/igneum-node (the pool's path dependency, so the EpochSeeds seam closes +# by a build against the pinned node), then on a box at gate priority (tools/build-remote.sh): cargo check of kaspad with the +# igneum-pow feature (rule 19 proves the generator's fingerprint at build time), igneum-miner, the pool crate (igneum-pool), the app +# crate (igneum-app) and the prove host; then tools/ci/release-manifest-check.sh over the tree and the fork. One red = exit 1. +# tools/ci/build-from-manifest.sh [--box N] [--dry] from the release branch's worktree; --dry prints the plan +set -euo pipefail +ROOT=$(git rev-parse --show-toplevel); cd "$ROOT"; BOX=""; DRY=0 +while [ $# -gt 0 ]; do case "$1" in --box) BOX="$2"; shift 2 ;; --dry) DRY=1; shift ;; *) echo "unknown $1" >&2; exit 2 ;; esac; done +M=packaging/release-manifest.json; [ -f "$M" ] || { echo "build-from-manifest: no $M on this tree" >&2; exit 2; } +NODE=$(python3 -c "import json;print(json.load(open('$M'))['node']['sha'])"); FP=$(python3 -c "import json;print(json.load(open('$M'))['generator']['fingerprint'])") +echo "build-from-manifest: node $NODE, generator fingerprint ${FP:0:16}, miner app $(git rev-parse --short HEAD)" +FORK=vendor/igneum-node; MIRROR="${IGNEUM_NODE_MIRROR:-build@188.40.146.49:/srv/igneum-node.git}" +if [ "$DRY" = 1 ]; then echo "plan: $FORK at $NODE from $MIRROR; cargo check kaspad(+igneum-pow), igneum-miner, igneum-pool, igneum-app, igneum-prove-host on box ${BOX:-auto} at gate priority; then release-manifest-check.sh $FORK"; exit 0; fi +if [ -d "$FORK/.git" ] || [ -f "$FORK/.git" ]; then git -C "$FORK" fetch -q "$MIRROR" "$NODE" 2>/dev/null || git -C "$FORK" fetch -q "$MIRROR" release-2.0.0-node; git -C "$FORK" checkout -q --detach "$NODE" +else mkdir -p vendor && git clone -q "$MIRROR" "$FORK" && git -C "$FORK" checkout -q --detach "$NODE"; fi +# the proving workspace names the same fork vendor/igneum-node-exec (proving/igneum-prove/Cargo.toml): a second checkout at the same +# sha, never a link (build-remote ships directories as overlays to the box; a link ships as nothing) +if [ -L vendor/igneum-node-exec ]; then rm -f vendor/igneum-node-exec; fi +if [ -d vendor/igneum-node-exec/.git ] || [ -f vendor/igneum-node-exec/.git ]; then git -C vendor/igneum-node-exec fetch -q "$MIRROR" "$NODE" 2>/dev/null || true; git -C vendor/igneum-node-exec checkout -q --detach "$NODE" +else git -C "$FORK" worktree add -q --detach "$ROOT/vendor/igneum-node-exec" "$NODE" 2>/dev/null || git clone -q "$MIRROR" vendor/igneum-node-exec && git -C vendor/igneum-node-exec checkout -q --detach "$NODE"; fi +echo "build-from-manifest: $FORK at $(git -C "$FORK" rev-parse --short HEAD); vendor/igneum-node-exec at $(git -C vendor/igneum-node-exec rev-parse --short HEAD)" +run() { local name="$1" dir="$2"; shift 2; echo "build-from-manifest: $name"; ( cd "$dir" && IGNEUM_AGENT=f03 bash "$ROOT/tools/build-remote.sh" ${BOX:+--box "$BOX"} --no-fetch --priority gate -- "$@" ) > "/tmp/f03-$name.log" 2>&1 || { echo "build-from-manifest: RED: $name (see /tmp/f03-$name.log)" >&2; grep -m3 -E '^error|RED|panicked' "/tmp/f03-$name.log" | cut -c1-160 >&2; return 1; }; echo "build-from-manifest: $name ok"; } +rc=0 +run kaspad "$FORK" check --release -p kaspad --features kaspad/igneum-pow || rc=1 +run igneum-miner "$FORK" check --release -p igneum-miner || rc=1 +run igneum-pool pool check --release || rc=1 +run igneum-app app/igneum-app check --release || rc=1 +run prove-host proving/igneum-prove check --release -p igneum-prove-host || rc=1 +bash tools/ci/release-manifest-check.sh "$FORK" || rc=1 +[ "$rc" = 0 ] && echo "build-from-manifest: every component builds from the manifest and every pin agrees" +exit $rc diff --git a/tools/ci/canary-check.sh b/tools/ci/canary-check.sh new file mode 100755 index 000000000..85d315279 --- /dev/null +++ b/tools/ci/canary-check.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# Rule 33 (the founder's "no more lost time", 8 October 2026, 21:3x UK): a release entry may not publish without a +# fresh-install canary record for its sha. The record is one JSON file in the tree, tools/ci/canary/.json (the +# release tip's commit, 8 to 40 hex), written by the lane that ran the canary, with the eight lines the founder named, +# each read back with its evidence path on a box (build-N:/srv/... or /srv/...). This check reads the record and says +# PASS or names the first line that is missing or failing; the publish path (packaging/ota/publish-manifest.sh, +# packaging/ota/publish-public.sh, deploy-win.sh) refuses an entry whose sha has no PASS record. +# +# tools/ci/canary-check.sh [--artefact ] exit 0: PASS (the line printed); 1: no record or a line fails (named); 2: bad args +# a record holds one artefact block at the top level, or an "artefacts" list (one block per entry's artefact: +# kind fleet | windows | mac | hive, each from its own non-AVX-512 box with its own eight lines); --artefact asks +# for that kind's block, so a Mac entry publishes on the Mac canary and the Windows entry waits for its own +# tools/ci/canary-check.sh --form prints the record form (every field, with what it must hold) +# tools/ci/canary-check.sh --self-test +# +# The record (tools/ci/canary/.json): +# sha the release tip's commit (the file name's sha, full or 8+) +# artefact {url or path, sha256}: what was installed, the published artefact itself, not a box's native build +# box {host, isa_line}: a non-AVX-512 box; isa_line is the kit-isa clean line read on it (tools/ci/kit-isa-check.sh) +# or the host's cpu flags line showing no avx512 +# datadir {path, empty_at_start: true, read_back}: the empty datadir before the install +# sync {genesis_height: 0, tip_height, seconds, read_back}: genesis to the network's tip +# mining {minutes >= 5, refusals: 0, accepted_blocks >= 1, read_back} +# shard {claimed: true, proved: true, paid_or_queued: "paid" | "queued", read_back} +# quit {bounded: true, seconds, read_back}: the process ends on its own quit inside the bound +# lines_read_back a list of the eight line names, each with an evidence path on a box +# verdict "PASS" (anything else is not a canary record for publishing) +# recorded_at, recorded_by UTC stamp, the lane +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd -P)"; ME="$HERE/$(basename "$0")"; ROOT="${CANARY_ROOT:-$(cd "$HERE/../.." && pwd -P)}" +DIR="${CANARY_DIR:-$ROOT/tools/ci/canary}" + +form() { + cat <<'EOF' +{ + "sha": "", + "artefact": {"url": "https://dl.igneum.network/public/", "sha256": "<64 hex>"}, + "box": {"host": "build-N or ", "isa_line": "kit-isa: clean ... | cpu flags: no avx512"}, + "datadir": {"path": "/srv/canary//data", "empty_at_start": true, "read_back": "build-N:/srv/canary//01-datadir.txt"}, + "sync": {"genesis_height": 0, "tip_height": 0, "seconds": 0, "read_back": "build-N:/srv/canary//02-sync.txt"}, + "mining": {"minutes": 5, "refusals": 0, "accepted_blocks": 1, "read_back": "build-N:/srv/canary//03-mining.txt"}, + "shard": {"claimed": true, "proved": true, "paid_or_queued": "paid", "read_back": "build-N:/srv/canary//04-shard.txt"}, + "quit": {"bounded": true, "seconds": 0, "read_back": "build-N:/srv/canary//05-quit.txt"}, + "lines_read_back": [ + {"line": "install", "evidence": "build-N:/srv/canary//00-install.txt"}, + {"line": "box", "evidence": "build-N:/srv/canary//00-box.txt"}, + {"line": "datadir", "evidence": "build-N:/srv/canary//01-datadir.txt"}, + {"line": "sync", "evidence": "build-N:/srv/canary//02-sync.txt"}, + {"line": "mining", "evidence": "build-N:/srv/canary//03-mining.txt"}, + {"line": "shard", "evidence": "build-N:/srv/canary//04-shard.txt"}, + {"line": "quit", "evidence": "build-N:/srv/canary//05-quit.txt"}, + {"line": "version", "evidence": "build-N:/srv/canary//00-version.txt"} + ], + "verdict": "PASS", + "recorded_at": "", + "recorded_by": "" +} +EOF +} + +check() { # [kind] -> prints the verdict line; 0 pass, 1 fail + local sha="$1" kind="${2:-}" f + case "$sha" in *[!0-9a-fA-F]*|"") echo "canary: REFUSED: '$sha' is not a commit sha"; return 1 ;; esac + [ "${#sha}" -ge 8 ] || { echo "canary: REFUSED: the sha must be 8 hex or more"; return 1; } + f=""; for c in "$DIR/$sha.json" "$DIR"/"${sha:0:8}"*.json; do [ -f "$c" ] && { f="$c"; break; }; done + [ -n "$f" ] || { echo "canary: REFUSED: no fresh-install canary record for ${sha:0:12} (rule 33: tools/ci/canary/.json, the eight lines read back; tools/ci/canary-check.sh --form)"; return 1; } + python3 - "$f" "$sha" "$kind" <<'PY' +import json, sys +f, sha = sys.argv[1], sys.argv[2].lower() +try: r = json.load(open(f)) +except Exception as e: print(f"canary: REFUSED: {f} is not JSON: {e}"); sys.exit(1) +def red(m): print(f"canary: REFUSED: {sha[:12]}: {m} ({f})"); sys.exit(1) +def need(obj, key, typ=None): + if key not in obj: red(f"the record has no '{key}'") + v = obj[key] + if typ and not isinstance(v, typ): red(f"'{key}' is not {typ.__name__ if not isinstance(typ, tuple) else '/'.join(t.__name__ for t in typ)}") + return v +rs = str(need(r, 'sha')).lower() +if not (rs.startswith(sha) or sha.startswith(rs)) or len(rs) < 8: red(f"the record's sha {rs[:12]} is not {sha[:12]}") +def box_path(p): return isinstance(p, str) and (':/' in p or p.startswith('/srv/')) +want_kind = sys.argv[3] if len(sys.argv) > 3 else '' +blocks = r['artefacts'] if isinstance(r.get('artefacts'), list) else [r] +if not blocks: red('the artefacts list is empty') +if want_kind: + blocks = [b for b in blocks if str(b.get('kind', b.get('artefact', {}).get('kind', ''))).lower() == want_kind.lower()] + if not blocks: red(f"no artefact block of kind '{want_kind}' (the entry's own canary: fleet, windows, mac or hive, each from its own box)") +lines_out = [] +for r_ in blocks: + r = r_ + a = need(r, 'artefact', dict) + if not (a.get('url') or a.get('path')): red("artefact names no url or path (the published artefact, not a native build)") + if not (isinstance(a.get('sha256'), str) and len(a['sha256']) == 64): red("artefact.sha256 is not 64 hex") + b = need(r, 'box', dict) + if not b.get('host'): red("box.host is empty") + isa = str(b.get('isa_line', '')).lower() + if not isa or ('clean' not in isa and 'no avx512' not in isa and 'no avx-512' not in isa): red("box.isa_line does not read a non-AVX-512 box (the kit-isa clean line or a cpu flags line with no avx512)") + d = need(r, 'datadir', dict) + if d.get('empty_at_start') is not True: red("datadir.empty_at_start is not true") + if not box_path(d.get('read_back')): red("datadir.read_back is not a box path") + s = need(r, 'sync', dict) + if s.get('genesis_height') != 0: red("sync.genesis_height is not 0 (the sync starts at genesis)") + if not (isinstance(s.get('tip_height'), int) and s['tip_height'] > 0): red("sync.tip_height is not a positive height") + if not box_path(s.get('read_back')): red("sync.read_back is not a box path") + m = need(r, 'mining', dict) + if not (isinstance(m.get('minutes'), (int, float)) and m['minutes'] >= 5): red("mining.minutes is under 5") + if m.get('refusals') != 0: red(f"mining.refusals is {m.get('refusals')!r}, not 0") + if not (isinstance(m.get('accepted_blocks'), int) and m['accepted_blocks'] >= 1): red("mining.accepted_blocks is under 1") + if not box_path(m.get('read_back')): red("mining.read_back is not a box path") + h = need(r, 'shard', dict) + if h.get('claimed') is not True or h.get('proved') is not True: red("shard.claimed and shard.proved must both be true") + if h.get('paid_or_queued') not in ('paid', 'queued'): red("shard.paid_or_queued must be 'paid' or 'queued'") + if not box_path(h.get('read_back')): red("shard.read_back is not a box path") + q = need(r, 'quit', dict) + if q.get('bounded') is not True: red("quit.bounded is not true") + if not (isinstance(q.get('seconds'), (int, float)) and q['seconds'] >= 0): red("quit.seconds is not a number") + if not box_path(q.get('read_back')): red("quit.read_back is not a box path") + lines = need(r, 'lines_read_back', list) + names = {str(x.get('line')) for x in lines if isinstance(x, dict)} + want = {'install', 'box', 'datadir', 'sync', 'mining', 'shard', 'quit', 'version'} + missing = sorted(want - names) + if missing: red(f"lines_read_back lacks {', '.join(missing)}") + for x in lines: + if isinstance(x, dict) and x.get('line') in want and not box_path(x.get('evidence')): red(f"line {x.get('line')} has no box evidence path") + top = json.load(open(f)) + v = r.get('verdict', top.get('verdict')) + if v != 'PASS': red(f"verdict is {v!r}, not PASS") + if not (r.get('recorded_at') or top.get('recorded_at')) or not (r.get('recorded_by') or top.get('recorded_by')): red("recorded_at or recorded_by is empty") + lines_out.append(f"{r.get('kind', 'artefact')} on {b['host']} ({str(a.get('sha256'))[:12]}…): genesis to {s['tip_height']} in {s.get('seconds')} s, {m['minutes']} min mining, 0 refusals, {m['accepted_blocks']} accepted, one shard {h['paid_or_queued']}, quit in {q['seconds']} s; eight lines read back") +print(f"canary: PASS: {sha[:12]} fresh-install canary: " + '; '.join(lines_out) + f"; recorded {blocks[0].get('recorded_at') or json.load(open(f)).get('recorded_at')} by {blocks[0].get('recorded_by') or json.load(open(f)).get('recorded_by')}") +PY +} + +if [ "${1:-}" = --form ]; then form; exit 0; fi +if [ "${1:-}" = --self-test ]; then + d=$(mktemp -d); trap 'rm -rf "$d"' EXIT; fails=0; export CANARY_DIR="$d" + SHA=0123456789abcdef0123456789abcdef01234567 + form | python3 -c " +import json,sys; r=json.load(sys.stdin); r['sha']='$SHA'; r['artefact']['sha256']='ab'*32; r['box']={'host':'build-4','isa_line':'kit-isa: clean: no AVX-512 encoding in 3 binaries'} +r['sync'].update(tip_height=3847, seconds=412); r['mining'].update(minutes=5, refusals=0, accepted_blocks=2); r['quit']['seconds']=3; r['recorded_at']='2026-10-08T21:30:00Z'; r['recorded_by']='shipper' +json.dump(r, open('$d/$SHA.json','w'))" + bash "$ME" "$SHA" >/dev/null 2>&1 || { echo "self-test failed: a complete PASS record was refused: $(bash "$ME" "$SHA" 2>&1)"; fails=1; } + bash "$ME" "${SHA:0:12}" >/dev/null 2>&1 || { echo "self-test failed: the record was not found by its short sha"; fails=1; } + out=$(bash "$ME" ffffffffffff 2>&1) && { echo "self-test failed: a sha with no record passed"; fails=1; }; case "$out" in *"no fresh-install canary record"*) ;; *) echo "self-test failed: the missing record was not named: $out"; fails=1 ;; esac + mut() { python3 -c " +import json,sys; p='$d/$SHA.json'; r=json.load(open(p)); exec(sys.argv[1]); json.dump(r, open(p,'w'))" "$1"; } + for case in "r['mining']['refusals']=1|refusals" "r['mining']['minutes']=4|under 5" "r['mining']['accepted_blocks']=0|accepted_blocks" "r['shard']['proved']=False|shard.claimed and shard.proved" "r['shard']['paid_or_queued']='lost'|paid_or_queued" "r['quit']['bounded']=False|quit.bounded" "r['datadir']['empty_at_start']=False|empty_at_start" "r['sync']['genesis_height']=100|genesis" "r['box']['isa_line']='avx512f present'|non-AVX-512" "r['lines_read_back']=r['lines_read_back'][:7]|lacks version" "r['lines_read_back'][2]['evidence']='notes.txt'|no box evidence" "r['verdict']='FAIL'|not PASS" "r['artefact']={'url':'x','sha256':'short'}|sha256"; do + cp "$d/$SHA.json" "$d/keep.json"; mut "${case%%|*}" + out=$(bash "$ME" "$SHA" 2>&1) && { echo "self-test failed: a record with ${case%%|*} passed"; fails=1; } + case "$out" in *"${case##*|}"*) ;; *) echo "self-test failed: the failing line was not named for ${case%%|*}: $out"; fails=1 ;; esac + cp "$d/keep.json" "$d/$SHA.json" + done + # the multi-artefact form: one file per sha, an artefacts list (fleet, windows, mac), each block its own box and eight lines; --artefact picks one + python3 -c " +import json; p='$d/$SHA.json'; r=json.load(open(p)); blk={k:r[k] for k in ('artefact','box','datadir','sync','mining','shard','quit','lines_read_back')} +import copy; arts=[] +for kind,host in (('fleet','lp-4090-11'),('windows','pc-2'),('mac','mini')): + b=copy.deepcopy(blk); b['kind']=kind; b['box']['host']=host; b['artefact']['url']='https://dl.igneum.network/public/'+kind; arts.append(b) +m={'sha':r['sha'],'artefacts':arts,'verdict':'PASS','recorded_at':r['recorded_at'],'recorded_by':'shipper'}; json.dump(m, open(p,'w'))" + bash "$ME" "$SHA" >/dev/null 2>&1 || { echo "self-test failed: a three-artefact record was refused: $(bash "$ME" "$SHA" 2>&1)"; fails=1; } + bash "$ME" "$SHA" --artefact mac >/dev/null 2>&1 || { echo "self-test failed: the mac block of a three-artefact record was refused"; fails=1; } + out=$(bash "$ME" "$SHA" --artefact hive 2>&1) && { echo "self-test failed: a kind with no block passed"; fails=1; }; case "$out" in *"no artefact block of kind 'hive'"*) ;; *) echo "self-test failed: the missing kind was not named: $out"; fails=1 ;; esac + python3 -c "import json; p='$d/$SHA.json'; r=json.load(open(p)); r['artefacts'][1]['mining']['refusals']=2; json.dump(r, open(p,'w'))" + out=$(bash "$ME" "$SHA" --artefact windows 2>&1) && { echo "self-test failed: a failing windows block passed under --artefact windows"; fails=1; } + bash "$ME" "$SHA" --artefact mac >/dev/null 2>&1 || { echo "self-test failed: the mac block was refused because the windows block fails (each entry publishes on its own canary)"; fails=1; } + out=$(bash "$ME" "$SHA" 2>&1) && { echo "self-test failed: the whole record passed with one failing block"; fails=1; } + echo "not json" > "$d/$SHA.json"; bash "$ME" "$SHA" >/dev/null 2>&1 && { echo "self-test failed: a non-JSON record passed"; fails=1; } + out=$(bash "$ME" "not-a-sha" 2>&1) && { echo "self-test failed: a non-sha argument passed"; fails=1; } + [ "$fails" = 0 ] && echo "self-test passed: a complete fresh-install canary record is PASS and found by its short sha; no record, a refusal, under five minutes, no accepted block, an unproved or lost shard, an unbounded quit, a non-empty datadir, a sync not from genesis, an AVX-512 box, a missing read-back line, a non-box evidence path, a non-PASS verdict or a bad artefact hash is refused and named; a one-file-per-sha record with an artefacts list (fleet, windows, mac, hive) passes whole or per --artefact kind, and a failing block fails its own kind and the whole, never another kind" + exit $fails +fi +KIND=""; SHA_ARG="" +while [ $# -gt 0 ]; do case "$1" in --artefact) KIND="$2"; shift 2 ;; *) SHA_ARG="$1"; shift ;; esac; done +[ -n "$SHA_ARG" ] || { echo "usage: $0 [--artefact ] | --form | --self-test" >&2; exit 2; } +check "$SHA_ARG" "$KIND" diff --git a/tools/ci/canary/README.md b/tools/ci/canary/README.md new file mode 100644 index 000000000..e211aa835 --- /dev/null +++ b/tools/ci/canary/README.md @@ -0,0 +1 @@ +# Rule 33 fresh-install canary records, one per release tip sha (tools/ci/canary-check.sh --form; the publish path refuses an entry without a PASS record here) diff --git a/tools/ci/checks.txt b/tools/ci/checks.txt index 055e68453..0b3835a40 100644 --- a/tools/ci/checks.txt +++ b/tools/ci/checks.txt @@ -80,7 +80,9 @@ the registry's evidence rules: a PASS names evidence that exists, a touched evid the kit ISA check: a distribution kit's binaries carry no AVX-512 encoding (the x86-64-v3 baseline; self-test with a fake objdump) F02 (Review B): the proof-rule test bypass cannot reach a release build: an env read with no cfg guard or under a default feature is red; a release binary carrying the bypass string is red (self-test) the test map merges structurally at a landing: master's cells plus the branch's, minus what the branch removed and master left (self-test) +rule 33: a release entry publishes only with a PASS fresh-install canary record for its sha (the record check and the publish guard, self-tests) the REV suite is generated from Review B's findings and dispatch and matches them (self-test, then the tree) +F03 (Review B): every component's own pin equals packaging/release-manifest.json where a release branch carries one (self-test, then the tree) the public ledger (docs/ledger-public.md) is what docs/fud-ledger.md generates: one row per item, no commit ids, times or team names (self-test first) the ledger page reads both entry heading forms (M1 and AP-F8-1) so no in-house pass row is dropped from /ledger (known-failed first) every workflow job carries timeout-minutes (site 15, changes 10, pow 60, sims 45; the hung-job class of 7 October 2026) diff --git a/tools/ci/int-suite.mjs b/tools/ci/int-suite.mjs index 3f31534c7..ea3c3af1d 100644 --- a/tools/ci/int-suite.mjs +++ b/tools/ci/int-suite.mjs @@ -24,7 +24,7 @@ export function suite(tr) { return { code: 'INT', title: 'INT: the master edition\'s integration gates (R1, the full-system review)', source: 'docs/plans/igneum-2.0-master/traceability.json integration_gates', gate: 'Integration gates closed', owner: 'the owner lanes per the coordinator\'s crosswalk', fixtures: ['F0', 'F5'], summary: `${tests.length} integration gates; each reads NOT RUN until its owner lane records a run`, tests }; } export function merge(reg, s) { const old = (reg.suites || []).find((x) => x.code === s.code); const live = new Map((old?.tests || []).map((t) => [t.id, t])); - for (const t of s.tests) { const o = live.get(t.id); if (o) for (const k of ['run_status', 'run_id', 'evidence_path', 'updated', 'evidence_record', 'in_progress_since', 'deferral_note', 'approvals']) if (k in o) t[k] = o[k]; } + for (const t of s.tests) { const o = live.get(t.id); if (o) for (const k of ['run_status', 'run_id', 'evidence_path', 'updated', 'evidence_record', 'evidence_records', 'blocked_on', 'method_recorded', 'in_progress_since', 'deferral_note', 'approvals']) if (k in o) t[k] = o[k]; } if (old?.notes) s.notes = old.notes; reg.suites = [...(reg.suites || []).filter((x) => x.code !== s.code), s]; return reg; } const canon = (o) => JSON.stringify(o, (k, v) => (v && typeof v === 'object' && !Array.isArray(v)) ? Object.fromEntries(Object.keys(v).sort().map((x) => [x, v[x]])) : v); function mapReasons(map, s) { const mapped = new Set(Object.values(map.cells || {}).flatMap((c) => c.cases || [])); map.not_run = map.not_run || {}; let n = 0; @@ -33,8 +33,8 @@ if (args.includes('--self-test')) { let fails = 0; const tr = { integration_gates: [{ id: 'INT-01', requirement: 'r one', status: 'PROPOSED / NOT RUN', source: 'R1' }, { id: 'INT-05', requirement: 'r five', status: 'x', source: 'R1' }, { id: 'INT-07', requirement: 'r seven', status: 'x', source: 'R1' }] }; const s = suite(tr); if (!(s.tests[2].definition && /V6-12/.test(s.tests[2].definition) && s.tests[2].accept === 'r seven')) { console.log('self-test failed: INT-07 does not carry V6-12 as a definition beside its verbatim requirement'); fails = 1; } if (!(s.tests.length === 3 && s.tests[0].id === 'INT-01' && s.tests[0].accept === 'r one' && /proving/.test(s.tests[0].owner_lane) && /node/.test(s.tests[1].owner_lane) && s.tests[0].run_status === 'NOT RUN' && s.tests[0].source[0] === 'R1')) { console.log('self-test failed: the INT cases are not shaped from the gates with the crosswalk owners'); fails = 1; } - const reg = { suites: [{ code: 'INT', notes: [{ text: 'n' }], tests: [{ id: 'INT-01', run_status: 'NOT RUN', in_progress_since: 't', run_id: 'r' }] }] }; const m = merge(reg, s); const i = m.suites.find((x) => x.code === 'INT'); - if (!(i.tests[0].in_progress_since === 't' && i.tests[1].run_status === 'NOT RUN' && i.notes?.length === 1)) { console.log('self-test failed: regenerating lost live fields or notes'); fails = 1; } + const reg = { suites: [{ code: 'INT', notes: [{ text: 'n' }], tests: [{ id: 'INT-01', run_status: 'NOT RUN', in_progress_since: 't', evidence_records: { 'c:x': { decision: 'PASS' } }, run_id: 'r' }] }] }; const m = merge(reg, s); const i = m.suites.find((x) => x.code === 'INT'); + if (!(i.tests[0].in_progress_since === 't' && i.tests[0].evidence_records?.['c:x']?.decision === 'PASS' && i.tests[1].run_status === 'NOT RUN' && i.notes?.length === 1)) { console.log('self-test failed: regenerating lost live fields or notes'); fails = 1; } const map = { cells: { c: { cases: ['INT-01'] } }, not_run: {} }; if (!(mapReasons(map, s) === 2 && !map.not_run['INT-01'] && /node lane/.test(map.not_run['INT-05']) && /CI steward/.test(map.not_run['INT-07']))) { console.log('self-test failed: the map reasons'); fails = 1; } if (!fails) console.log('self-test passed: one INT case per integration gate, the requirement verbatim, source R1, NOT RUN, the owner from the crosswalk; regenerating keeps live fields and notes; unmapped gates get a NOT RUN reason naming the owner'); process.exit(fails); } diff --git a/tools/ci/merge-to-master.sh b/tools/ci/merge-to-master.sh index 08d8faa24..06ef08aed 100755 --- a/tools/ci/merge-to-master.sh +++ b/tools/ci/merge-to-master.sh @@ -283,7 +283,7 @@ success 4 u push run node tools/ci/test-record.mjs --record tools/ci/batches/r-branch.json >/dev/null && git add -A && git -c user.name=t -c user.email=t@t commit -q -m "batch r-branch" git checkout -q master; printf '{"run_id":"r-master","manifest_sha":"m","method":"native","cells":[{"cell":"c2","status":"NOT RUN","evidence":"tools/ci/test-map.json"}]}\n' > /tmp/r-master.json node tools/ci/test-record.mjs --record /tmp/r-master.json >/dev/null && git add -A && git -c user.name=t -c user.email=t@t commit -q -m "master row" ) >/dev/null 2>&1 - out=$( cd "$rb" && TIP=$(git rev-parse master) && SHA=$(git rev-parse branch) && BASE=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=A "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge with replay" 2>&1 && python3 -c " + out=$( cd "$rb" && TIP=$(git rev-parse master) && SHA=$(git rev-parse branch) && BASE=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=AM "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge with replay" 2>&1 && python3 -c " import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); t={c['id']:c.get('run_id') for s in d['suites'] for c in s['tests']}; print('rows', t)" ) case "$out" in *"'X-1': 'r-branch'"*"'X-2': 'r-master'"*|*"'X-2': 'r-master'"*"'X-1': 'r-branch'"*) ;; *) echo "self-test failed: the batch replay did not land both the branch's row and master's row: $out"; fails=1 ;; esac # the page race: the branch adds cell c3 to the map (page regenerated), master adds c4 (page regenerated); the merge regenerates the page with both @@ -295,8 +295,17 @@ import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); t={c # both at once: the branch records a batch (its registry copy conflicts with master's) and adds a map cell; the page must regenerate # after the registry is rebuilt, never from a copy with conflict markers (8 October 2026, 20:24 UK: the REV landing lost to this) ( cd "$rb" && git checkout -q -b both pbase && printf '{"run_id":"r-both","manifest_sha":"m","method":"native","cells":[{"cell":"c1","status":"NOT RUN","evidence":"tools/ci/test-map.json"}]}\n' > tools/ci/batches/r-both.json && node tools/ci/test-record.mjs --record tools/ci/batches/r-both.json >/dev/null && python3 -c "import json; m=json.load(open('tools/ci/test-map.json')); m['cells']['c5']={'command':'v','box_class':'b','fixtures':[],'cases':['X-1']}; json.dump(m,open('tools/ci/test-map.json','w'))" && node tools/ci/test-map-doc.mjs >/dev/null 2>&1; git add -A; git -c user.name=t -c user.email=t@t commit -q -m both ) >/dev/null 2>&1 - out=$( cd "$rb" && TIP=$(git rev-parse master) && SHA=$(git rev-parse both) && BASE=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=A "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && MAP_CHANGED=1 && MAP_PATH=tools/ci/test-map.json && PAGE_PATH=docs/plans/igneum-2.0-test-harness-map.md && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge both" 2>&1 && grep -c -E '^### c[45]$' docs/plans/igneum-2.0-test-harness-map.md && python3 -c "import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); print('ok' if any(c.get('run_id')=='r-both' for s in d['suites'] for c in s['tests']) else 'no-batch')" ) + out=$( cd "$rb" && TIP=$(git rev-parse master) && SHA=$(git rev-parse both) && BASE=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=AM "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && MAP_CHANGED=1 && MAP_PATH=tools/ci/test-map.json && PAGE_PATH=docs/plans/igneum-2.0-test-harness-map.md && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge both" 2>&1 && grep -c -E '^### c[45]$' docs/plans/igneum-2.0-test-harness-map.md && python3 -c "import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); print('ok' if any(c.get('run_id')=='r-both' for s in d['suites'] for c in s['tests']) else 'no-batch')" ) case "$out" in *replayed*2*ok*) ;; *) echo "self-test failed: a landing with both a batch and a map change did not land both (the page before the registry rebuild?): $out"; fails=1 ;; esac + # a MODIFIED batch replays too (8 October 2026, 21:4x UK: a re-record of the kills batch on a branch never reached master's rows because + # the replay read added batches only): master carries r-mod.json, the branch re-records it as FAIL, the merge must move X-2 to FAIL + ( cd "$rb" && git checkout -q master && git tag premod && printf '{"run_id":"r-mod","manifest_sha":"m","method":"native","cells":[{"cell":"c2","status":"NOT RUN","evidence":"tools/ci/test-map.json"}]}\n' > tools/ci/batches/r-mod.json && node tools/ci/test-record.mjs --record tools/ci/batches/r-mod.json >/dev/null && git add -A && git -c user.name=t -c user.email=t@t commit -q -m r-mod && git tag mbase + git checkout -q -b mod mbase && printf '{"run_id":"r-mod","manifest_sha":"m","method":"native","cells":[{"cell":"c2","status":"FAIL","evidence":"tools/ci/test-map.json"}]}\n' > tools/ci/batches/r-mod.json && node tools/ci/test-record.mjs --record tools/ci/batches/r-mod.json >/dev/null && git add -A && git -c user.name=t -c user.email=t@t commit -q -m mod + git checkout -q master && printf '{"run_id":"r-m2","manifest_sha":"m","method":"native","cells":[{"cell":"c1","status":"NOT RUN","evidence":"tools/ci/test-map.json"}]}\n' > tools/ci/batches/r-m2.json && node tools/ci/test-record.mjs --record tools/ci/batches/r-m2.json >/dev/null && git add -A && git -c user.name=t -c user.email=t@t commit -q -m m2 ) >/dev/null 2>&1 || { echo "self-test failed: the modified-batch fixture did not build"; fails=1; } + out=$( cd "$rb" && TIP=$(git rev-parse master) && SHA=$(git rev-parse mod) && BASE=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=AM "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && MAP_CHANGED=0 && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge mod" 2>&1 && python3 -c " +import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); t={c['id']:c.get('run_status') for s in d['suites'] for c in s['tests']}; print('status', t)" ) + case "$out" in *"'X-2': 'FAIL'"*) ;; *) echo "self-test failed: a batch modified on the branch did not replay onto master's registry: $out"; fails=1 ;; esac + ( cd "$rb" && git checkout -q master && git reset -q --hard premod && git checkout -q both ) >/dev/null 2>&1 # the fixture back to where the later cases expect it push_race "To x ! [remote rejected] HEAD -> master (failed to update ref) remote: error: cannot lock ref 'refs/heads/master': is at a but expected b" || { echo "self-test failed: a lost compare-and-swap was not read as a race"; fails=1; } @@ -313,7 +322,7 @@ error: failed to push some refs" && { echo "self-test failed: a red check was re rm -rf "$ld/master-landing"; unset IGNEUM_LOCK_SSH IGNEUM_LOCK_DIR IGNEUM_LOCK_CAP IGNEUM_LOCK_STALE IGNEUM_LOCK_POLL # the branch-side merge of master under the lock: master moved (c4 and a row), the branch (c3 and a batch) takes it with the transforms ( cd "$rb" && git checkout -q both ) >/dev/null 2>&1 - out=$( cd "$rb" && git checkout -q both && TIP=$(git rev-parse master) && SHA=$(git rev-parse both) && BASE0=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=A "$BASE0" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && MAP_CHANGED=1 && MAP_PATH=tools/ci/test-map.json && PAGE_PATH=docs/plans/igneum-2.0-test-harness-map.md && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge master into both" branch 2>&1 && git merge-base --is-ancestor master HEAD && echo ancestor && grep -c -E '^### c[45]$' docs/plans/igneum-2.0-test-harness-map.md && python3 -c "import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); print('ok' if any(c.get('run_id')=='r-both' for s in d['suites'] for c in s['tests']) and any(c.get('run_id')=='r-master' for s in d['suites'] for c in s['tests']) else 'rows-lost')" ) + out=$( cd "$rb" && git checkout -q both && TIP=$(git rev-parse master) && SHA=$(git rev-parse both) && BASE0=$(git merge-base "$TIP" "$SHA") && BATCHES=$(git diff --name-only --diff-filter=AM "$BASE0" "$SHA" -- tools/ci/batches/ | grep -E '\.json$') && MAP_CHANGED=1 && MAP_PATH=tools/ci/test-map.json && PAGE_PATH=docs/plans/igneum-2.0-test-harness-map.md && REGISTRY_PATH=docs/plans/igneum-2.0-test-registry.json && AUTHOR=(-c user.name=t -c user.email=t@t) && export TEST_RECORD_ROOT="$rb" && merge_with_batches "$TIP" "$SHA" "merge master into both" branch 2>&1 && git merge-base --is-ancestor master HEAD && echo ancestor && grep -c -E '^### c[45]$' docs/plans/igneum-2.0-test-harness-map.md && python3 -c "import json; d=json.load(open('docs/plans/igneum-2.0-test-registry.json')); print('ok' if any(c.get('run_id')=='r-both' for s in d['suites'] for c in s['tests']) and any(c.get('run_id')=='r-master' for s in d['suites'] for c in s['tests']) else 'rows-lost')" ) case "$out" in *ancestor*2*ok*) ;; *) echo "self-test failed: the branch-side merge of master under the lock did not carry master's rows and cells plus the branch's: $out"; fails=1 ;; esac [ "$fails" = 0 ] && echo "self-test passed: a GitHub remote is refused with exit 2 while the suspension marker stands and a mirror remote is not; the CI rule binds a GitHub remote only; a landed master is fast-forwarded to every mirror and a refused mirror push is a line, not a failure; a green branch run merges; a red one is refused; an unknown read is waited through and refused only at the deadline; a queued run is waited for with the clock; an unrun branch is pushed once and then waited for; a red master refuses every merge but the declared fix; a registry landing's batches replay onto master's copy at the merge; a map change regenerates the harness page at the merge; a push that lost the ref race retries without re-running the hook; the master-landing lock holds for the whole landing and master merges into the branch under it" exit $fails @@ -349,7 +358,7 @@ git fetch -q "$REMOTE" master if ! git merge-base --is-ancestor "$REMOTE/master" "$SHA"; then echo "merge-to-master: master moved since the branch point ($(git rev-parse --short "$REMOTE/master")); merging it into $BRANCH under the lock" PRE_SHA="$SHA"; BASE0=$(git merge-base "$SHA" "$REMOTE/master") - BATCHES=$(git diff --name-only --diff-filter=A "$BASE0" "$SHA" -- tools/ci/batches/ | grep -E '\.json$' || true); NOTES=$(git diff --name-only --diff-filter=A "$BASE0" "$SHA" -- tools/ci/notes/ | grep -E '\.json$' || true) + BATCHES=$(git diff --name-only --diff-filter=AM "$BASE0" "$SHA" -- tools/ci/batches/ | grep -E '\.json$' || true); NOTES=$(git diff --name-only --diff-filter=AM "$BASE0" "$SHA" -- tools/ci/notes/ | grep -E '\.json$' || true) MAP_CHANGED=0; git diff --quiet "$BASE0" "$SHA" -- "${MAP_PATH:-tools/ci/test-map.json}" 2>/dev/null || MAP_CHANGED=1 REGISTRY_PATH="${REGISTRY_PATH:-docs/plans/igneum-2.0-test-registry.json}"; MAP_PATH="${MAP_PATH:-tools/ci/test-map.json}"; PAGE_PATH="${PAGE_PATH:-docs/plans/igneum-2.0-test-harness-map.md}" [ -n "$NOTES" ] || [ "$MAP_CHANGED" = 1 ] || git diff --quiet "$BASE0" "$SHA" -- "$REGISTRY_PATH" 2>/dev/null || BATCHES="${BATCHES:-.}" @@ -366,8 +375,8 @@ bash tools/ci/rule24-crate-gate.sh "$BASE" "$SHA" || { echo "merge-to-master: RE # every batch the branch added onto master's copy of the registry (tools/ci/test-record.mjs --record, idempotent), so the branch's # copy is never what lands and rule 26 does not bind the registry path for such a branch (the evidence rules run on the merged result) REGISTRY_PATH="${REGISTRY_PATH:-docs/plans/igneum-2.0-test-registry.json}" -BATCHES=$(git diff --name-only --diff-filter=A "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$' || true) -NOTES=$(git diff --name-only --diff-filter=A "$BASE" "$SHA" -- tools/ci/notes/ | grep -E '\.json$' || true) # {suite, text}, replayed through test-record.mjs --note-file +BATCHES=$(git diff --name-only --diff-filter=AM "$BASE" "$SHA" -- tools/ci/batches/ | grep -E '\.json$' || true) +NOTES=$(git diff --name-only --diff-filter=AM "$BASE" "$SHA" -- tools/ci/notes/ | grep -E '\.json$' || true) # {suite, text}, replayed through test-record.mjs --note-file REVGEN=0; git diff --quiet "$BASE" "$SHA" -- tools/ci/review-suite.mjs tools/ci/int-suite.mjs docs/analysis/review-2026-10-08-b/findings.json docs/analysis/review-2026-10-08-b/dispatch.md docs/plans/igneum-2.0-master/traceability.json 2>/dev/null || REVGEN=1 # the REV and INT suites regenerate on the merged tree [ -n "$NOTES" ] || [ "$REVGEN" = 1 ] && BATCHES="${BATCHES:-.}" # the registry is rebuilt from master's copy whenever any transform rides RULE26_SKIP_PATHS=""; [ -n "$BATCHES" ] && RULE26_SKIP_PATHS="$REGISTRY_PATH" diff --git a/tools/ci/p01-vectors.py b/tools/ci/p01-vectors.py index 8ed7f724d..dd21ba1b6 100755 --- a/tools/ci/p01-vectors.py +++ b/tools/ci/p01-vectors.py @@ -11,7 +11,13 @@ the worker never answered. tools/ci/p01-vectors.py --worker [--worker-arg=X ...] --pack --reference --count 1000000 (a worker argument that itself starts with "--" must be given as --worker-arg=--serve; argparse reads "--worker-arg --serve" as two options) [--start 0] [--job-nonces 1048576] [--prehash <64 hex>] [--manifest ] --out + tools/ci/p01-vectors.py --worker [--worker-arg X ...] --job-context --phase 1|2|3 --out tools/ci/p01-vectors.py --self-test +The job context (the same-work test, docs/plans/igneum-2.0-same-work-test.md) names two packs and two references (day D and +day D+1), the prehash, the range width (range_log2, 20) and the boundary nonce; phase N runs nonces [(N-1)*2^w, N*2^w): phase 1 +under day D, phase 3 under day D+1, phase 2 under day D up to the boundary nonce and day D+1 from it, no job line crossing the +boundary, and the evidence carries the boundary block (the nonce, the program id and day bytes on each side, the two hashes +either side) that the readers are compared on. The job line is pool.rs's: `job class= era=`. """ import argparse, json, os, subprocess, sys, tempfile, time @@ -31,9 +37,36 @@ def pack_fields(pack): cls = j.get('program_class', '?'); era = j.get('era_seed_bytes', '') return j.get('seed_bytes', ''), j['dataset']['day_bytes'], cls, era, j.get('program_id', '?'), j.get('generator', '?') +def segments_for(a): + """[(start, end, pack dir, reference path)] with no job crossing a segment edge; one segment for the plain form.""" + if not a.job_context: return [(a.start, a.start + a.count, a.pack, a.reference)], None + jc = json.load(open(a.job_context)); w = int(jc.get('range_log2', 20)); n = int(a.phase) + if n not in (1, 2, 3): raise SystemExit('p01-vectors: --phase must be 1, 2 or 3') + base = os.path.dirname(os.path.abspath(a.job_context)) + pth = lambda x: x if os.path.isabs(x) else os.path.join(base, x) + packs, refs = jc['packs'], jc['references'] + s0, e0 = (n - 1) << w, n << w + if n == 1: segs = [(s0, e0, pth(packs['D']), pth(refs['D']))] + elif n == 3: segs = [(s0, e0, pth(packs['D1']), pth(refs['D1']))] + else: + b = int(jc['boundary_nonce']) + if not (s0 < b < e0): raise SystemExit(f'p01-vectors: the boundary nonce {b} is not inside phase 2 [{s0}, {e0})') + segs = [(s0, b, pth(packs['D']), pth(refs['D'])), (b, e0, pth(packs['D1']), pth(refs['D1']))] + a.start, a.count = s0, e0 - s0 + if jc.get('prehash'): a.prehash = jc['prehash'] + if jc.get('manifest') and not a.manifest: a.manifest = jc['manifest'] + return segs, jc + def run(a): - ref = read_reference(a.reference) - seed_bytes, day_bytes, cls, era, program_id, generator = pack_fields(a.pack) + segs, jc = segments_for(a) + ref = {}; seg_fields = [] + for (ss, se, pack, rpath) in segs: + r = read_reference(rpath); ref.update({k: v for k, v in r.items() if ss <= k < se}); seg_fields.append((ss, se, pack_fields(pack))) + seed_bytes, day_bytes, cls, era, program_id, generator = seg_fields[0][2] + def fields_at(n): + for (ss, se, f) in seg_fields: + if ss <= n < se: return ss, se, f + return None p = subprocess.Popen([a.worker] + a.worker_arg, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) ready = None; t0 = time.time() for line in p.stdout: @@ -44,8 +77,9 @@ def run(a): def feed(): nonlocal seq, start while start < end and len(pending) < 4: - n = min(a.job_nonces, end - start); seq += 1 - p.stdin.write(f"job {seq} {a.prehash} {'f'*16} {start} {n} {seed_bytes} {day_bytes} class={cls} era={era}\n"); p.stdin.flush() + ss, se, (sb, db, c, er, _pid, _gen) = fields_at(start) + n = min(a.job_nonces, end - start, se - start); seq += 1 # a job never crosses a segment edge (the day boundary) + p.stdin.write(f"job {seq} {a.prehash} {'f'*16} {start} {n} {sb} {db} class={c} era={er}\n"); p.stdin.flush() pending.add(seq); start += n feed() for line in p.stdout: @@ -72,7 +106,15 @@ def run(a): 'worker': a.worker, 'worker_args': a.worker_arg, 'device_line': ready, 'manifest_sha': a.manifest, 'prehash': a.prehash, 'nonces': {'start': a.start, 'count': a.count}, 'answered': len(got), 'agree': agree, 'disagree': disagree_count[0], 'missing': len(missing), 'first_disagreements': disagree, 'first_missing': missing[:10], 'worker_errors': errors[:10], 'seconds': round(time.time() - t0, 1), 'at': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())} - ev['verdict'] = 'PASS' if (agree == a.count and not disagree and not missing) else 'FAIL' + if jc is not None: + ev['job_context'] = os.path.abspath(a.job_context); ev['phase'] = int(a.phase); ev['object'] = jc.get('object') + ev['segments'] = [{'start': ss, 'end': se, 'program_id': f[4], 'day_bytes': f[1], 'class': f[2]} for (ss, se, f) in seg_fields] + if len(seg_fields) == 2: + b = seg_fields[1][0]; fb, fa = seg_fields[0][2], seg_fields[1][2] + ev['boundary'] = {'nonce': b, 'program_id_before': fb[4], 'program_id_after': fa[4], 'day_bytes_before': fb[1], 'day_bytes_after': fa[1], + 'hash_before': got.get(b - 1), 'hash_after': got.get(b), 'reference_before': ref.get(b - 1), 'reference_after': ref.get(b), + 'switched': bool(fb[1] != fa[1] and got.get(b - 1) == ref.get(b - 1) and got.get(b) == ref.get(b))} + ev['verdict'] = 'PASS' if (agree == a.count and not disagree and not missing and (jc is None or len(seg_fields) < 2 or ev['boundary']['switched'])) else 'FAIL' return ev, (0 if ev['verdict'] == 'PASS' else 1) disagree_count = [0]; errors = [] @@ -92,6 +134,7 @@ for line in sys.stdin: for k in range(start, start + n): if os.environ.get("DROP") == "1" and k == 9: continue h = (k * 0x9e3779b97f4a7c15) % (1 << 64) + if p[7] == "dd" * 19: h ^= 0xdd00dd00dd00dd00 # day D+1's bytes hash differently (a reader on the wrong day disagrees) if os.environ.get("WRONG") == "1" and k == 7: h ^= 1 print(f"found {seq} {k} {h:016x}", flush=True) print(f"done {seq} {n} 1.0", flush=True) @@ -107,15 +150,40 @@ for line in sys.stdin: if not (rc == 1 and ev.get('verdict') == 'FAIL' and ev['disagree'] == 1 and ev['first_disagreements'][0]['nonce'] == 7): print(f"self-test failed: one wrong hash was not a FAIL naming nonce 7: rc={rc} {ev.get('first_disagreements')}"); fails = 1 rc, ev = go({'DROP': '1'}, 'drop') if not (rc == 1 and ev['missing'] == 1 and ev['first_missing'] == [9]): print(f"self-test failed: an unanswered nonce was not a FAIL naming nonce 9: rc={rc} {ev.get('first_missing')}"); fails = 1 - if not fails: print('self-test passed: a clean million-shape run is PASS with the counts; one wrong hash is FAIL naming the nonce, the gpu and cpu hashes; an unanswered nonce is FAIL naming it; the job lines carry the pack fields and the all-pass target') + # the job-context form: two packs (day D cc.., day D+1 dd..), two references, range_log2 4 (16 nonces a phase), boundary 24 inside phase 2 + pack2 = os.path.join(d, 'pack2'); os.makedirs(pack2) + json.dump({'seed_bytes': 'aa' * 32, 'program_class': 'v5', 'era_seed_bytes': 'bb' * 32, 'program_id': '0x2', 'generator': 5, 'dataset': {'day_bytes': 'dd' * 19, 'log2_words': 20}}, open(os.path.join(pack2, 'program.json'), 'w')) + refD = os.path.join(d, 'refD.txt'); open(refD, 'w').write(''.join(f"{k} {(k * 0x9e3779b97f4a7c15) % (1 << 64):016x}\n" for k in range(0, 48))) + refD1 = os.path.join(d, 'refD1.txt'); open(refD1, 'w').write(''.join(f"{k} {((k * 0x9e3779b97f4a7c15) % (1 << 64)) ^ 0xdd00dd00dd00dd00:016x}\n" for k in range(0, 48))) + jc = os.path.join(d, 'job-context.json') + json.dump({'object': 'fake', 'prehash': '00' * 31 + '01', 'range_log2': 4, 'boundary_nonce': 24, 'manifest': 'deadbeef', 'packs': {'D': pack, 'D1': pack2}, 'references': {'D': refD, 'D1': refD1}}, open(jc, 'w')) + def gojc(phase, tag, boundary=None): + if boundary is not None: + j = json.load(open(jc)); j['boundary_nonce'] = boundary; json.dump(j, open(jc, 'w')) + out = os.path.join(d, f'{tag}.json') + r = subprocess.run([sys.executable, __file__, '--worker', sys.executable, '--worker-arg', w, '--job-context', jc, '--phase', str(phase), '--job-nonces', '8', '--out', out], capture_output=True, text=True) + return r.returncode, (json.load(open(out)) if os.path.exists(out) else {}), r.stdout + r.stderr + for ph, s0, pid in ((1, 0, '0x1'), (3, 32, '0x2')): + rc, ev, o = gojc(ph, f'jc{ph}') + if not (rc == 0 and ev.get('verdict') == 'PASS' and ev['nonces'] == {'start': s0, 'count': 16} and ev['segments'][0]['program_id'] == pid and 'boundary' not in ev): print(f"self-test failed: phase {ph} of the job context was not a PASS on its range and pack: rc={rc} {ev.get('nonces')} {ev.get('segments')} {o[-200:]}"); fails = 1 + rc, ev, o = gojc(2, 'jc2') + b = ev.get('boundary', {}) + if not (rc == 0 and ev.get('verdict') == 'PASS' and ev['nonces'] == {'start': 16, 'count': 16} and b.get('nonce') == 24 and b.get('program_id_before') == '0x1' and b.get('program_id_after') == '0x2' and b.get('switched') is True and len(ev['segments']) == 2): print(f"self-test failed: phase 2 did not switch pack at the boundary nonce 24 with the boundary block: rc={rc} {b} {o[-200:]}"); fails = 1 + rc, ev, o = gojc(2, 'jc2bad', boundary=40) + if rc == 0 or 'not inside phase 2' not in o: print(f"self-test failed: a boundary outside phase 2 was not refused: rc={rc} {o[-200:]}"); fails = 1 + if not fails: print('self-test passed: a clean million-shape run is PASS with the counts; one wrong hash is FAIL naming the nonce, the gpu and cpu hashes; an unanswered nonce is FAIL naming it; the job-context form runs each phase on its range and pack, splits phase 2 at the boundary nonce with the boundary block, and refuses a boundary outside phase 2; the job lines carry the pack fields and the all-pass target') return fails if __name__ == '__main__': if '--self-test' in sys.argv: sys.exit(self_test()) - ap = argparse.ArgumentParser(); ap.add_argument('--worker', required=True); ap.add_argument('--worker-arg', action='append', default=[]); ap.add_argument('--pack', required=True) - ap.add_argument('--reference', required=True); ap.add_argument('--count', type=int, default=1_000_000); ap.add_argument('--start', type=int, default=0); ap.add_argument('--job-nonces', type=int, default=1 << 20) + ap = argparse.ArgumentParser(); ap.add_argument('--worker', required=True); ap.add_argument('--worker-arg', action='append', default=[]); ap.add_argument('--pack', default='') + ap.add_argument('--reference', default=''); ap.add_argument('--count', type=int, default=1_000_000); ap.add_argument('--start', type=int, default=0); ap.add_argument('--job-nonces', type=int, default=1 << 20) ap.add_argument('--prehash', default='00' * 31 + '01'); ap.add_argument('--manifest', default=''); ap.add_argument('--out', required=True) - a = ap.parse_args(); ev, rc = run(a) + ap.add_argument('--job-context', default=''); ap.add_argument('--phase', type=int, default=0) + a = ap.parse_args() + if a.job_context and not a.phase: ap.error('--job-context needs --phase 1|2|3') + if not a.job_context and not (a.pack and a.reference): ap.error('--pack and --reference, or --job-context with --phase') + ev, rc = run(a) os.makedirs(os.path.dirname(os.path.abspath(a.out)), exist_ok=True); json.dump(ev, open(a.out, 'w'), indent=2) print(f"p01-vectors: {ev.get('verdict', 'ERROR')}: answered {ev.get('answered')} agree {ev.get('agree')} disagree {ev.get('disagree')} missing {ev.get('missing')} in {ev.get('seconds')} s -> {a.out}") sys.exit(rc) diff --git a/tools/ci/pre-push.sh b/tools/ci/pre-push.sh index 88b019638..1837e6e13 100755 --- a/tools/ci/pre-push.sh +++ b/tools/ci/pre-push.sh @@ -179,7 +179,9 @@ tree_checks() { run "the kit ISA check: a distribution kit's binaries carry no AVX-512 encoding (the x86-64-v3 baseline; self-test with a fake objdump)" bash tools/ci/kit-isa-check.sh --self-test run "F02 (Review B): the proof-rule test bypass cannot reach a release build: an env read with no cfg guard or under a default feature is red; a release binary carrying the bypass string is red (self-test)" bash tools/ci/proof-rule-bypass-check.sh --self-test run "the test map merges structurally at a landing: master's cells plus the branch's, minus what the branch removed and master left (self-test)" python3 tools/ci/test-map-merge.py --self-test + run "rule 33: a release entry publishes only with a PASS fresh-install canary record for its sha (the record check and the publish guard, self-tests)" bash -c 'bash tools/ci/canary-check.sh --self-test >/dev/null && bash packaging/ota/publish-manifest.sh --self-test-canary-guard >/dev/null' run "the REV suite is generated from Review B's findings and dispatch and matches them (self-test, then the tree)" bash tools/ci/review-suite-check.sh + run "F03 (Review B): every component's own pin equals packaging/release-manifest.json where a release branch carries one (self-test, then the tree)" bash -c 'bash tools/ci/release-manifest-check.sh --self-test >/dev/null && bash tools/ci/release-manifest-check.sh' run "the public ledger (docs/ledger-public.md) is what docs/fud-ledger.md generates: one row per item, no commit ids, times or team names (self-test first)" bash -c 'node tools/ledger/export-public.mjs --self-test && node tools/ledger/export-public.mjs --check' run "the ledger page reads both entry heading forms (M1 and AP-F8-1) so no in-house pass row is dropped from /ledger (known-failed first)" node tools/ledger-page.mjs --self-test run "every workflow job carries timeout-minutes (site 15, changes 10, pow 60, sims 45; the hung-job class of 7 October 2026)" bash tools/ci/workflow-timeouts-check.sh --self-test diff --git a/tools/ci/release-manifest-check.sh b/tools/ci/release-manifest-check.sh new file mode 100755 index 000000000..bfde43f9e --- /dev/null +++ b/tools/ci/release-manifest-check.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# F03 (Review B, 8 October 2026): one release manifest (packaging/release-manifest.json) pins node, generator, dataset policy, +# acceptance rule, host ABI, miner app, pool, proof guests, verifying keys and activation; every component's own pin must equal it: +# 1. packaging/windows/node-source.pin == manifest.node.sha +# 2. the node fork's packaging/pow-freeze.txt carries manifest.generator.fingerprint (the fork at , when given) +# 3. proving/igneum-prove/elf/manifest.json's elf and vk sha256s == manifest.proof_guests, and the files on disk hash to them +# 4. the pool's vendored node checkout (pool/../vendor/igneum-node, when present) is at manifest.node.sha +# tools/ci/release-manifest-check.sh [--tree ] [] exit 0 when every pin agrees, 1 with every disagreement named +# tools/ci/release-manifest-check.sh --self-test +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd -P)"; ME="$HERE/$(basename "$0")" +check() { # [] ; prints "red ..." lines + local tree="$1" fork="${2:-}" rc=0 + python3 - "$tree" "$fork" <<'PY' || rc=1 +import json, sys, os, hashlib, subprocess +tree, fork = sys.argv[1], sys.argv[2]; red = [] +m = json.load(open(os.path.join(tree, 'packaging/release-manifest.json'))) +pin = os.path.join(tree, 'packaging/windows/node-source.pin') +if os.path.exists(pin): + p = open(pin).read().split()[0] if open(pin).read().split() else '' + if not (p.startswith(m['node']['sha']) or m['node']['sha'].startswith(p)): red.append(f"red node-source.pin reads {p}, the manifest pins node {m['node']['sha']}") +else: red.append('red packaging/windows/node-source.pin is missing') +pm_path = os.path.join(tree, 'proving/igneum-prove/elf/manifest.json') +if os.path.exists(pm_path): + pm = json.load(open(pm_path)) + for g in ('shard', 'aggregator'): + for k in ('elf_sha256', 'vk_sha256'): + if pm[g][k] != m['proof_guests'][g][k]: red.append(f"red proof guest {g} {k}: the proving manifest reads {pm[g][k][:18]}, the release manifest {m['proof_guests'][g][k][:18]}") + for fk, sk in (('elf', 'elf_sha256'), ('vk', 'vk_sha256')): + fp = os.path.join(tree, 'proving/igneum-prove/elf', pm[g][fk]) + if os.path.exists(fp): + h = '0x' + hashlib.sha256(open(fp, 'rb').read()).hexdigest() + if h != m['proof_guests'][g][sk]: red.append(f"red proof guest {g} {fk} on disk hashes to {h[:18]}, the release manifest pins {m['proof_guests'][g][sk][:18]}") +else: red.append('red proving/igneum-prove/elf/manifest.json is missing') +# provenance (V6-10, the proving lane's class, 8 October 2026): a proving manifest that names its source_commit must name a commit +# the tree's HEAD contains; a manifest pinned from a commit this tree never carried (the 5 October manifest had no provenance at all) +# is red. A manifest without source_commit is a note, not a red, until igneum-prove-pin writes one everywhere. +if os.path.exists(pm_path) and os.path.isdir(os.path.join(tree, '.git')) or os.path.exists(pm_path) and os.path.isfile(os.path.join(tree, '.git')): + sc = pm.get('source_commit') + if sc: + r = subprocess.run(['git', '-C', tree, 'merge-base', '--is-ancestor', sc, 'HEAD'], capture_output=True, text=True) + if r.returncode != 0: red.append(f"red proving manifest source_commit {sc[:12]} is not an ancestor of this tree's HEAD (pinned from a commit this branch never carried)") + else: print('release-manifest: note: the proving manifest names no source_commit (pre-provenance pin)') +if fork: + fz = os.path.join(fork, 'packaging/pow-freeze.txt') + if os.path.exists(fz): + if m['generator']['fingerprint'] not in open(fz).read(): red.append(f"red the fork's packaging/pow-freeze.txt does not carry the manifest's generator fingerprint {m['generator']['fingerprint'][:16]}") + else: red.append(f'red {fz} is missing') + try: + head = subprocess.run(['git', '-C', fork, 'rev-parse', 'HEAD'], capture_output=True, text=True).stdout.strip() + if head and not head.startswith(m['node']['sha']): red.append(f"red the fork checkout is at {head[:8]}, the manifest pins node {m['node']['sha']}") + except Exception: pass +vend = os.path.join(tree, 'vendor/igneum-node') +if os.path.isdir(vend): + head = subprocess.run(['git', '-C', vend, 'rev-parse', 'HEAD'], capture_output=True, text=True).stdout.strip() + if head and not head.startswith(m['node']['sha']): red.append(f"red the pool's vendored node checkout is at {head[:8]}, the manifest pins node {m['node']['sha']} (the shadow_reps seam closes by a build against the pinned node)") + nodeas = os.path.join(tree, 'pool/src/node.rs') + if os.path.exists(nodeas) and 'struct EpochSeeds' in open(nodeas).read(): red.append('red pool/src/node.rs redefines EpochSeeds; it must import kaspa_pow::igneum::EpochSeeds') +# V6-12 (R1 p. 213, the master): the signed manifest binds lockfile hashes, kernel and host binaries, supported devices and drivers, the prover +# server patch, memory thresholds and tuner identity; a manifest without the block reads BLOCKED on INT-07, never a pass of it +v = m.get('v6_12') or {} +for k in ('lockfile_sha256', 'kernel_binaries', 'host_binaries', 'supported_devices', 'supported_drivers', 'prover_server_patch', 'memory_thresholds', 'tuner_identity', 'signature'): + if k not in v: print(f'note V6-12 field {k} is not in the manifest (INT-07 reads BLOCKED until it is)') +for r in red: print(r) +sys.exit(1 if red else 0) +PY + return $rc +} +if [ "${1:-}" = --self-test ]; then + d=$(mktemp -d); trap 'rm -rf "$d"' EXIT; fails=0 + mk() { local t="$d/$1"; mkdir -p "$t/packaging/windows" "$t/proving/igneum-prove/elf" "$t/pool/src"; printf 'elf' > "$t/proving/igneum-prove/elf/a.elf"; printf 'vk' > "$t/proving/igneum-prove/elf/a.vk" + local es="0x$(printf 'elf' | shasum -a 256 | cut -d' ' -f1)" vs="0x$(printf 'vk' | shasum -a 256 | cut -d' ' -f1)" + printf '{"shard":{"elf":"a.elf","elf_sha256":"%s","vk":"a.vk","vk_sha256":"%s"},"aggregator":{"elf":"a.elf","elf_sha256":"%s","vk":"a.vk","vk_sha256":"%s"}}\n' "$es" "$vs" "$es" "$vs" > "$t/proving/igneum-prove/elf/manifest.json" + printf '{"node":{"sha":"%s"},"generator":{"fingerprint":"ffff0000"},"proof_guests":{"shard":{"elf_sha256":"%s","vk_sha256":"%s"},"aggregator":{"elf_sha256":"%s","vk_sha256":"%s"}}}\n' "$2" "$es" "$vs" "$es" "$vs" > "$t/packaging/release-manifest.json" + printf '%s\n' "$3" > "$t/packaging/windows/node-source.pin"; printf 'use kaspa_pow::igneum::EpochSeeds;\n' > "$t/pool/src/node.rs"; } + mk agree abcd1234 abcd1234; mk pinoff abcd1234 ffff1234 + bash "$ME" --tree "$d/agree" >/dev/null 2>&1 || { echo "self-test failed: agreeing pins were refused: $(bash "$ME" --tree "$d/agree" 2>&1)"; fails=1; } + out=$(bash "$ME" --tree "$d/pinoff" 2>&1) && { echo "self-test failed: a node-source pin off the manifest passed"; fails=1; }; case "$out" in *"node-source.pin reads ffff1234"*) ;; *) echo "self-test failed: the pin was not named: $out"; fails=1 ;; esac + printf 'elf2' > "$d/agree/proving/igneum-prove/elf/a.elf"; out=$(bash "$ME" --tree "$d/agree" 2>&1) && { echo "self-test failed: a guest file that hashes off the manifest passed"; fails=1; }; case "$out" in *"on disk hashes to"*) ;; *) echo "self-test failed: the hash drift was not named: $out"; fails=1 ;; esac + printf 'elf' > "$d/agree/proving/igneum-prove/elf/a.elf"; mkdir -p "$d/fork/packaging"; printf '# f\nffff0000 c1 v5 2026\n' > "$d/fork/packaging/pow-freeze.txt" + bash "$ME" --tree "$d/agree" "$d/fork" >/dev/null 2>&1 || { echo "self-test failed: a fork carrying the fingerprint was refused: $(bash "$ME" --tree "$d/agree" "$d/fork" 2>&1)"; fails=1; } + printf '# f\n00000000 c1 v5 2026\n' > "$d/fork/packaging/pow-freeze.txt"; out=$(bash "$ME" --tree "$d/agree" "$d/fork" 2>&1) && { echo "self-test failed: a fork without the fingerprint passed"; fails=1; } + printf 'pub struct EpochSeeds {}\n' > "$d/agree/pool/src/node.rs"; mkdir -p "$d/agree/vendor/igneum-node" && ( cd "$d/agree/vendor/igneum-node" && git init -q && git -c user.name=t -c user.email=t@t commit -q --allow-empty -m x ) >/dev/null 2>&1 + out=$(bash "$ME" --tree "$d/agree" 2>&1) && { echo "self-test failed: a redefined EpochSeeds and an unpinned vendored node passed"; fails=1; }; case "$out" in *"redefines EpochSeeds"*) ;; *) echo "self-test failed: the seam was not named: $out"; fails=1 ;; esac + # provenance: a git tree whose proving manifest names a source_commit HEAD contains passes; one naming a commit HEAD never carried is red + mk prov abcd1234 abcd1234; ( cd "$d/prov" && git init -q && git add -A && git -c user.name=t -c user.email=t@t commit -q -m x ) >/dev/null 2>&1; sc=$(git -C "$d/prov" rev-parse HEAD) + python3 - "$d/prov/proving/igneum-prove/elf/manifest.json" "$sc" <<'PY2' +import json,sys; p=sys.argv[1]; d=json.load(open(p)); d['source_commit']=sys.argv[2]; json.dump(d,open(p,'w')) +PY2 + bash "$ME" --tree "$d/prov" >/dev/null 2>&1 || { echo "self-test failed: a manifest whose source_commit HEAD contains was refused: $(bash "$ME" --tree "$d/prov" 2>&1)"; fails=1; } + python3 - "$d/prov/proving/igneum-prove/elf/manifest.json" <<'PY2' +import json,sys; p=sys.argv[1]; d=json.load(open(p)); d['source_commit']='0'*40; json.dump(d,open(p,'w')) +PY2 + out=$(bash "$ME" --tree "$d/prov" 2>&1) && { echo "self-test failed: a manifest pinned from a commit the tree never carried passed"; fails=1; }; case "$out" in *"not an ancestor"*) ;; *) echo "self-test failed: the provenance red was not named: $out"; fails=1 ;; esac + [ "$fails" = 0 ] && echo "self-test passed: agreeing pins pass; a node-source pin, a guest file's hash, a fork freeze file, the pool's vendored node checkout, a redefined EpochSeeds or a proving manifest pinned from a commit the tree never carried is red and named" + exit $fails +fi +TREE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"; FORK="" +while [ $# -gt 0 ]; do case "$1" in --tree) TREE="$2"; shift 2 ;; *) FORK="$1"; shift ;; esac; done +[ -f "$TREE/packaging/release-manifest.json" ] || { echo "release-manifest: no packaging/release-manifest.json on this tree: not a release branch, nothing to pin"; exit 0; } +rc=0; out=$(check "$TREE" "$FORK") || rc=1 +printf '%s\n' "$out" | sed -n 's/^red /release-manifest: RED: /p; s/^note /release-manifest: /p' | grep . || true +[ "$rc" = 0 ] && echo "release-manifest: every component's own pin equals packaging/release-manifest.json" +exit $rc diff --git a/tools/ci/review-suite.mjs b/tools/ci/review-suite.mjs index 41c86bf9b..06f27ec00 100644 --- a/tools/ci/review-suite.mjs +++ b/tools/ci/review-suite.mjs @@ -36,7 +36,7 @@ function suite(findings, dispatchMd, prefix, sourceNote, master) { } function merge(reg, s) { // replace the suite of the same code, keeping live fields of cases that already exist const old = (reg.suites || []).find((x) => x.code === s.code); const live = new Map((old?.tests || []).map((t) => [t.id, t])); - for (const t of s.tests) { const o = live.get(t.id); if (o) for (const k of ['run_status', 'run_id', 'evidence_path', 'updated', 'evidence_record', 'in_progress_since', 'deferral_note', 'approvals']) if (k in o) t[k] = o[k]; } + for (const t of s.tests) { const o = live.get(t.id); if (o) for (const k of ['run_status', 'run_id', 'evidence_path', 'updated', 'evidence_record', 'evidence_records', 'blocked_on', 'method_recorded', 'in_progress_since', 'deferral_note', 'approvals']) if (k in o) t[k] = o[k]; } reg.suites = [...(reg.suites || []).filter((x) => x.code !== s.code), s]; return reg; } if (args.includes('--self-test')) { @@ -49,9 +49,9 @@ if (args.includes('--self-test')) { if (!(s.tests[0].title === 'r one' && s.tests[0].accept === 'r one')) { console.log('self-test failed: the title and accept are not the regression line verbatim'); fails = 1; } if (!(s.tests[0].owner_lane === 'lane x, lane y' && s.tests[2].owner_lane === 'lane z')) { console.log(`self-test failed: owners not read from the dispatch table: ${s.tests.map((t) => t.owner_lane)}`); fails = 1; } if (!(s.tests[0].finding === 'R2-F01' && s.tests[0].priority === 'P0' && s.tests[0].run_status === 'NOT RUN' && s.tests[0].method.includes('Automated'))) { console.log('self-test failed: finding, priority, NOT RUN or method missing'); fails = 1; } - const reg = { suites: [{ code: 'GOV', tests: [] }, { code: 'REV', tests: [{ id: 'R2-F01-R01', run_status: 'NOT RUN', in_progress_since: 't', run_id: 'r9' }] }] }; + const reg = { suites: [{ code: 'GOV', tests: [] }, { code: 'REV', tests: [{ id: 'R2-F01-R01', run_status: 'NOT RUN', in_progress_since: 't', evidence_records: { 'c:x': { decision: 'PASS' } }, run_id: 'r9' }] }] }; const m = merge(JSON.parse(JSON.stringify(reg)), s); const rev = m.suites.find((x) => x.code === 'REV'); - if (!(m.suites.length === 2 && rev.tests.length === 3 && rev.tests[0].in_progress_since === 't' && rev.tests[0].run_id === 'r9' && rev.tests[1].run_status === 'NOT RUN')) { console.log('self-test failed: a regenerated suite did not keep the existing case\'s live fields or dropped another suite'); fails = 1; } + if (!(m.suites.length === 2 && rev.tests.length === 3 && rev.tests[0].in_progress_since === 't' && rev.tests[0].evidence_records?.['c:x']?.decision === 'PASS' && rev.tests[0].run_id === 'r9' && rev.tests[1].run_status === 'NOT RUN')) { console.log('self-test failed: a regenerated suite did not keep the existing case\'s live fields or dropped another suite'); fails = 1; } const map = { cells: { c1: { cases: ['R2-F01-R01'] } }, not_run: { 'REV-F02-1': 'old' } }; const n = mapReasons(map, s); if (!(n === 2 && !('R2-F01-R01' in map.not_run) && /lane z/.test(map.not_run['REV-F02-1']) && /lane x/.test(map.not_run['REV-F01-2']))) { console.log(`self-test failed: the map's NOT RUN reasons: ${JSON.stringify(map.not_run)} n=${n}`); fails = 1; } if (!fails) console.log('self-test passed: one case per required regression with the id --, the line verbatim as title and accept, the owner from the dispatch table, finding and priority carried, NOT RUN; regenerating keeps live fields and the other suites; every unmapped case gets a NOT RUN reason naming its owner lane in the map, a mapped one loses it'); diff --git a/tools/ci/test-map.json b/tools/ci/test-map.json index dca147e97..b1b508883 100644 --- a/tools/ci/test-map.json +++ b/tools/ci/test-map.json @@ -225,8 +225,8 @@ } }, "bench:pc1-packs": { - "command": "tools/ca3-v4-amend/pc1-ca4-packs.ps1 on PC 1 (RTX 5090, the signed-jobs channel, --cards-off, the Power Helper lock at 1,300 MHz, nvidia-smi at 1 Hz, 250 x 2^24 per row, the kit's CUDA worker --bench)", - "box_class": "PC 1 bench", + "command": "tools/ca3-v4-amend/pc1-ca4-packs.ps1 on the project's own rig (RTX 5090, the signed-jobs channel, --cards-off, the Power Helper lock at 1,300 MHz, nvidia-smi at 1 Hz, 250 x 2^24 per row, the kit's CUDA worker --bench)", + "box_class": "the project's own rig bench", "fixtures": [ "F0", "F1" @@ -245,8 +245,8 @@ } }, "bench:pc1-amd": { - "command": "tools/ca3-v4-amend/pc1-amd-cardin-bench.ps1 on PC 1 (RX 7600, OpenCL, gfx1102, 30 dispatches of 2^24 per size, rate only)", - "box_class": "PC 1 bench", + "command": "tools/ca3-v4-amend/pc1-amd-cardin-bench.ps1 on the project's own rig (RX 7600, OpenCL, gfx1102, 30 dispatches of 2^24 per size, rate only)", + "box_class": "the project's own rig bench", "fixtures": [ "F0", "F1" @@ -422,7 +422,7 @@ "VER-08" ], "coverage": { - "VER-03": "partial: a payment receipt is labelled payment only when the receipt is proven against the segment statement's receipts commitment under the finality certificate, inclusion-only receipts read inclusion, a tampered receipt and a flipped status fail; run on the Devnet 3 fixtures, the 2.0 devnet re-run waits for its first paid segment whose last block carries a transaction", + "VER-03": "partial: a payment receipt is labelled payment only when the receipt is proven against the segment statement's receipts commitment under the finality certificate, inclusion-only receipts read inclusion, a tampered receipt and a flipped status fail; run on the the earlier devnet fixtures, the 2.0 devnet re-run waits for its first paid segment whose last block carries a transaction", "VER-04": "partial: the oracle's trust() names the deployer-installed table and the unchecked aggregator signature, a second hash at a stored certificate index is refused, a root claim at an unknown index is refused, another chain id is refused (tools/reference-apps/oracle/test.mjs on Sepolia); Review B F04: the Sepolia verifiers apply the two-thirds rule only, carry no lock-kind field, and an under-threshold (recovery-rule) certificate fails closed in submitCertificate, so no stored root is a recovery lock (the DEX lane's docs/bridge/light-client-bridge.md paragraph); the claimed-option review of the trust model is the reviewer's", "VER-05": "partial: the public node and the reference reader on build-1 executed the 2.0 devnet from genesis with no snapshot (unit ExecStart without --igneum-exec-snapshot), and the pages fetch headers, certificates, coinbase bodies and account proofs from the public read RPC only; the archive and availability model beyond the 2,048-block state ring is the OPS no-founder exercise", "VER-06": "partial: the read service never serves a certificate from another network, a withheld header in the path and a corrupted proof node are detected, stale data prints its lock age beside every balance, and a client told no certificate never falls back to a trusted RPC balance; detection on the fault network F4 is not run", @@ -576,8 +576,8 @@ } }, "bench:amd-intel-energy": { - "command": "the AMD-and-Intel energy lane jobs on the signed-jobs channel: run-ae-pc2-b580-energy-20261008 (PC 2, Arc B580, the class v6 kit worker --memprobe and --bench-pack on hl-v6-foldrw and hl-v6-all, Level Zero energy counter) and run-ae-pc1-7600-energy-20261008 (PC 1, RX 7600, the same with ADLX watts at stock, plimit -30, gmax -500 + plimit -30); scripts in docs/analysis/class-v6/amd-intel-energy/", - "box_class": "PC 1 and PC 2 bench (OpenCL)", + "command": "the AMD-and-Intel energy lane jobs on the signed-jobs channel: run-ae-pc2-b580-energy-20261008 (PC 2, Arc B580, the class v6 kit worker --memprobe and --bench-pack on hl-v6-foldrw and hl-v6-all, Level Zero energy counter) and run-ae-pc1-7600-energy-20261008 (the project's own rig, RX 7600, the same with ADLX watts at stock, plimit -30, gmax -500 + plimit -30); scripts in docs/analysis/class-v6/amd-intel-energy/", + "box_class": "the project's own rig and PC 2 bench (OpenCL)", "fixtures": [ "F0", "F1" @@ -588,19 +588,116 @@ "coverage": { "GPU-03": "partial: the 64-register window on AMD and Intel, rate per unit of work (RX 7600 0 percent, Arc B580 -0.3 percent, kernel throughput, quiet) with the B580 fingerprints equal on both packs and the offline RDNA allocation (160 VGPRs, no spill); energy owed (the 7600 job queued, the B580 counter unsupported unelevated); team-run, not under the standard's paired protocol or a wall meter" } + }, + "adversary:mf-placed": { + "command": "cd tools/chip-model/mf && make flow- power- (inside openroad/orfs:latest on a rented CPU host; never the Mac); python3 flow/collect.py results; python3 flow/board.py ; python3 flow/hash.py results power", + "box_class": "rented CPU host (Vast, 48 to 72 cores) on the ORFS image", + "fixtures": [ + "F0", + "F1" + ], + "cases": [ + "ADV-01", + "ADV-03", + "ADV-07", + "ADV-08", + "POW-03", + "POW-04", + "ADV-05" + ], + "coverage": { + "ADV-01": "the mf core (18 families, SRAM window and imem macros, operand isolation, 5-phase port) placed and routed on ASAP7 with SPEF and a gate-level VCD; k per family and per draw; outputs checked against the RTL simulation's checksums per tag, not yet against the POW vectors (owed)", + "ADV-03": "the data-local, hybrid and companion-host rows of multi-family-adversary.md sections 11, 13 and 14 (bit counts closed-form; the hop and wire energies claimed)", + "ADV-07": "the lifetime table and the six-row transition matrix (sections 7 and 14) on the placed rows; the 32-lane genesis comparator placed on adv-g", + "ADV-08": "the best-cost envelope on the complete machine (section 6) and the SRAM-die ticket reconciliation with lane B (section 15); the unaffiliated second reviewer is not this lane's", + "POW-03": "partial: the live state held in an SRAM macro per 8 lanes with a time-multiplexed single port and operand isolation, the complete-system cost in section 6; the liveness trace itself is the connected-state lane's tool", + "POW-04": "partial: the 64-register window's cost on the macro core (sections 4 and 5, agreeing with the k lane's gated-flop row within 5 percent); the restructured candidate's GPU cost is the invention lane's", + "ADV-05": "partial: the SRAM macros, ports, wiring, clocking and the complete-board terms are modelled in sections 2.3, 5 and 6 with the unmodelled items carried as uncertainty; the calibration against an existing hardware block is the k lane's bare-lane row beside it, owed as a named comparison" + } + }, + "adversary:d2b": { + "command": "cd tools/chip-model/mf && python3 flow/d2b.py results N5 1 && python3 flow/d2b.py results N3 1 (on a build box under lease pool or a rented host; reads results/table.csv)", + "box_class": "any box (a one-minute Python model on the placed rows)", + "fixtures": [ + "F0" + ], + "cases": [ + "ADV-02", + "ADV-04" + ], + "coverage": { + "ADV-02": "memory sharing, partial stores at the measured window-layer hit rates (and hit 0.50 with layer 8 off), recomputation and the set-up amortisation on the complete board (section 13); the formal memory model's terms (section 16)", + "ADV-04": "selective participation over 2,000 era draws under the layer 1 band with two reserve families live per epoch, the specialist's revenue against its ratio gain (section 13); downtime and re-entry priced as the DAA window's lag, not simulated against the difficulty rule itself (owed)" + } + }, + "pc:install-update": { + "command": "signed jobs and relay runs on the project's own rig (ae432dc7) and PC 2 (1ccfe586): the installer over the running app (rule 14: installed versions, the process set, the first upload), the OTA path (update-now, the update-return read-back line), and the no-click resume after the install (relay run mining-on-after-200.ps1)", + "box_class": "PC (the two Windows PCs; nothing on the Mac)", + "fixtures": [ + "F2" + ], + "cases": [ + "UX-01", + "UX-07", + "OPS-03" + ], + "coverage": { + "UX-01": "team-run evidence only (the two PCs taking the Windows entry through the app's own update path with no click and mining on every card); the P10 study is NOT RUN until unaffiliated participants are recruited", + "UX-07": "partial: the install classes (the payload's stop step, the detached installer under a job, the installer refusing a wrong-version name, a stale install flag) and the no-click resume; the recovery-instruction and canary rows are the shipper's", + "OPS-03": "partial: the update path's read-back and the signed manifest's verify on the PC (a tampered or rolled-back payload is refused by the engine's manifest check); authority separation is the shipper's review" + } + }, + "harness:release-manifest": { + "command": "bash tools/ci/release-manifest-check.sh (self-test, then the tree; the pins, the proof guests' hashes on disk, the fork freeze, the pool's vendored node, the proving manifest's source_commit provenance) and bash tools/ci/build-from-manifest.sh --box N on the release tree (kaspad with igneum-pow, igneum-miner, igneum-pool, igneum-app, igneum-prove-host from packaging/release-manifest.json with the node vendored at the manifest's sha)", + "box_class": "gate", + "fixtures": [ + "F0" + ], + "cases": [ + "R2-F03-R01" + ], + "coverage": { + "R2-F03-R01": "full for the components the tree builds (node, miner, pool, app, prove-host from one manifest, no unpublished vendor tree); the GPU workers are the kit's cross-build and read under the shipper's kit-isa line, not this cell" + } + }, + "harness:same-work": { + "command": "tools/ci/p01-vectors.py --job-context --phase 1|2|3 per reader (CUDA, OpenCL, Metal workers), igneum-miner --recheck-vectors (node), igneum-pow hash-bound (CPU reference), the pool's member-side re-check; docs/plans/igneum-2.0-same-work-test.md", + "box_class": "release (the readers on their pods and boxes)", + "fixtures": [ + "F0" + ], + "cases": [ + "R2-F03-R02", + "R2-F03-R03" + ], + "coverage": { + "R2-F03-R02": "partial until every reader has run: bit-for-bit agreement per nonce across node, CPU reference, CUDA, OpenCL, Metal and pool on one job context", + "R2-F03-R03": "partial: the day-boundary transition in phase 2 (the one transition every live network crosses hourly at 60x); a class rotation is the same test on a research object until a v6 floor is set" + } + }, + "canary:fresh-install": { + "command": "the rule 33 fresh-install canary on a release tip (the founder, 8 October 2026): install from the published artefact on a non-AVX-512 box with an empty datadir, sync genesis to tip, five minutes mining with zero refusals and an accepted block, one shard claimed, proved and paid or queued, a bounded quit, every line read back; the record tools/ci/canary/.json (one file per sha, an artefacts list: fleet, windows, mac, hive) read by tools/ci/canary-check.sh [--artefact kind], which publish-manifest.sh and publish-public.sh refuse without", + "box_class": "release (a non-AVX-512 box with an empty datadir)", + "fixtures": [ + "F0" + ], + "cases": [ + "INT-07" + ], + "coverage": { + "INT-07": "partial: V6-12's clean-install half (one published object installed fresh, synced, mined, proved and paid on one host per artefact); the cross-host agreement half (node, pool, CPU verifier, each GPU host across activation) is harness:same-work's" + } } }, "not_run": { "GOV-02": "the approval is recorded in the registry's approval field; the automated half (thresholds frozen before any run_status) is the gate rule landing by 21:00", "GOV-04": "the evidence vault F9 (raw and negative evidence preserved) is the gate rule landing by 21:00: a PASS must carry its evidence file", "GOV-08": "the stale-evidence rule (evidence older than the manifest sha reads NOT RUN) is the gate rule landing by 21:00", - "GPU-04": "the memory-clock ladder has no harness tonight: PC 1 mines nothing under the Devnet 3 off order", + "GPU-04": "the memory-clock ladder has no harness tonight: the project's own rig mines nothing under the the earlier devnet off order", "GPU-06": "accepted work under ordinary connectivity needs the fault network F4", - "GPU-07": "the sustained thermal and power soak has no harness tonight: PC 1 mines nothing under the Devnet 3 off order", + "GPU-07": "the sustained thermal and power soak has no harness tonight: the project's own rig mines nothing under the the earlier devnet off order", "POW-05": "amortised cheap winning attempts are the attack lanes' grind and era harnesses (tools/attack/f7-era, f9-grind), not in the release matrix; their rows come from those lanes", - "ADV-04": "selective participation needs the economic model F7 and a live chain window", "ADV-06": "process-advantage separation is the adversary lanes' chip study", - "ADV-07": "lifetime rows are the adversary lanes' models", "ROT-03": "miner-voted bring-forward needs a vote harness on the fault network F4", "ROT-04": "seed-selection resistance is the census harness (the class v6 invention lane), not yet in the matrix", "EVM-01": "no EVM conformance-vector harness is mapped tonight; the exec suite does not run the reference test vectors", @@ -687,7 +784,6 @@ "INT-04": "INT-04 (R1 integration gate): the gate's harness is the owner lane's (pool lane (a1c484c48a62948c2)); not yet named in the map", "INT-05": "INT-05 (R1 integration gate): the gate's harness is the owner lane's (node lane (a283f5f0d364ceef0)); not yet named in the map", "INT-06": "INT-06 (R1 integration gate): the gate's harness is the owner lane's (node lane (a283f5f0d364ceef0)); not yet named in the map", - "INT-07": "INT-07 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", "INT-08": "INT-08 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", "INT-09": "INT-09 (R1 integration gate): the gate's harness is the owner lane's (app lane); not yet named in the map", "INT-10": "INT-10 (R1 integration gate): the gate's harness is the owner lane's (CI steward with the hash lane (a690540514aa453d7) and the worker lane (a9e87343f008e0edd)); not yet named in the map", @@ -706,9 +802,6 @@ "R2-F02-R01": "R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map", "R2-F02-R02": "R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map", "R2-F02-R03": "R2-F02 (P0, the external review): the regression's harness is the owner lane's (proving lane, CI steward); not yet named in the map", - "R2-F03-R01": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", - "R2-F03-R02": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", - "R2-F03-R03": "R2-F03 (P0, the external review): the regression's harness is the owner lane's (CI steward, hash lane (ProgramClass::V6 on freeze), pool lane); not yet named in the map", "R2-F04-R01": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", "R2-F04-R02": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", "R2-F04-R03": "R2-F04 (P0, the external review): the regression's harness is the owner lane's (node lane, reference apps, site (explorer)); not yet named in the map", diff --git a/tools/fleet/box-dn3.sh b/tools/fleet/box-dn3.sh new file mode 100644 index 000000000..040d97805 --- /dev/null +++ b/tools/fleet/box-dn3.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# A Devnet 3 box (7 October 2026, the founder's clock: a fresh chain from genesis on 0.3.22, network igneum-devnet-3, every activation at 0, +# NO override file). Modelled on box-dn2.sh. The node runs with NET_ARGS (default "--devnet --devnet-suffix=3": own handshake magic, +# own data directory $F/$APPDIR; a live-devnet or Devnet 2 peer refuses it at the handshake), peered with SEED (comma list); one miner on +# card 0 with the box's vote key; PROVER=1 adds the segment prover loop (CHAIN_NAME igneum-devnet-3). NODE_BIN names the igneumd. +# FRESH=1 wipes $F/$APPDIR (a new genesis); UNSYNCED=1 adds --enable-unsynced-mining (the genesis boxes: a fresh chain's nodes start +# unsynced and must mine anyway). RPC_PORT, P2P_PORT, EVM_PORT: other ports on a box whose live node holds 26610/26611/26790 +# (a standing box running a second node for Devnet 3: 36610/36611/36790). Nothing here touches the live devnet's node or miner. +set -uo pipefail +mkdir -p /root/fleet/pids; echo $$ > /root/fleet/pids/loop.pid # 15:3xZ 8 Oct 2026: kills by pid file only (main): loop.pid, node.pid, miner.pid under /root/fleet/pids +F=/root/fleet; OUT=$F/out; mkdir -p $F/in $OUT $F/$APPDIR $F/mine/packs; exec >> $OUT/dn3.log 2>&1 +stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; } +LABEL="${LABEL:-dn3}"; WALLET="${WALLET:-0x1919191919191919191919191919191919191919}"; SEED="${SEED:-}"; NODE_BIN="${NODE_BIN:-$F/in/igneumd-0322}" +APPDIR="${APPDIR:-dn3}"; PACK="${PACK:-dn3}"; CHAIN_NAME="${CHAIN_NAME:-igneum-devnet-3}" # 15:4xZ 8 Oct 2026: devnet-4 takes APPDIR=dn4 PACK=dn4 CHAIN_NAME=igneum-devnet-4 NET_ARGS="--devnet --devnet-suffix=4" +PROVER="${PROVER:-0}"; UNSYNCED="${UNSYNCED:-}"; RPC_PORT="${RPC_PORT:-26610}"; P2P_PORT="${P2P_PORT:-26611}"; EVM_PORT="${EVM_PORT:-26790}"; JSON_PORT="${JSON_PORT:-$((RPC_PORT+2080))}"; MINE="${MINE:-1}" +NET_ARGS="${NET_ARGS:---devnet --devnet-suffix=3}"; P2P_LISTEN="${P2P_LISTEN:-0.0.0.0:$P2P_PORT}"; MINER_ONLY="${MINER_ONLY:-0}"; ANNOUNCE="${ANNOUNCE:-}"; export -n MINER_ONLY # 21:3xZ 7 Oct 2026: never in the loop's environment (the puller restarts from it; MINER_ONLY=1 there would leave the node down at the next move) # MINER_ONLY=1: the node stays, only the miner loop restarts (with --announce ip:port when ANNOUNCE is set: the peer directory) +JSONF="--rpclisten-json=127.0.0.1:$JSON_PORT"; case " $NET_ARGS ${EXTRA_ARGS:-} " in *rpclisten-json*) JSONF="";; esac # 13:3xZ 8 Oct: the pool boxes already carry it in NET_ARGS (dn3-pool-b: "cannot be used multiple times") +MINER_BIN="${MINER_BIN:-$F/in/igneum-miner-0322}" # the 0.3.22 miner (sub-version 3 draw); the hive package's 0.3.20 miner is the live devnet's and never mines Devnet 3 (16:5xZ: every block BlockInvalid) +[ -x "$NODE_BIN" ] || { echo "RESULT dn3_failed $(stamp) no node binary at $NODE_BIN"; exit 2; } +[ "${MINE:-1}" = 1 ] && { [ -x "$MINER_BIN" ] || { echo "RESULT dn3_failed $(stamp) no 0.3.22 miner at $MINER_BIN"; exit 2; }; } +# the pack-id gate (main through the shipper, 7 Oct 2026 17:0xZ): BEFORE the miner starts, the worker's pack and the node's engine must come +# from the same igneum-pow pin. Tonight's shape (the shipper, 17:05Z): BY CONSTRUCTION, the pack the worker hashes is EXPORTED on this box by the +# paired 0.3.22 miner (MINER_BIN's sha equals PAIR_MINER_SHA16, default 07246920fd9fe895 = igneum-pow 017e7037, the node's pin), never a copied kit; +# a different miner sha or a pre-shipped pack directory is UNREADABLE and holds the miner. The id-equality read over RPC (a785001687d8688a against +# the kit's id) replaces it from the next cut when the node lane names the method (NODE_ID_CMD / PACK_ID_CMD). +pack_gate() { + local msha want; msha=$(sha256sum "$MINER_BIN" | cut -c1-16); want="${PAIR_MINER_SHA16:-07246920fd9fe895 c29f33bbbd284a12 dfdc6883aa79a76f fb147dd1754cbfc0 cfa9f5ca382e0efc} $(cat $F/in/pair-miners.txt 2>/dev/null | tr '\n' ' ')" # 8 Oct 2026 09:4xZ: the list is also a FILE the puller appends every move's miner sha to (the 10:05 move: 18 miners refused for a sha only in a hand-edited default) # the two paired 0.3.22 miners (hands / node-lane builds, both igneum-pow 017e7037) + if [ -n "${NODE_ID_CMD:-}" ] && [ -n "${PACK_ID_CMD:-}" ]; then + local pid nid; pid=$(eval "$PACK_ID_CMD" 2>/dev/null); nid=$(eval "$NODE_ID_CMD" 2>/dev/null) + [ -n "$pid" ] && [ -n "$nid" ] || { echo "RESULT dn3_pack_gate $(stamp) UNREADABLE pack_id=${pid:-none} node_id=${nid:-none}"; return 1; } + [ "$pid" = "$nid" ] || { echo "RESULT dn3_pack_gate $(stamp) REFUSED pack_id=$pid node_id=$nid"; return 1; } + echo "RESULT dn3_pack_gate $(stamp) PASS by id pack_id=$pid node_id=$nid"; return 0 + fi + case " $want " in *" $msha "*) ;; *) echo "RESULT dn3_pack_gate $(stamp) UNREADABLE miner=$msha is not a paired miner ($want); a pre-shipped kit or another miner"; return 1;; esac + [ -e $F/mine/packs/$PACK ] && { echo "RESULT dn3_pack_gate $(stamp) UNREADABLE packs/$PACK already present before this export (a copied kit?)"; return 1; } + echo "RESULT dn3_pack_gate $(stamp) PASS by construction (miner $msha = pair, pack exported here by it, generator v4 sub-version 3)"; return 0 +} +echo "RESULT dn3_start $(stamp) label=$LABEL node=$(sha256sum $NODE_BIN | cut -c1-16) miner=$(sha256sum $MINER_BIN 2>/dev/null | cut -c1-16) seed=${SEED:-none} prover=$PROVER mine=$MINE net=\"$NET_ARGS\" ports=$RPC_PORT/$P2P_PORT/$EVM_PORT" +command -v curl >/dev/null || { apt-get update -qq >/dev/null 2>&1; apt-get install -y -qq curl ca-certificates python3 >/dev/null 2>&1; } +if [ ! -x /opt/igneum/pkg/bin/igneum-miner ]; then + read -r PKG_PATH PKG_SHA PKG_VER <<< "$(curl -fsSL -m 30 https://dl.igneum.network/dl/public/igneum-downloads.json | python3 -c 'import sys,json; d=json.load(sys.stdin)["files"]["miner-hive"]; print(d["path"], d["sha256"], d["version"])')" + curl -fsSL -o $F/pkg.tgz "https://dl.igneum.network$PKG_PATH" && echo "$PKG_SHA $F/pkg.tgz" | sha256sum -c - >/dev/null && mkdir -p /opt/igneum/pkg && tar -C /opt/igneum/pkg --strip-components=1 -xzf $F/pkg.tgz || { echo "RESULT dn3_failed package"; exit 2; } +fi +B=/opt/igneum/pkg/bin +# the Devnet 3 node and its miner only (anchored on the dn3 appdir and this RPC port), never the live node beside it +# kills by pid file only (founder 8 Oct 2026, by construction: pkill and killall are shimmed to exit 97 on every box) +pidkill() { local P; P=$(cat "$F/pids/$1.pid" 2>/dev/null); [ -n "$P" ] && kill -0 "$P" 2>/dev/null && { kill -TERM "$P" 2>/dev/null; sleep 2; kill -0 "$P" 2>/dev/null && kill -9 "$P" 2>/dev/null; }; return 0; } +[ "$MINER_ONLY" = 1 ] || pidkill node; pidkill miner; sleep 2 +[ "${FRESH:-0}" = 1 ] && { rm -rf $F/$APPDIR; mkdir -p $F/$APPDIR; [ -s $F/$APPDIR-node.log ] && mv $F/$APPDIR-node.log $F/$APPDIR-node.prev.log; echo "RESULT dn3_fresh $(stamp) appdir wiped for the genesis"; } +PEER=""; for sd in ${SEED//,/ }; do PEER="$PEER --addpeer=$sd"; done +UNS=""; [ "$UNSYNCED" = 1 ] && UNS="--enable-unsynced-mining" +if [ "$MINER_ONLY" != 1 ]; then +echo "=== dn3 node start $(stamp) $(sha256sum $NODE_BIN | cut -c1-16)" >> $F/$APPDIR-node.log +IGNEUM_PROOF_PROGRAM_IDS="${PROGRAM_IDS:-}" IGNEUM_PROOF_VERIFIER="${HOST_VERIFIER:-}" setsid nohup $NODE_BIN $NET_ARGS --appdir=$F/$APPDIR --rpclisten=0.0.0.0:$RPC_PORT --evm-rpclisten=127.0.0.1:$EVM_PORT $JSONF --listen=$P2P_LISTEN $PEER $UNS --unsaferpc --nodnsseed --disable-upnp --nologfiles --yes > $F/$APPDIR-node.log 2>&1 & + echo $! > $F/pids/node.pid +sleep 10 +echo "RESULT dn3_node $(stamp) pid=$(pgrep -f '[/]root/fleet/in/igneumd-[0-9a-f]+ .* --appdir=/root/fleet/$APPDIR ' | head -1) version=$(grep -oE 'igneumd/[0-9]+\.[0-9]+\.[0-9]+-[0-9a-f]+' $F/$APPDIR-node.log | tail -1) digest=$(grep -o 'digest: [0-9a-f]*' $F/$APPDIR-node.log | tail -1 | awk '{print substr($2,1,16)}') network=$(grep -oiE 'igneum-devnet-[0-9][^ ,]*' $F/$APPDIR-node.log | head -1) genesis=$(grep -oiE 'genesis[^\n]{0,120}' $F/$APPDIR-node.log | grep -oE '[0-9a-f]{64}' | head -1 | cut -c1-16)" +for i in $(seq 1 30); do w="$($B/igneum-miner watch 1 grpc://127.0.0.1:$RPC_PORT 2>/dev/null | grep -o 'blocks=[0-9]*.*synced=[a-z]*' | tail -1)"; [ -n "$w" ] && break; sleep 5; done +echo "RESULT dn3_watch $(stamp) $(printf '%s' "$w" | sed -E 's/difficulty=[0-9.]* sink=[0-9a-f]* //')" +fi +rm -rf $F/mine/packs/$PACK.prev; [ -e $F/mine/packs/$PACK ] && mv $F/mine/packs/$PACK $F/mine/packs/$PACK.prev # the previous run's export, moved aside so the gate sees a clean slot +if [ "$MINE" = 1 ] && ! pack_gate; then echo "RESULT dn3_miner_refused $(stamp) the pack-id gate refused the place; node runs, miner off"; MINE=0; fi +if [ "$MINE" = 1 ]; then + cd $F/mine && rm -rf packs/$PACK && $MINER_BIN export-pack grpc://127.0.0.1:$RPC_PORT packs/$PACK > $OUT/dn3-export-pack.log 2>&1 + ( echo $BASHPID > $F/pids/miner-loop.pid; while :; do $MINER_BIN mine grpc://127.0.0.1:$RPC_PORT 1 100000000 "$LABEL" --exec-rpc http://127.0.0.1:$EVM_PORT --worker $WORKER_BIN --worker-args "--device 0 --pack packs/$PACK" --prepare-packs packs/$PACK-prepare --exit-on-seed-change ${ANNOUNCE:+--announce $ANNOUNCE} --evm-address "$WALLET" --payout-label "$LABEL" --status-secs 30 >> $OUT/dn3-miner.log 2>&1 & echo $! > $F/pids/miner.pid; wait $(cat $F/pids/miner.pid); sleep 5; done ) /dev/null 2>&1 & + echo "RESULT dn3_miner_started $(stamp) miner=$(sha256sum $MINER_BIN | cut -c1-16) announce=${ANNOUNCE:-none}" + # the engine's program id as the paired miner prints it ("class v4 program id <16 hex>", the node lane 17:0xZ): a785001687d8688a on sub-version 3, + # 1a4230699a6b9c60 is the 0.3.20 kit (known-failed); logged as the gate's id line, refused on the known-failed value + # the worker form prints no id (the node lane, main.rs 1471): a second one-thread CPU miner beside the worker for 20 s prints "class v4 program id <16 hex>" + timeout 60 $MINER_BIN mine grpc://127.0.0.1:$RPC_PORT 1 20 idread --engine igneum-pow --no-vote --stall-secs 0 > $OUT/dn3-idread.log 2>&1 > $OUT/dn3-prover-launch.log 2>&1 & + echo "RESULT dn3_prover_started $(stamp)" +fi diff --git a/tools/fleet/box-ember.sh b/tools/fleet/box-ember.sh index e278c4497..db5f1bfae 100755 --- a/tools/fleet/box-ember.sh +++ b/tools/fleet/box-ember.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +. /root/fleet/in/pidkill.sh # kills by pid only (founder 8 Oct 2026) # Ember Tune's two-knob ladder on a rented NVIDIA card (docs/plans/ember-tune.md, branch ember-tune): the miner runs # throughout; the power ladder 100, 90, 80, 70, 60, 50% of the default limit at the unlocked clock (clamped at the # card's reported minimum), then the clock ladder 90, 80, 70, 60% of the maximum graphics clock at the power the @@ -15,7 +16,7 @@ stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; } say() { echo "$(stamp) $*"; } q() { nvidia-smi --query-gpu="$1" --format=csv,noheader,nounits -i 0 2>/dev/null | head -1 | tr -d ' '; } NAME="$(q name)"; DRV="$(q driver_version)"; PDEF="$(q power.default_limit)"; PMIN="$(q power.min_limit)"; PMAX="$(q power.max_limit)"; CMAX="$(q clocks.max.graphics)" -pkill -x sp1-gpu-server 2>/dev/null; rm -f /tmp/sp1-cuda-*.sock +. /root/fleet/in/pidkill.sh; kill_sock_owner 'sp1-cuda-[0-9]*\.sock' echo "RESULT start $(stamp) card=$NAME driver=$DRV power_default_w=$PDEF min_w=$PMIN max_w=$PMAX clock_max_mhz=$CMAX" # can we set anything? nvidia-smi -i 0 -pl "$PDEF" >/dev/null 2>&1 && PL_OK=1 || PL_OK=0 @@ -27,7 +28,7 @@ rm -rf packs/devnet; $B/igneum-miner export-pack grpc://127.0.0.1:26610 packs/de nohup $B/igneum-miner mine grpc://127.0.0.1:26610 1 100000000 "$LABEL" --worker $B/igneum-worker-cuda --worker-args "--device 0 --pack packs/devnet" \ --prepare-packs packs/prepare --exit-on-seed-change --evm-address "$WALLET" --payout-label "$LABEL" --status-secs 10 > $OUT/ember-miner.log 2>&1 & MPID=$!; cd $F -cleanup() { nvidia-smi -i 0 -rgc >/dev/null 2>&1; [ "$PL_OK" = 1 ] && nvidia-smi -i 0 -pl "$PDEF" >/dev/null 2>&1; kill $MPID 2>/dev/null; pkill -f '[/]opt/igneum/pkg/bin/igneum-worker-cuda' 2>/dev/null; } +cleanup() { nvidia-smi -i 0 -rgc >/dev/null 2>&1; [ "$PL_OK" = 1 ] && nvidia-smi -i 0 -pl "$PDEF" >/dev/null 2>&1; kill_children $MPID; kill $MPID 2>/dev/null; } trap cleanup EXIT say "miner warming 90 s"; sleep 90 STEPS=$OUT/ember-steps.jsonl; : > $STEPS @@ -38,7 +39,7 @@ step() { #