Merge remote-tracking branch 'box/master' into ci-guest-format

# Conflicts:
#	docs/plans/igneum-2.0-test-registry.json
#	tools/ci/checks.txt
#	tools/ci/pre-push.sh
This commit is contained in:
igneum-labs 2026-10-08 21:02:04 +00:00
commit c97eae06c9
13 changed files with 405 additions and 104 deletions

View file

@ -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,8 +336,8 @@ 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
@ -367,7 +367,7 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover
### pc:install-update
- Command: `signed jobs and relay runs on PC 1 (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)`
- 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:
@ -392,14 +392,22 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover
- 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/<sha>.json (one file per sha, an artefacts list: fleet, windows, mac, hive) read by tools/ci/canary-check.sh <sha> [--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-06 Separate process advantage from specialisation: process-advantage separation is the adversary lanes' chip study
- ROT-03 Test miner-voted bring-forward governance: miner-voted bring-forward needs a vote harness on the fault network F4
@ -488,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
@ -544,4 +551,4 @@ Rule: a case maps to a cell only where the cell's tests visibly answer it; cover
## Count
171 automated cases: 81 mapped to a cell, 147 NOT RUN with a reason.
171 automated cases: 82 mapped to a cell, 146 NOT RUN with a reason.

View file

@ -52,7 +52,7 @@
"run_status": "NOT RUN",
"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:53:53.921Z",
"updated": "2026-10-08T21:02:04.084Z",
"evidence_record": {
"requirement_id": "GOV-01",
"decision": "NOT RUN",
@ -73,7 +73,7 @@
},
"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:53:53.921Z"
"at": "2026-10-08T21:02:04.084Z"
},
"in_progress_since": "2026-10-08T19:32:50.856Z",
"approvals": {
@ -103,7 +103,7 @@
},
"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:53:53.921Z"
"at": "2026-10-08T21:02:04.084Z"
}
}
},
@ -949,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": {
@ -1197,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": {
@ -1546,30 +1520,30 @@
"manual_page": 24,
"owner_lane": "adversary lane (a1a9876a88f5a72fc)",
"run_status": "FAIL",
"evidence_path": "docs/analysis/class-v6/connected-state.md; docs/analysis/class-v6; docs/analysis/class-v6/multi-family-adversary.md",
"run_id": "adversary-20261008-placed-8lane",
"updated": "2026-10-08T20:41:20.327Z",
"evidence_path": "docs/analysis/class-v6/connected-state.md; docs/analysis/class-v6/multi-family-adversary.md",
"run_id": "kills-20261008",
"updated": "2026-10-08T21:00:32.906Z",
"evidence_record": {
"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",
"decision": "FAIL",
"method": "GPU",
"cell": "experiment:connected-state",
"manifest_sha": "7cfa422a",
"run_id": "kills-20261008",
"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": {
"commit": "3a8874fef",
"commit": "7cfa422a",
"lockfile": "",
"binary": "",
"network_object": "",
"activation": "",
"profile_hashes": ""
},
"claim_impact": "",
"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:41:20.327Z"
"at": "2026-10-08T21:00:32.906Z"
},
"approvals": {
"scope_approved": null,
@ -1607,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": {
@ -1620,7 +1594,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"
},
"adversary:mf-placed": {
"requirement_id": "POW-03",
@ -1943,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",
@ -1953,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": {
@ -1966,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,
@ -2008,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": {
@ -2021,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"
}
}
},
@ -11993,7 +11967,7 @@
],
"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:53:53.921Z",
"updated": "2026-10-08T21:02:04.084Z",
"evidence_record": {
"requirement_id": "R2-F03-R01",
"decision": "PASS",
@ -12014,7 +11988,7 @@
},
"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:53:53.921Z"
"at": "2026-10-08T21:02:04.084Z"
},
"evidence_records": {
"harness:release-manifest": {
@ -12037,7 +12011,7 @@
},
"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:53:53.921Z"
"at": "2026-10-08T21:02:04.084Z"
}
},
"approvals": {
@ -15231,25 +15205,32 @@
"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",
"run_id": "canary-20261008-01",
"evidence_path": "tools/ci/canary-check.sh; packaging/ota/publish-manifest.sh; packaging/ota/publish-public.sh",
"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": {
@ -15269,6 +15250,28 @@
"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": {

View file

@ -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:<file or journal:unit>[@user@host]" read by tools/digest-read.sh on the hub)
--move-clock) MOVE_CLOCK="$2"; shift 2 ;; # "HH:MM UTC, <reason>": 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/<sha>.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() { # <release sha> [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 <release tip commit> 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

View file

@ -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: <sha>" 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/<sha>.json.
canary_gate() { # <sha> <what> [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

View file

@ -17,6 +17,7 @@
| 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/<sha>.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 |

View file

@ -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/<sha>.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"
}
]
}

