From bd9a74b3e5d9653df85eb312fd07f5de0319a839 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:03:19 +0000 Subject: [PATCH 1/7] Counter ASIC 3.0 status: sub-version 2 committed (07a809a7, id a788661687db4bb3, the fixpoint rule, seven fingerprints, packs sha256) Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-3-status.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-3-status.md b/docs/plans/counter-asic-3-status.md index 6158ed191..53964c039 100644 --- a/docs/plans/counter-asic-3-status.md +++ b/docs/plans/counter-asic-3-status.md @@ -355,7 +355,19 @@ the project lead gave the go in advance for tonight: the shipper runs publish 1 | p25 | 1.28x | | | the 53 passing | 0.9915x to 1.16x | no predicted source | -Three residual classes, all a constant delivered through a writer the rule admits: (1) saturation or zero preserved through rotl, rotr, load or mad (p31, p6, p23, p26, p34); (2) zero from mulhi (p45) and zero preserved by rotates; (3) the iteration boundary, the rule's writer state starting fresh at instruction 0 so an or at 63 feeds a load at 1 (p11). Sub-version 2's dataflow-freshness rule closes all three IF the freshness is computed as a fixpoint over the loop (the state after instruction 63 feeds instruction 0 of the next iteration), with the dynamic (c') count on load sources as the backstop; sent to the hash lane. "No lossy-sourced load by construction" is not true of sub-version 1 and stays held. Chip consequence on sub-version 1 by the 1.4 arithmetic: the hot set is still one item at one site, under 1 percent of rate for a chip caching it, so the ship is safe on the rate side; the auditor's flag is what sub-version 2 removes. F9's hot-set harness cannot be the second re-gate (its metric counts the era's designed half and quarter windows as hot; its chain-path 8.3 percent on sub-version 1 is the window model, verified on its worst seed); F8's census is the single re-gate instrument, re-run on sub-version 2 within the hour of its commit. 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 | +Three residual classes, all a constant delivered through a writer the rule admits: (1) saturation or zero preserved through rotl, rotr, load or mad (p31, p6, p23, p26, p34); (2) zero from mulhi (p45) and zero preserved by rotates; (3) the iteration boundary, the rule's writer state starting fresh at instruction 0 so an or at 63 feeds a load at 1 (p11). Sub-version 2's dataflow-freshness rule closes all three IF the freshness is computed as a fixpoint over the loop (the state after instruction 63 feeds instruction 0 of the next iteration), with the dynamic (c') count on load sources as the backstop; sent to the hash lane. "No lossy-sourced load by construction" is not true of sub-version 1 and stays held. Chip consequence on sub-version 1 by the 1.4 arithmetic: the hot set is still one item at one site, under 1 percent of rate for a chip caching it, so the ship is safe on the rate side; the auditor's flag is what sub-version 2 removes. F9's hot-set harness cannot be the second re-gate (its metric counts the era's designed half and quarter windows as hot; its chain-path 8.3 percent on sub-version 1 is the window model, verified on its worst seed); F8's census is the single re-gate instrument, re-run on sub-version 2 within the hour of its commit. SUB-VERSION 2 COMMITTED: ca3-v4-amend 07a809a7, 13:01Z (origin and build), the string with the attack-pass lane. The loop fixpoint is in as rule (a') in accept.rs (the freshness run to its fixpoint over base then shadow block; every load's source fresh in the steady state, else the candidate rejected and the next attempt drawn; it closes the iteration boundary the draw's fallback cannot see); (c') counts zero and all-ones alike (v == 0 or v == MAX) per load site over the 16,384 evaluations, rejected at 164 or more; both and the draw rule keyed on the class v4 shape on every draw path, so v2 and v3 do not move; mulhi never fresh. Epoch-0 id a788661687db4bb3 (the devnet seed's attempt 0 rejected by the new rules, attempt 1 accepted); must-differ c120d7963abdcd96 and 1a4230699a6b9c60 pinned; object byte 7 in recheck.rs. The seven fingerprints (Metal = Apple OpenCL on the M5 Max, 13:01:17 to 13:01:49Z, the control 90f794dd556f7a3b unchanged): + +| Pack | Fingerprint | +|---|---| +| v4-devnet-epoch0 | e370fb2080b7dbb1 | +| era-0 | b7237555d31fc3cf | +| era-1 | b6b167fa15dfe2c9 | +| era-2 | 28bdf65eff33f2c4 | +| era-3 | e26d38c46f3f1b16 | +| era-4 | dd8fdf6ff4f59eed | +| era-5 | 8bf40f5cb858d835 | + +Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 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 project lead'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) | ### AP-F4-1, the weak-day MUL draw (the attack-pass lane, 7 October, morning): PASS against v4, a class v5 rule From f7136c60cd90ade86a333dfb89457d1a3102f4d1 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:11:22 +0000 Subject: [PATCH 2/7] Counter ASIC 3.0 status: 0.3.20's cut set as it stands, 0.3.21's clock and the byte-5 fallback, the CI queue note Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-3-status.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-3-status.md b/docs/plans/counter-asic-3-status.md index 53964c039..d59dc87d6 100644 --- a/docs/plans/counter-asic-3-status.md +++ b/docs/plans/counter-asic-3-status.md @@ -367,7 +367,7 @@ Three residual classes, all a constant delivered through a writer the rule admit | era-4 | dd8fdf6ff4f59eed | | era-5 | 8bf40f5cb858d835 | -Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 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 | +Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. 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 project lead'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) | ### AP-F4-1, the weak-day MUL draw (the attack-pass lane, 7 October, morning): PASS against v4, a class v5 rule From 851943655835082c45581498cd58289b32c6a07f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:13:02 +0000 Subject: [PATCH 3/7] Counter ASIC 3.0 status: main's word on the floor move, the CI lever and the rule's reading Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-3-status.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-3-status.md b/docs/plans/counter-asic-3-status.md index d59dc87d6..f5c64c815 100644 --- a/docs/plans/counter-asic-3-status.md +++ b/docs/plans/counter-asic-3-status.md @@ -367,7 +367,7 @@ Three residual classes, all a constant delivered through a writer the rule admit | era-4 | dd8fdf6ff4f59eed | | era-5 | 8bf40f5cb858d835 | -Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. 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 | +Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. MAIN'S WORD (14:2x UK): the floor move is the project lead's word already and ships with 0.3.20 at the cut; the CI lever is a second self-hosted runner on build-2 plus ubuntu-latest for docs-only pushes, ordered to the CI lane; the rule reads "own a red when the conclusion lands", never holding pushes; the census verdict about 14:00Z (15:00 UK) decides 0.3.21's byte. 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 project lead'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) | ### AP-F4-1, the weak-day MUL draw (the attack-pass lane, 7 October, morning): PASS against v4, a class v5 rule From 8f166648186b4fa162510534b2b1a8a541684ca0 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:14:03 +0000 Subject: [PATCH 4/7] Counter ASIC 3.0 status: sub-version 2's static census (0 lossy-sourced sites of 16,432), the attempt cost, G1 blocked on PC 2's intake Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-3-status.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-3-status.md b/docs/plans/counter-asic-3-status.md index f5c64c815..8a05017fa 100644 --- a/docs/plans/counter-asic-3-status.md +++ b/docs/plans/counter-asic-3-status.md @@ -367,7 +367,7 @@ Three residual classes, all a constant delivered through a writer the rule admit | era-4 | dd8fdf6ff4f59eed | | era-5 | 8bf40f5cb858d835 | -Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. MAIN'S WORD (14:2x UK): the floor move is the project lead's word already and ships with 0.3.20 at the cut; the CI lever is a second self-hosted runner on build-2 plus ubuntu-latest for docs-only pushes, ordered to the CI lane; the rule reads "own a red when the conclusion lands", never holding pushes; the census verdict about 14:00Z (15:00 UK) decides 0.3.21's byte. 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 | +Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. MAIN'S WORD (14:2x UK): the floor move is the project lead's word already and ships with 0.3.20 at the cut; the CI lever is a second self-hosted runner on build-2 plus ubuntu-latest for docs-only pushes, ordered to the CI lane; the rule reads "own a red when the conclusion lands", never holding pushes; the census verdict about 14:00Z (15:00 UK) decides 0.3.21's byte. SUB-VERSION 2's STATIC CENSUS (the hash lane, tools/ca3-v4-uniform on box 2, 13:12Z, 1,024 chain-shaped seeds plus F8's p1 to p3): 0 lossy-sourced load sites of 16,432 (14,329 injecting, 2,103 bijective); 0 programs with an or-, mul- or mulhi-sourced load; the no-era draw path gives the devnet epoch-0 seed the pack's own id a788661687db4bb3, so every draw path reads one stream. Cost of (a') and (c'): 1.99 attempts per seed on average against 0.05 before (p2's seed five), about 2 ms of generation per rejected attempt on one core; nothing a miner or node notices. Ledger entry 715f14be. Master 36e08c80 merged into ca3-v4-amend as f5244ad7 (igneum-pow untouched, re-export 0 differing files), pushed with the gate GREEN, its CI runs queued; the box-2 suite runs through the merged tools (the first attempt died on test initialisers missing the (c') field, fixed in the same push; library and packs unaffected). G1 BLOCKED: PC 2 has not picked up fetch-ca3-v4-sub2-20261007 and run-ca3-v4-sub2-g1-pc2-20261007 (published 13:04:02Z, signature OK); the intake shows nothing from 1ccfe586 since job-update-now-0319 at 10:34:21Z; the PC 2 lock releases when the job closes or in 30 minutes; the run is republished when the app reports. 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 project lead'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) | ### AP-F4-1, the weak-day MUL draw (the attack-pass lane, 7 October, morning): PASS against v4, a class v5 rule From 8c6111b513b84f9dd611fa6ab110eae4c002ecea Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:13:14 +0000 Subject: [PATCH 5/7] CI red watcher: its own workflow_run workflow (ci-red.yml) so the copy on master watches every branch's run whatever ci.yml the branch carries GitHub runs a workflow_run workflow from the default branch only, so a feature branch no longer waits for a merge of master before its reds are posted. record() reads the FAILED run from RED_WATCH_* (id, attempt, workflow, branch, sha, event, url, actor, title, author: the workflow_run payload) and asks the jobs API for that run, not the watcher's own; the inline GITHUB_* shape stays for a branch with the old `red` job (one line per run id either way). The inline job leaves ci.yml. Self-test covers the workflow_run shape and the full line. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci-red.yml | 42 ++++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 36 ++++++------------------------- tools/ci/red-watch.mjs | 41 ++++++++++++++++++++++++++--------- 3 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/ci-red.yml diff --git a/.github/workflows/ci-red.yml b/.github/workflows/ci-red.yml new file mode 100644 index 000000000..dddb758b0 --- /dev/null +++ b/.github/workflows/ci-red.yml @@ -0,0 +1,42 @@ +# The red watcher as its own workflow, on workflow_run, so the copy on master watches EVERY branch's ci run whatever +# ci.yml that branch carries: GitHub runs a workflow_run workflow from the default branch only, and the branch's own +# ci.yml never enters it (7 October 2026: the inline `red` job of ci.yml was conditioned on master and release-*, and +# a feature branch would have waited for a merge of master before its reds were posted at all). +# +# One line per failed run (tools/ci/red-watch.mjs record, idempotent per run attempt) to /srv/ci-red/red.jsonl on the +# box; the box's igneum-ci-red.timer posts each new line once to the hidden updates channel, naming the branch, the +# commit, the red check and the pushing author. Runs on the box's own runner (not a GitHub-hosted machine: the billing +# block of 6 October 2026, 18:37Z to 20:10Z, failed every hosted job at start and nobody was told). Never blocks a +# release: it reads the run, writes one line, and ends. +name: ci-red +on: + workflow_run: + workflows: [ci] + types: [completed] +jobs: + red: + name: red watcher (every branch; one line per failed run, with the branch, commit, red check and pushing author, to the updates channel and the box file) + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + runs-on: [self-hosted, linux, x64, igneum-build-1] + timeout-minutes: 5 + permissions: + actions: read # the failed run's jobs API (the first real red run, 21:19Z on 6 October: the default token answered 403 and the line carried no step) + contents: read + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: tools/ci + - name: record the failed run (one line, the branch, the commit, the failed jobs and their first failed step from the run's own API, the pushing author) + env: + GITHUB_TOKEN: ${{ github.token }} + RED_WATCH_RUN_ID: ${{ github.event.workflow_run.id }} + RED_WATCH_ATTEMPT: ${{ github.event.workflow_run.run_attempt }} + RED_WATCH_WORKFLOW: ${{ github.event.workflow_run.name }} + RED_WATCH_BRANCH: ${{ github.event.workflow_run.head_branch }} + RED_WATCH_SHA: ${{ github.event.workflow_run.head_sha }} + RED_WATCH_EVENT: ${{ github.event.workflow_run.event }} + RED_WATCH_URL: ${{ github.event.workflow_run.html_url }} + RED_WATCH_ACTOR: ${{ github.event.workflow_run.actor.login }} + RED_WATCH_TITLE: ${{ github.event.workflow_run.head_commit.message }} + RED_WATCH_AUTHOR: ${{ github.event.workflow_run.head_commit.author.name }} + run: node tools/ci/red-watch.mjs record --file /srv/ci-red/red.jsonl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47d816738..5311c6dc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,11 +10,12 @@ # # Where it runs: `pow` and `sims` go to the box's runner (igneum-build-1, rustc pinned, sccache read-only, 48 jobs) # when the repository variable IGNEUM_CI_RUNNER is `box`, else to ubuntu-latest (docs/plans/ci-self-hosted.md; GitHub -# has no fallback in runs-on, the variable is the switch). The `site` job stays on GitHub's machines. The `red` job -# runs on the box after any failed run on ANY branch and records the failure for the watcher -# (tools/ci/red-watch.mjs; infra/build-server/ci-red): one line per run, naming the branch, the commit, the red check -# and the pushing author, to the hidden updates channel and to /srv/ci-red/red.jsonl, so nobody opens the Actions page -# to learn a branch is red (master and release-* only until 7 October 2026, when eight red runs on ca3-v4-node went unseen). +# has no fallback in runs-on, the variable is the switch). The `site` job stays on GitHub's machines. The red watcher +# is its own workflow, .github/workflows/ci-red.yml (workflow_run, so the copy on master watches every branch's run +# whatever ci.yml that branch carries): one line per failed run, naming the branch, the commit, the red check and the +# pushing author, to the hidden updates channel and to /srv/ci-red/red.jsonl (tools/ci/red-watch.mjs; +# infra/build-server/ci-red), so nobody opens the Actions page to learn a branch is red (the inline `red` job here +# watched master and release-* only until 7 October 2026, when eight red runs on ca3-v4-node went unseen). # # What does not run, on purpose: the node fork (vendor/igneum-node*, a rusty-kaspa fork of about 500 crates with # rocksdb, blst and the execution layer) is gitignored here and too big for the free runners today (a cold build is @@ -76,28 +77,3 @@ jobs: - name: public stats API answers with the documented fields (the live site; master only, the endpoints exist there after the merge) if: github.ref == 'refs/heads/master' run: node tools/ci/public-api-check.mjs https://igneum.network - - red: - # Runs when a run on any branch has a failed job, on the box's own runner (not a GitHub-hosted machine: - # the billing block of 6 October 2026, 18:37Z to 20:10Z, failed every hosted job at start and nobody was told). - # tools/ci/red-watch.mjs record appends ONE line for this run to /srv/ci-red/red.jsonl (idempotent per run attempt); - # the box's igneum-ci-red.timer posts each new line once to the hidden updates channel. Never blocks a release: - # it reads the run, writes one line, and ends. - name: red watcher (every branch; one line per failed run, with the branch, commit, red check and pushing author, to the updates channel and the box file) - needs: [pow, sims, site] - if: ${{ failure() }} - runs-on: [self-hosted, linux, x64, igneum-build-1] - timeout-minutes: 5 - permissions: - actions: read # the run's jobs API (the first real red run, 21:19Z: the default token answered 403 and the line carried no step) - contents: read - steps: - - uses: actions/checkout@v4 - with: - sparse-checkout: tools/ci - - name: record this run (one line, the branch, the commit, the failed jobs and their first failed step from the run's own API, the pushing author) - env: - GITHUB_TOKEN: ${{ github.token }} - RED_WATCH_TITLE: ${{ github.event.head_commit.message }} - RED_WATCH_AUTHOR: ${{ github.event.head_commit.author.name }} - run: node tools/ci/red-watch.mjs record --file /srv/ci-red/red.jsonl diff --git a/tools/ci/red-watch.mjs b/tools/ci/red-watch.mjs index 3d401e21f..4f585edd7 100755 --- a/tools/ci/red-watch.mjs +++ b/tools/ci/red-watch.mjs @@ -4,10 +4,12 @@ // opens the Actions page to learn a branch is red. // Node 22, standard library only. // -// node tools/ci/red-watch.mjs record --file in the workflow's `red` job (runs on igneum-build-1 after a -// failed run): reads the run from the GitHub environment and +// node tools/ci/red-watch.mjs record --file in .github/workflows/ci-red.yml (a workflow_run job on +// igneum-build-1 after a failed ci run on any branch): reads the +// FAILED run from RED_WATCH_* (the workflow_run payload; the +// GITHUB_* variables there describe the watcher's own run) and // the failed jobs and steps from the API with the job's own -// token, appends ONE JSON line for this run id (idempotent) +// token, appends ONE JSON line for that run id (idempotent) // node tools/ci/red-watch.mjs post --file [--live] on the box, every minute as `build` (igneum-ci-red.timer): // every recorded run not yet posted goes as one line to the // hidden updates channel (DISCORD_WEBHOOK_UPDATES in the @@ -63,15 +65,22 @@ export function readLines(file) { return fs.readFileSync(file, 'utf8').split('\n').filter(Boolean).map((l) => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean); } +// The run being recorded: the FAILED run from RED_WATCH_* when the watcher runs as a workflow_run job (ci-red.yml), else +// the job's own run from GITHUB_* (the inline shape, kept for a branch whose ci.yml still carries the old `red` job). +export const watchedRunId = (env = process.env) => env.RED_WATCH_RUN_ID || env.GITHUB_RUN_ID; export function runFromEnv(env = process.env) { - const need = ['GITHUB_RUN_ID', 'GITHUB_REPOSITORY', 'GITHUB_REF_NAME', 'GITHUB_SHA', 'GITHUB_WORKFLOW']; + const need = ['GITHUB_REPOSITORY', 'GITHUB_RUN_ID']; for (const k of need) if (!env[k]) throw new Error(`record: ${k} is not set (this command runs inside a GitHub Actions job)`); + const pick = (own, fallback) => env[own] || env[fallback] || ''; const server = env.GITHUB_SERVER_URL || 'https://github.com'; + const id = String(watchedRunId(env)); + const sha = pick('RED_WATCH_SHA', 'GITHUB_SHA'); + if (!sha) throw new Error('record: neither RED_WATCH_SHA nor GITHUB_SHA is set'); return { - run_id: String(env.GITHUB_RUN_ID), attempt: Number(env.GITHUB_RUN_ATTEMPT || 1), workflow: env.GITHUB_WORKFLOW, - branch: env.GITHUB_REF_NAME, sha: env.GITHUB_SHA.slice(0, 7), event: env.GITHUB_EVENT_NAME || '', - actor: env.GITHUB_ACTOR || '', author: env.RED_WATCH_AUTHOR || '', // who pushed (the GitHub login), who the head commit names - url: `${server}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`, at: new Date().toISOString(), + run_id: id, attempt: Number(pick('RED_WATCH_ATTEMPT', 'GITHUB_RUN_ATTEMPT') || 1), workflow: pick('RED_WATCH_WORKFLOW', 'GITHUB_WORKFLOW'), + branch: pick('RED_WATCH_BRANCH', 'GITHUB_REF_NAME'), sha: sha.slice(0, 7), event: pick('RED_WATCH_EVENT', 'GITHUB_EVENT_NAME'), + actor: pick('RED_WATCH_ACTOR', 'GITHUB_ACTOR'), author: env.RED_WATCH_AUTHOR || '', // who pushed (the GitHub login), who the head commit names + url: env.RED_WATCH_URL || `${server}/${env.GITHUB_REPOSITORY}/actions/runs/${id}`, at: new Date().toISOString(), }; } @@ -79,7 +88,7 @@ export function runFromEnv(env = process.env) { // (the caller) is skipped by name. Any API trouble gives an empty list and a note, never a thrown error: the line is // the thing that must land. export async function failedJobs(env = process.env, fetchImpl = fetch) { - const token = env.GITHUB_TOKEN; const repo = env.GITHUB_REPOSITORY; const id = env.GITHUB_RUN_ID; + const token = env.GITHUB_TOKEN; const repo = env.GITHUB_REPOSITORY; const id = watchedRunId(env); const api = env.GITHUB_API_URL || 'https://api.github.com'; if (!token) return { failed: [], note: 'no GITHUB_TOKEN; failed steps not read' }; try { @@ -238,6 +247,18 @@ async function selfTest() { await record(fileFeature, envFeature, fakeFetch); const textFeature = formatLine(readLines(fileFeature)[0]); if (!/^CI red: ci on ca3-v4-node @0f0abc6 "Merge box-work[^"]*": pushed by igneum-labs; site build at/.test(textFeature)) fails.push(`format on a feature branch: ${textFeature}`); + // the workflow_run shape (ci-red.yml): GITHUB_* describe the watcher's own run, RED_WATCH_* the failed one; the line is the failed run's + const envRun = { GITHUB_RUN_ID: '999', GITHUB_RUN_ATTEMPT: '1', GITHUB_REPOSITORY: 'igneum-network/igneum', GITHUB_REF_NAME: 'master', GITHUB_SHA: 'ffffffffffff', GITHUB_WORKFLOW: 'ci-red', GITHUB_ACTOR: 'igneum-labs', GITHUB_TOKEN: 'x', + RED_WATCH_RUN_ID: '37620364667', RED_WATCH_ATTEMPT: '2', RED_WATCH_WORKFLOW: 'ci', RED_WATCH_BRANCH: 'ca3-v4-node', RED_WATCH_SHA: '26a4b0f1deadbeef', RED_WATCH_EVENT: 'push', + RED_WATCH_URL: 'https://github.com/igneum-network/igneum/actions/runs/37620364667', RED_WATCH_ACTOR: 'igneum-labs', RED_WATCH_TITLE: 'Counter ASIC 3.0 node plan 6.7', RED_WATCH_AUTHOR: 'igneum-labs' }; + const fileRun = path.join(dir, 'workflow-run.jsonl'); const asked = []; + const askingFetch = async (url) => { asked.push(url); return { ok: true, status: 200, json: async () => jobs }; }; + await record(fileRun, envRun, askingFetch); + const lr = readLines(fileRun)[0]; + if (lr.run_id !== '37620364667' || lr.attempt !== 2 || lr.branch !== 'ca3-v4-node' || lr.sha !== '26a4b0f' || lr.workflow !== 'ci') fails.push(`workflow_run shape: recorded ${JSON.stringify({ run_id: lr.run_id, attempt: lr.attempt, branch: lr.branch, sha: lr.sha, workflow: lr.workflow })}, expected the failed run, not the watcher's`); + if (!asked[0] || !asked[0].includes('/actions/runs/37620364667/jobs')) fails.push(`workflow_run shape: the jobs API was asked for ${asked[0]}, not the failed run`); + const textRun = formatLine(lr); + if (!/^CI red: ci on ca3-v4-node @26a4b0f "Counter ASIC 3.0 node plan 6.7": pushed by igneum-labs \(commit by igneum-labs\); site build at "identity grep of the public export list"; simulators at "\(job never started: runner or billing\)" https:\/\/github.com\/igneum-network\/igneum\/actions\/runs\/37620364667$/.test(textRun)) fails.push(`workflow_run line: ${textRun}`); // post, dry run: prints, sends nothing, marks nothing let printed = []; const log = (s) => printed.push(s); const sends = []; const hookFetch = async (url, init) => { sends.push({ url, body: JSON.parse(init.body) }); return { ok: true, status: 204 }; }; @@ -286,7 +307,7 @@ async function selfTest() { if (!d3.sent) fails.push('digest: not sent the next day'); fs.rmSync(dir, { recursive: true, force: true }); if (fails.length) { for (const f of fails) console.error(`self-test failed: ${f}`); process.exit(1); } - console.log('self-test passed: one line per run however often record runs, on any branch, naming the pushing author; the dry run sends nothing; a missing key is named, never a URL; one live send per run; a webhook error keeps the run pending; a box row is counted, never posted alone; the digest goes once per London day, at or after 09:00'); + console.log('self-test passed: one line per run however often record runs, on any branch, naming the pushing author, the failed run and not the watcher\'s own under workflow_run; the dry run sends nothing; a missing key is named, never a URL; one live send per run; a webhook error keeps the run pending; a box row is counted, never posted alone; the digest goes once per London day, at or after 09:00'); } const cmd = args[0]; From 2c4bda5b63e343c32b95043276fe70a1cfca27e0 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:15:44 +0000 Subject: [PATCH 6/7] Counter ASIC 3.0 status: PC 2 silent since the 0.3.19 update-now; G1 and the Windows kept-datadir gate wait on a hand restart Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-3-status.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-3-status.md b/docs/plans/counter-asic-3-status.md index 8a05017fa..7f7ce07fb 100644 --- a/docs/plans/counter-asic-3-status.md +++ b/docs/plans/counter-asic-3-status.md @@ -367,7 +367,7 @@ Three residual classes, all a constant delivered through a writer the rule admit | era-4 | dd8fdf6ff4f59eed | | era-5 | 8bf40f5cb858d835 | -Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. MAIN'S WORD (14:2x UK): the floor move is the project lead's word already and ships with 0.3.20 at the cut; the CI lever is a second self-hosted runner on build-2 plus ubuntu-latest for docs-only pushes, ordered to the CI lane; the rule reads "own a red when the conclusion lands", never holding pushes; the census verdict about 14:00Z (15:00 UK) decides 0.3.21's byte. SUB-VERSION 2's STATIC CENSUS (the hash lane, tools/ca3-v4-uniform on box 2, 13:12Z, 1,024 chain-shaped seeds plus F8's p1 to p3): 0 lossy-sourced load sites of 16,432 (14,329 injecting, 2,103 bijective); 0 programs with an or-, mul- or mulhi-sourced load; the no-era draw path gives the devnet epoch-0 seed the pack's own id a788661687db4bb3, so every draw path reads one stream. Cost of (a') and (c'): 1.99 attempts per seed on average against 0.05 before (p2's seed five), about 2 ms of generation per rejected attempt on one core; nothing a miner or node notices. Ledger entry 715f14be. Master 36e08c80 merged into ca3-v4-amend as f5244ad7 (igneum-pow untouched, re-export 0 differing files), pushed with the gate GREEN, its CI runs queued; the box-2 suite runs through the merged tools (the first attempt died on test initialisers missing the (c') field, fixed in the same push; library and packs unaffected). G1 BLOCKED: PC 2 has not picked up fetch-ca3-v4-sub2-20261007 and run-ca3-v4-sub2-g1-pc2-20261007 (published 13:04:02Z, signature OK); the intake shows nothing from 1ccfe586 since job-update-now-0319 at 10:34:21Z; the PC 2 lock releases when the job closes or in 30 minutes; the run is republished when the app reports. 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 | +Packs zip (eight packs, packs-ca3-v4-sub2) sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b. The suite re-runs through master's build-remote on box 2 (the worktree's own script predates --box; the first run died on the flag), line to follow; G1 on PC 2 under --cards-off after it. The sub-version-2 pairing waits on the node lane's re-pin to a788661687db4bb3 and byte 7. For the record, sub-version 1's pairing: igneum-pow 8c728ca3 against b7cc37e7 (8097d600's assert_ne in) 17 passed, 0 failed, rc 0, 12:21Z. 0.3.20's CUT SET AS IT STANDS (the shipper, 14:1x UK): pin c4459193, igneum-pow 8c728ca3 at object byte 5 (sub-version 1), the floor-moved file publishing with it (the project lead's word; the floor from the live DAA at the publish plus 604,800, the digest read on c4459193), publish about 15:15Z (16:15 BST) on CASES END, the sweep from then with PC 1 first. 0.3.21's clock tonight: the node lane stages release-0.3.21-node at the shipper's sweep-end word (about 15:45Z, 16:45 BST) with the sub-version-2 re-pin (07a809a7, byte 7, id a788661687db4bb3) as its own commit, held pending the F8 census on 07a809a7; the census's clock about 14:00Z (15:00 BST) by the attack-pass lane's within-the-hour line from 13:01Z; the pass line every one of the 64 seeds under 1.2x of the window model on the chain path. If the census fails or slips past 19:00Z (20:00 BST), main's standing ruling applies (nothing on sub-version 2 is proposed until the census is green): 0.3.21's node ships byte 5 again with the re-pin dropped and the rest of its line kept. CI NOTE (13:1x UTC): master's ci runs since 12dc5c97 (nineteen of mine) sit queued behind one self-hosted runner (igneum-build-1, busy; 31 queued across branches, one in progress); the last completed master runs (439a233f to 5f990a09) are success; no red exists, the conclusions are unread until the queue drains. MAIN'S WORD (14:2x UK): the floor move is the project lead's word already and ships with 0.3.20 at the cut; the CI lever is a second self-hosted runner on build-2 plus ubuntu-latest for docs-only pushes, ordered to the CI lane; the rule reads "own a red when the conclusion lands", never holding pushes; the census verdict about 14:00Z (15:00 UK) decides 0.3.21's byte. SUB-VERSION 2's STATIC CENSUS (the hash lane, tools/ca3-v4-uniform on box 2, 13:12Z, 1,024 chain-shaped seeds plus F8's p1 to p3): 0 lossy-sourced load sites of 16,432 (14,329 injecting, 2,103 bijective); 0 programs with an or-, mul- or mulhi-sourced load; the no-era draw path gives the devnet epoch-0 seed the pack's own id a788661687db4bb3, so every draw path reads one stream. Cost of (a') and (c'): 1.99 attempts per seed on average against 0.05 before (p2's seed five), about 2 ms of generation per rejected attempt on one core; nothing a miner or node notices. Ledger entry 715f14be. Master 36e08c80 merged into ca3-v4-amend as f5244ad7 (igneum-pow untouched, re-export 0 differing files), pushed with the gate GREEN, its CI runs queued; the box-2 suite runs through the merged tools (the first attempt died on test initialisers missing the (c') field, fixed in the same push; library and packs unaffected). G1 BLOCKED: PC 2 has not picked up fetch-ca3-v4-sub2-20261007 and run-ca3-v4-sub2-g1-pc2-20261007 (published 13:04:02Z, signature OK); the intake shows nothing from 1ccfe586 since job-update-now-0319 at 10:34:21Z; the PC 2 lock releases when the job closes or in 30 minutes; the run is republished when the app reports. PC 2 READS SILENT on the console (the shipper, 14:4x UK): last seen 2 h ago, app 0.3.19 on node 5899f603, its last line the 0.3.19 update-now at 10:34:21Z; the app went down or stopped polling on that update (the UI lane's update-now; PC 1 took the same update and reports). The build-server lane's PC 2 kept-datadir job (run-20261007-125433, published 12:54Z) is unpicked for the same reason, and it is the Windows kept-datadir gate for 0.3.20's PC 1 step. The sweep cannot bring PC 2 back (the app's poller applies updates; a silent app does not poll); a Windows restart of the app is a hand action, the project lead's or by main's word; the shipper has asked main. The hash lane's G1 and the Windows kept-datadir line wait on that answer; the PC 2 lock stays. 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 project lead'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) | ### AP-F4-1, the weak-day MUL draw (the attack-pass lane, 7 October, morning): PASS against v4, a class v5 rule From f97cfcc30a0f195319a1e1b8c4ee8b07f9e4e29b Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:16:39 +0000 Subject: [PATCH 7/7] Build boxes: the box probe carries its own ssh options (bash 3.2 under set -u refused the unset BS_SSH_OPTS array before bs_host built it; every unpinned route on the Mac died, the pool lane 7 Oct 2026); the router check runs the ssh path under /bin/bash Co-Authored-By: Claude Fable 5.1 --- infra/build-server/lib.sh | 4 +++- tools/ci/route-spill-check.sh | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/infra/build-server/lib.sh b/infra/build-server/lib.sh index de1e7d25c..223d89568 100755 --- a/infra/build-server/lib.sh +++ b/infra/build-server/lib.sh @@ -45,7 +45,9 @@ bs_box_state() { # -> "free= slots= load1=" | "absent" | "down" v=$(eval "printf '%s' \"\${BS_ROUTE_STATE_$b:-}\""); if [ -n "$v" ]; then printf '%s' "$v"; return 0; fi f=$(bs_box_file "$b"); [ -s "$f" ] || { printf 'absent'; return 0; } h="$(head -1 "$f" | tr -d '[:space:]')" - ssh "${BS_SSH_OPTS[@]}" -o ConnectTimeout=8 "$h" 'd=/srv/builds/_locks; n=$(cat $d/slots 2>/dev/null || echo 1); free=0; k=0; while [ $k -lt $n ]; do exec 9>>$d/build-$k; if flock -n 9; then free=$((free+1)); fi; exec 9>&-; k=$((k+1)); done; printf "free=%s slots=%s load1=%s" $free $n "$(cut -d" " -f1 /proc/loadavg)"' 2>/dev/null || printf 'down' + # the probe runs BEFORE bs_host builds BS_SSH_OPTS (the pool lane, 7 Oct 2026 15:1x UK: bash 3.2 under set -u refuses an + # unset array), so it carries its own options: the ops key, batch mode, a short connect timeout, no control socket + ssh -i "$BS_KEY" -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=8 "$h" 'd=/srv/builds/_locks; n=$(cat $d/slots 2>/dev/null || echo 1); free=0; k=0; while [ $k -lt $n ]; do exec 9>>$d/build-$k; if flock -n 9; then free=$((free+1)); fi; exec 9>&-; k=$((k+1)); done; printf "free=%s slots=%s load1=%s" $free $n "$(cut -d" " -f1 /proc/loadavg)"' 2>/dev/null || printf 'down' } bs_state_ok() { # -> 0 when the box can take a job now (a free slot, load1 at or under the line) local st="$1" free load diff --git a/tools/ci/route-spill-check.sh b/tools/ci/route-spill-check.sh index 4c8ef4f6a..0da8b3d03 100755 --- a/tools/ci/route-spill-check.sh +++ b/tools/ci/route-spill-check.sh @@ -33,5 +33,12 @@ BS_ROUTE_STATE_1="down" BS_ROUTE_STATE_2="free=3 slots=3 load BS_ROUTE_STATE_1="free=1 slots=2 load1=5" BS_ROUTE_STATE_2="free=3 slots=3 load1=5" expect "a proving class with no box 3 prefers box 1" prove 1 0 BS_ROUTE_STATE_1="free=0 slots=2 load1=5" BS_ROUTE_STATE_2="free=3 slots=3 load1=5" expect "a proving class spills to box 2 when box 1 is held" prove 2 1 BS_ROUTE_STATE_1="free=1 slots=2 load1=64" BS_ROUTE_STATE_2="free=3 slots=3 load1=5" expect "load exactly 64 is under the line" build 1 0 +# the ssh path itself under the hook's shell (/bin/bash is 3.2 on the Mac; the pool lane found `BS_SSH_OPTS[@]: unbound variable` +# at the first unpinned route, 7 Oct 2026): a host file pointing at a port nothing answers on must read "down" in a few seconds, +# not die on an unset array +printf 'build@127.0.0.1\n' > "$t/hosts" +out=$(IGNEUM_BUILD_KEY="$t/no-such-key" /bin/bash -c '. infra/build-server/lib.sh; bs_box_state 1' 2>&1 || true) +if [ "$out" = down ]; then echo "route-spill: the ssh probe under /bin/bash $(/bin/bash -c 'echo $BASH_VERSION') reads an unreachable box as down" +else echo "route-spill: the ssh probe under /bin/bash failed: '$out' (expected 'down')" >&2; fail=1; fi [ "$fail" = 0 ] && echo "route-spill: the class is a preference; a full or overloaded box hands the job to the other one" exit $fail