diff --git a/.github/workflows/mutants.yml b/.github/workflows/mutants.yml index 72a11131e..6dc0e50c9 100644 --- a/.github/workflows/mutants.yml +++ b/.github/workflows/mutants.yml @@ -1,3 +1,7 @@ +# Weekly cargo-mutants + PR in-diff advisory. Not a required PR check. +# Must pass --workspace: default-members is rbitcoin-node only. +# Owner: TESTING.md (Mutation testing). Snapshot lists: docs/mutants/. + name: mutants on: schedule: @@ -22,13 +26,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false submodules: false fetch-depth: 0 - - uses: dtolnay/rust-toolchain@a5f673d0ba8626c3977bb416a1612774bc82181b - - uses: taiki-e/install-action@9534c84618278caac52cb373bb164ed464dbd8af + - uses: dtolnay/rust-toolchain@a5f673d0ba8626c3977bb416a1612774bc82181b # rustc 1.95.0 + - uses: taiki-e/install-action@9534c84618278caac52cb373bb164ed464dbd8af # v2.87.11 with: tool: cargo-mutants@27.1.0 - run: git diff origin/${{ github.base_ref }}.. --unified=0 > git.diff @@ -44,16 +48,16 @@ jobs: matrix: shard: [0, 1, 2, 3, 4, 5, 6, 7] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false submodules: false - - uses: dtolnay/rust-toolchain@a5f673d0ba8626c3977bb416a1612774bc82181b - - uses: taiki-e/install-action@9534c84618278caac52cb373bb164ed464dbd8af + - uses: dtolnay/rust-toolchain@a5f673d0ba8626c3977bb416a1612774bc82181b # rustc 1.95.0 + - uses: taiki-e/install-action@9534c84618278caac52cb373bb164ed464dbd8af # v2.87.11 with: tool: cargo-mutants@27.1.0 - run: cargo mutants --workspace --shard ${{ matrix.shard }}/8 -j 2 - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: name: mutants-${{ matrix.shard }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c7a31f39..27ec43b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,11 @@ before 1.0). ### Changed +- **Weekly cargo-mutants:** `mutants.yml` — PR `--workspace --in-diff` + advisory (`continue-on-error`); Sunday 8-shard `--workspace` sweep. + Must use `--workspace` (`default-members` is node). Snapshot lists: + [`docs/mutants/`](docs/mutants/). How to run: [`TESTING.md`](TESTING.md). + - **Electrum/Esplora no longer require `--sh-index` to bind.** Address and scripthash methods return `scripthash index disabled` (Electrum JSON-RPC error; Esplora HTTP 503). Txid/outpoint/block/fees work. Channel watches diff --git a/TESTING.md b/TESTING.md index 34711d7ed..80f5c6c80 100644 --- a/TESTING.md +++ b/TESTING.md @@ -234,7 +234,7 @@ to us. `regtest_rpc.rs` / `regtest_pad.rs` stay in the denominator. shipped path that drives the real entry point. 4. Re-run `./scripts/coverage.sh` until the ratio is **≥ 92%**. -## Structural lints, CRAP, Miri +## Structural lints, CRAP, Miri, mutants These do **not** measure operator RSS ([`docs/ibd-memory.md`](./docs/ibd-memory.md) owns caps). They catch the *shapes* of unbounded heap / leaked tasks, untested @@ -250,9 +250,10 @@ matches scalar in default tests). Owner: [`docs/quality.md`](./docs/quality.md). | **cargo-crap** | After LCOV, `./scripts/coverage.sh` calls `./scripts/coverage-crap.sh` (skip if `cargo-crap` missing). `--fail-above --threshold 30`; `.cargo-crap.toml` allowlists today's production CRAP>30 functions (remove a name when it scores ≤30). Dry-run: `CRAP_DRY_RUN=1 ./scripts/coverage-crap.sh`. Self-test: `./scripts/coverage-crap.test.sh` | Rides required `coverage`. No `--fail-regression` (llvm-cov coverage % jitters per function) | | **coverage ignore / badge** | `./scripts/coverage.test.sh` (filename ignore, Tier A IBD not skipped, 92% floor, Shields JSON). Publish dry-run: `BADGE_DRY_RUN=1 ./scripts/publish-coverage-badge.sh` | `test` job self-test; `coverage` job writes `coverage/badge.json` and, on green `master`, pushes `badges/coverage.json` | | **Miri** | `./scripts/miri.sh` → `cargo +nightly miri test -p rbitcoin-primitives`. Dry-run: `MIRI_DRY_RUN=1 ./scripts/miri.sh`. Self-test: `./scripts/miri.test.sh` | Nightly `miri.yml` (not required). Never `--workspace` | +| **cargo-mutants** | Must pass `--workspace` (`Cargo.toml` `default-members` is `rbitcoin-node` only; without `--workspace` the list is ~7 mutants). PR advisory: `git diff origin/.. --unified=0 > git.diff` then `cargo mutants --workspace --in-diff git.diff -j 2`. Weekly: `cargo mutants --workspace --shard N/8 -j 2`. Snapshot of missed/timeouts: [`docs/mutants/`](docs/mutants/). | Weekly `mutants.yml` Sunday 03:00 UTC, 8 shards, 360min cap (not required). `workflow_dispatch`. PR job `mutants-pr` is `continue-on-error` (not a merge gate). First Sunday wall-clock is still a measurement. | Artifact silos above are unchanged: ast-grep / Miri dry-run / crap dry-run do -not write `target/`. +not write `target/`. `mutants.out/` is gitignored. ### Mature-chain fixtures diff --git a/crates/rbitcoin-primitives/src/script_sigops.rs b/crates/rbitcoin-primitives/src/script_sigops.rs index c549c589d..c0e3aea56 100644 --- a/crates/rbitcoin-primitives/src/script_sigops.rs +++ b/crates/rbitcoin-primitives/src/script_sigops.rs @@ -58,22 +58,6 @@ pub fn script_sigop_count(script: &[u8], accurate: bool) -> u64 { #[cfg(test)] mod tests { - - #[test] - fn sigop_count_covers_pushdata_and_truncation() { - // 21 -= : [4c,2,ac] original 0, mutant i-=1 => 1 - assert_eq!(script_sigop_count(&[0x4c, 2, 0xac], true), 0); - // 21 *= : [4c,1,ac] original 0, mutant i*=1 => 1 - assert_eq!(script_sigop_count(&[0x4c, 1, 0xac], true), 0); - // 28 *= : [4d,1,0,ac] original 0, mutant i*=2 => 1 - assert_eq!(script_sigop_count(&[0x4d, 1, 0, 0xac], true), 0); - // 41 > vs >= vs == : truncated must be 0 - assert_eq!(script_sigop_count(&[0x02, 0x00], true), 0); - assert_eq!(script_sigop_count(&[0x4c, 2, 0xac, 0xac], true), 0); - // extra long killer for 21 -= if guard changes - assert_eq!(script_sigop_count(&[0x4c, 2, 0xac, 0xac, 0xac], true), 1); // PUSHDATA1 2 [ac,ac] + ac => 1 - } - use super::*; #[test] @@ -113,6 +97,12 @@ mod tests { script_sigop_count(&[0x4e, 0x01, 0x00, 0x00, 0x00, 0xcd, 0xac], false), 1 ); + assert_eq!(script_sigop_count(&[0x4c, 2, 0xac], true), 0); + assert_eq!(script_sigop_count(&[0x4c, 1, 0xac], true), 0); + assert_eq!(script_sigop_count(&[0x4d, 1, 0, 0xac], true), 0); + assert_eq!(script_sigop_count(&[0x02, 0x00], true), 0); + assert_eq!(script_sigop_count(&[0x4c, 2, 0xac, 0xac], true), 0); + assert_eq!(script_sigop_count(&[0x4c, 2, 0xac, 0xac, 0xac], true), 1); } #[cfg(miri)] diff --git a/docs/ORIENT.md b/docs/ORIENT.md index 7173572f4..6e3e79a88 100644 --- a/docs/ORIENT.md +++ b/docs/ORIENT.md @@ -30,6 +30,7 @@ Before the first edit in a crate, read `crates//AGENTS.md` when that file | Which head file (tx / header / scripthash) | [`heads.md`](./heads.md) | | Crash, tip-as-commit, kill-9 | [`crash-recovery.md`](./crash-recovery.md) | | Tests, budgets, coverage, fixtures | [`../TESTING.md`](../TESTING.md) | +| cargo-mutants (weekly sweep, PR advisory) | [`../TESTING.md`](../TESTING.md) (Mutation testing); snapshot [`mutants/`](./mutants/) | | Multi-step plan (Red → Green → Refactor) | [`how-we-plan.md`](./how-we-plan.md) | | cargo / clippy / deny / rustc logs (do not load into the session) | [`how-we-plan.md`](./how-we-plan.md) (Agent RAM) | | `rearden-grok[bot]` operator VM (ignore unless that identity) | [`../rearden-vm-HOST.md`](../rearden-vm-HOST.md) | diff --git a/docs/README.md b/docs/README.md index 49b06d2a8..56067c445 100644 --- a/docs/README.md +++ b/docs/README.md @@ -58,6 +58,7 @@ into `AGENTS.md`. | [`errata.md`](./errata.md) | Known one-off store/confirm quirks. | | [`peer-clients.md`](./peer-clients.md) | Hornet Node and satd: what to steal (tests/ideas) and what not to copy. Ranked items stay here; do not copy into quality.md. | | [`lightning.md`](./lightning.md) | CLN and LDK as Bitcoin backends: five `bcli` calls, Esplora/Electrum chain sync, `--sh-index` API matrix. Not LND. | +| [`mutants/`](./mutants/) | Generated cargo-mutants missed/timeout snapshot. How to run / CI: [`TESTING.md`](../TESTING.md). Not a quality backlog. | | [`external_findings/`](./external_findings/) | Numbered audit reports + regression pointers. Do not flatten into CHANGELOG. | ## Root (stay at root) diff --git a/docs/mutants/missed.md b/docs/mutants/missed.md index f06fee421..8dac2ddc6 100644 --- a/docs/mutants/missed.md +++ b/docs/mutants/missed.md @@ -1,11 +1,19 @@ -# Mutants Baseline +# cargo-mutants snapshot -> Generated: 2026-09-20 16:14 ET · Runner: M4 mini · consensus 2026-09-19 night → 2026-09-20 morning (9h, 2518 tested) · primitives 2026-09-20 ~15:00 ET (16m, 292 tested) +How to run and what CI does: [`../../TESTING.md`](../../TESTING.md) +(Mutation testing). This directory is a **generated** missed/timeout list +from a host run, not the quality roadmap +([`../quality.md`](../quality.md)). + +Generated: 2026-09-20 16:14 ET. Host: M4 mini. Consensus 2026-09-19 night +→ 2026-09-20 morning (9h, 2518 tested). Primitives 2026-09-20 ~15:00 ET +(16m, 292 tested). | Crate | Tested | Missed | Timeout | Details | | :--- | ---: | ---: | ---: | :--- | | `rbitcoin-primitives` | 292 | 4 | 8 | [./rbitcoin-primitives.md](./rbitcoin-primitives.md) | | `rbitcoin-consensus` | 2518 | 304 | 38 | [./rbitcoin-consensus.md](./rbitcoin-consensus.md) | -### Top files - consensus +### Top files — consensus + - `block/mod.rs` 65 · `script/interpreter.rs` 57 · `silent_payments.rs` 50 · `confirm_run/write.rs` 23 · `confirm_run/pin.rs` 22 diff --git a/docs/mutants/rbitcoin-consensus.md b/docs/mutants/rbitcoin-consensus.md index 4d0d72b7f..d08d0c732 100644 --- a/docs/mutants/rbitcoin-consensus.md +++ b/docs/mutants/rbitcoin-consensus.md @@ -1,4 +1,6 @@ -# rbitcoin-consensus Mutants - Full Specifics Index +# rbitcoin-consensus mutants (generated) + +Owner: [`../../TESTING.md`](../../TESTING.md). Index: [`missed.md`](./missed.md). Generated from 9h run: 304 missed + 38 timeouts = 342 entries diff --git a/docs/mutants/rbitcoin-primitives.md b/docs/mutants/rbitcoin-primitives.md index 9c255ec74..583c8d94c 100644 --- a/docs/mutants/rbitcoin-primitives.md +++ b/docs/mutants/rbitcoin-primitives.md @@ -1,4 +1,6 @@ -# rbitcoin-primitives Mutants - Full Specifics Index +# rbitcoin-primitives mutants (generated) + +Owner: [`../../TESTING.md`](../../TESTING.md). Index: [`missed.md`](./missed.md). Generated from 16m run: 4 missed + 8 timeouts = 12 entries diff --git a/docs/quality.md b/docs/quality.md index 7447a1bf1..643b5e571 100644 --- a/docs/quality.md +++ b/docs/quality.md @@ -8,7 +8,8 @@ in [`CHANGELOG.md`](../CHANGELOG.md). 1.0 product gates: **Last reaudit:** 2026-09-17. Schema **24**. Core functional **75** `test_runner` jobs (**69** inventory `run`; Core expands transport twins and `wallet_txn_*` flags) / **198** `skip`. Findings **001–023** fixed. Nightly -fuzz **20** jobs. Previous: 2026-09-15. +fuzz **20** jobs. Weekly mutants **8** shards (not required; owner +[`TESTING.md`](../TESTING.md)). Previous: 2026-09-15. | Section | Purpose | |---------|---------|