View file

@ -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."
}

174
tools/ci/canary-check.sh Executable file
View file

@ -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/<sha>.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 <sha> [--artefact <kind>] 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/<sha>.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": "<release tip commit, full>",
"artefact": {"url": "https://dl.igneum.network/public/<file>", "sha256": "<64 hex>"},
"box": {"host": "build-N or <name>", "isa_line": "kit-isa: clean ... | cpu flags: no avx512"},
"datadir": {"path": "/srv/canary/<sha>/data", "empty_at_start": true, "read_back": "build-N:/srv/canary/<sha>/01-datadir.txt"},
"sync": {"genesis_height": 0, "tip_height": 0, "seconds": 0, "read_back": "build-N:/srv/canary/<sha>/02-sync.txt"},
"mining": {"minutes": 5, "refusals": 0, "accepted_blocks": 1, "read_back": "build-N:/srv/canary/<sha>/03-mining.txt"},
"shard": {"claimed": true, "proved": true, "paid_or_queued": "paid", "read_back": "build-N:/srv/canary/<sha>/04-shard.txt"},
"quit": {"bounded": true, "seconds": 0, "read_back": "build-N:/srv/canary/<sha>/05-quit.txt"},
"lines_read_back": [
{"line": "install", "evidence": "build-N:/srv/canary/<sha>/00-install.txt"},
{"line": "box", "evidence": "build-N:/srv/canary/<sha>/00-box.txt"},
{"line": "datadir", "evidence": "build-N:/srv/canary/<sha>/01-datadir.txt"},
{"line": "sync", "evidence": "build-N:/srv/canary/<sha>/02-sync.txt"},
{"line": "mining", "evidence": "build-N:/srv/canary/<sha>/03-mining.txt"},
{"line": "shard", "evidence": "build-N:/srv/canary/<sha>/04-shard.txt"},
{"line": "quit", "evidence": "build-N:/srv/canary/<sha>/05-quit.txt"},
{"line": "version", "evidence": "build-N:/srv/canary/<sha>/00-version.txt"}
],
"verdict": "PASS",
"recorded_at": "<UTC>",
"recorded_by": "<lane>"
}
EOF
}
check() { # <sha> [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/<sha>.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 <sha> [--artefact <kind>] | --form | --self-test" >&2; exit 2; }
check "$SHA_ARG" "$KIND"

View file

@ -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)

View file

@ -80,6 +80,7 @@ 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 guest input format moves with the pinned guests: the elf manifest's guest_input_format equals the source constant where the field exists, a format bump without a guest change is refused at the merge (self-test, then the tree)
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)

View file

@ -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"

View file

@ -179,6 +179,7 @@ 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 guest input format moves with the pinned guests: the elf manifest's guest_input_format equals the source constant where the field exists, a format bump without a guest change is refused at the merge (self-test, then the tree)" bash -c 'bash tools/ci/guest-format-check.sh --self-test >/dev/null && bash tools/ci/guest-format-check.sh --tree .'
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'

View file

@ -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"
@ -631,7 +631,7 @@
}
},
"pc:install-update": {
"command": "signed jobs and relay runs on PC 1 (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)",
"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"
@ -674,15 +674,28 @@
"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/<sha>.json (one file per sha, an artefacts list: fleet, windows, mac, hive) read by tools/ci/canary-check.sh <sha> [--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-06": "process-advantage separation is the adversary lanes' chip study",
"ROT-03": "miner-voted bring-forward needs a vote harness on the fault network F4",
@ -771,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",