Update flake.lock - #3
Open
github-actions[bot] wants to merge 1 commit into
Open
Conversation
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
July 6, 2025 01:09
276d4b9 to
6c084a5
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
July 13, 2025 01:11
6c084a5 to
4f533b5
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
July 20, 2025 01:12
4f533b5 to
813c735
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
July 27, 2025 01:12
813c735 to
8ff43d3
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
August 10, 2025 01:11
63b22c1 to
1f5ced7
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
August 24, 2025 01:05
cbe3e8c to
0976a98
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
August 31, 2025 01:01
0976a98 to
8c629ea
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
September 14, 2025 01:00
7aed32b to
88a00d6
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
September 21, 2025 01:01
88a00d6 to
481e47d
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
October 5, 2025 01:01
309694e to
c5bda1e
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
October 12, 2025 00:59
c5bda1e to
41c44f6
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
October 26, 2025 01:02
ab795ae to
836d148
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
November 9, 2025 01:03
93abcaf to
8bfabe3
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
November 16, 2025 01:05
8bfabe3 to
525c669
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
November 30, 2025 01:08
3fd5568 to
ac3b447
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
December 7, 2025 01:08
ac3b447 to
01dfab4
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
December 21, 2025 01:08
7e7c6c0 to
f24fbc6
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
December 28, 2025 01:12
f24fbc6 to
fb5ceee
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
February 22, 2026 00:13
1054a7f to
63c03cf
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
March 8, 2026 00:13
73f7460 to
b295bf8
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
March 22, 2026 00:14
db11d6c to
d7bd59a
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
March 29, 2026 00:17
d7bd59a to
22064a5
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
April 5, 2026 00:17
22064a5 to
7797be6
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
April 12, 2026 00:19
7797be6 to
af1995d
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
April 19, 2026 00:19
af1995d to
31204e0
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
April 26, 2026 00:22
31204e0 to
01e5084
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
May 3, 2026 00:25
01e5084 to
0a213b3
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
May 10, 2026 00:27
0a213b3 to
d4449d1
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
May 17, 2026 00:28
d4449d1 to
8a199c9
Compare
zerosnacks
pushed a commit
that referenced
this pull request
May 19, 2026
…icates (foundry-rs#14482) * feat(invariant): handler-side assertion bugs decoupled from invariant predicates Rebased on top of merged foundry-rs#12587 (assert_all). Adds handler-bug machinery: - Decoupled handler-side assertions from invariant predicates: dedup by (reverter, selector) site, shortest-reproducer wins on collision. - Dedicated 'Suite handlers:' report section with full counterexamples. - Continuous campaign under assert_all: preflight handler bugs are recorded and the campaign keeps running for the full budget. - Live pulse-stream metrics surface unique handler-bug counts alongside invariant unique_failures; record_new_invariant_failures emits per-tick events deterministically so multi-break ticks are all reported. - Persistence + replay under failures/<contract>/handlers/<site>.json, keyed by keccak256(reverter || selector); replays merge with shortest-wins, prune non-reproducing files, leave incompatible ones. - Post-campaign shrinking reuses the invariant shrink loop; intermediate calls that themselves assert are rejected to avoid promoting a different finding. - Cached failure counts on InvariantFailures keep invariant_count() and handler_count() O(1) on the per-call hot path. - E2E coverage in tests/cli/test_cmd/invariant/handler.rs. * fix(invariant): clear GLOBAL_FAIL_SLOT after recording handler bug A non-reverting `vm.assert*` (`assertions_revert = false`) writes GLOBAL_FAIL_SLOT = 1 on the cheatcode address and the call gets committed. Left in place, that committed slot poisons every subsequent `is_success` / `handlers_succeeded` check for the rest of the run, silently suppressing later `assert_invariants` and `afterInvariant` evaluations and undercounting bugs the campaign would otherwise find. Clear the slot via `Executor::clear_global_failure` right after `record_handler_assertion_bug` in both call sites (the per-call `can_continue` path and the `should_check_invariant == false` inline path). Simplified the helper signature: dropped the unused `Option<&mut StateChangeset>` argument and the always-discarded return type; backend-write failures (a fundamentally broken state) are traced. Adds a regression e2e test that asserts once on call #1, then bumps a counter on calls #2/#3 to trip a real predicate. Without the fix the predicate failure is silently dropped; with the fix both the handler bug and the predicate failure are reported. * feat(fuzz): generate msg.value for payable functions in invariant fuzzing (foundry-rs#14565) * feat(invariant): assert all invariants * Tests and Nits Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: check all invariants in afterInvariant gate and preflight Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: use per-invariant fail_on_revert when recording handler revert failures Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 * fix: commit state between txs in generate_counterexample Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: preflight check all invariants, not just primary Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: exclude secondary invariants from optimization mode runs Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * refactor: rename invariant_fn to primary_invariant_fn, deterministic preflight error, debug_assert on empty invariants Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * feat: show broken invariant count in progress bar during continuous runs Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): rename continuous_run to assert_all and default to true Renames the InvariantConfig field to better describe its semantics ("assert every invariant in the suite, don't stop on first failure") and flips the default to true so multi-invariant suites report all broken invariants by default, matching Echidna/Medusa behavior. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcd68-66ac-76ed-ac5c-7ea722a9c9ae * feat(invariant): parameterize shrinker by target invariant + persisted failures footer - Generalize shrink_sequence, shrink_sequence_value, replay_run, replay_error to accept target_invariant: &Function (currently always primary; unblocks per-secondary shrinking). - Move reset_shrink_progress out of shrink fns; called once per invariant from replay_error. Progress label now 'Shrink: <invariant_name>'. - Add TestResult.invariant_failure_dir; Display appends 'N invariant failures persisted to <dir> — rerun to shrink' when secondary failures were written. Amp-Thread-ID: https://ampcode.com/threads/T-019dcd68-66ac-76ed-ac5c-7ea722a9c9ae Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): structured InvariantOtherFailure for assert_all secondaries Promotes TestResult.other_failures from Vec<String> to Vec<InvariantOtherFailure> carrying name, reason, optional counterexample, and persisted path. Display renders each secondary symmetrically with [FAIL: reason] + [Sequence] block when a counterexample is available, falling back to the terse 'name: reason' one-liner otherwise. Amp-Thread-ID: https://ampcode.com/threads/T-019dcd68-66ac-76ed-ac5c-7ea722a9c9ae Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): serial secondary shrinking + Ctrl-C persists un-shrunk secondaries PR-3 of the assert_all rollout. After the campaign finishes, every broken secondary invariant is shrunk in turn via replay_error so users get a ready-to-debug counterexample for each failure in a single run (matching how the primary is rendered: [FAIL: reason] <name> + [Sequence] block). On Ctrl-C, instead of dropping known secondaries (previous behavior was a 'break' before pushing them), the loop keeps recording every failure the campaign discovered. The shrink + replay step is skipped to honor the interrupt, but the un-shrunk sequence is persisted via BaseCounterExample::from_invariant_call (no execution required), so a re-run targeting that secondary picks up the saved counterexample and shrinks from there — same UX as re-running an interrupted primary. Output of an interrupted run now includes a terse '<invariant>: <reason>' line for each secondary the campaign saw, preserving visibility of all broken invariants while keeping the interrupt fast. Adds e2e coverage: - assert_all: extended to verify secondary failures render symmetrically with shrunk sequences and that re-running skips persisted secondaries. - assert_all_only_primary: new test confirming no secondary [FAIL] blocks or persisted-failures footer appear when only the primary breaks. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcdd3-53f5-76b6-ac36-d59f06b58280 * feat(invariant): assert_all polish — [i/N] shrink counter, suite roll-up, opt-mode warning Three small UX wins for assert_all campaigns. No behavior change, no new dependencies. 1. Shrink progress bar gets an [i/N] queue counter when more than one invariant needs shrinking, so users see how many shrinkers are queued behind the current one (e.g. '[2/3] Shrink: invariant_X'). reset_shrink_progress and replay_error gain a position parameter; single-invariant call sites pass None. 2. Suite-level roll-up footer: when assert_all exercised >1 invariant and the test failed, render 'Suite assert_all: <broken>/<total> invariants broken' above the per-invariant blocks. Gives CI logs and Slack pastes a glanceable health line. New Option<usize> field on TestResult, populated only when meaningful. 3. Startup warning when assert_all + optimization-mode are combined. Optimization mode tracks one int256 return value, so any boolean secondary invariants in the same contract are filtered out before the campaign — previously silent. Now emits a once-per-suite warning naming the optimization invariant and every dropped boolean so users can move them to a separate contract. E2E tests: extend assert_all to assert the new 4/5 roll-up; assert_all_only_primary covers the 1/2 case; new assert_all_optimization_mode_warning verifies the warning fires with the dropped invariant names. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcdd3-53f5-76b6-ac36-d59f06b58280 * feat(invariant): warn when assert_all skips invariants with persisted failures Symmetric with the primary's existing persisted-replay warning. Echidna and Medusa never silently drop properties between runs — properties are re-evaluated every campaign and a previous failure doesn't suppress them. Foundry's per-property failure file model meant secondaries with a stale persisted counterexample were filtered out of the campaign with no acknowledgment, so users coming from Echidna/Medusa would see fewer invariants in the report than their contract defines. Now emits one stderr line listing every skipped name and the cache dir to clean, e.g.: Warning: test/X.t.sol:Suite: 3 invariant(s) skipped due to persisted failures: invariant_a, invariant_b, invariant_c. Run `forge clean` or delete files in cache/invariant/failures/Suite to re-include. E2E: extends assert_all re-run case with stderr_eq snapshot asserting the warning fires with all 3 skipped names. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcdd3-53f5-76b6-ac36-d59f06b58280 * fix(invariant): gate afterInvariant per-run under assert_all Previously afterInvariant was gated on failures.errors.is_empty() campaign-wide. Under assert_all that gate stayed closed for the rest of the campaign once any invariant broke, silently skipping the afterInvariant hook on every subsequent run. Any assertions or cleanup logic in afterInvariant therefore stopped running after the first unrelated invariant failure. Now snapshot failures.errors.len() at the start of each run and only skip afterInvariant when the current run produced a new failure. Preserves the legacy 'don't run afterInvariant on a run that already failed' semantics while letting it run on subsequent runs once an earlier invariant has broken. E2E: new assert_all_after_invariant_runs_after_earlier_failure case breaks invariant_first in run 1, keeps the campaign alive with a second never-breaking invariant, and asserts an always-reverting afterInvariant surfaces its marker in failure output. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): re-evaluate secondary persisted failures on settings change The secondary persisted-failure skip used a bare `.exists()` check at two sites in runner.rs (the warning + InvariantContract::new filter, and the post-campaign shrink loop). Under the new `assert_all = true` default this meant any leftover failure file from a previous run was treated as still valid even after the user changed a tracked setting (target contracts/selectors, target/excluded senders, fail_on_revert), silently dropping the secondary from the campaign with a misleading 'skipped due to persisted failures' warning. Now both sites use the same settings-aware compatibility check the primary's replay path uses (persisted_call_sequence settings.diff). Stale caches fall back to a fresh evaluation; only secondaries whose persisted settings still match the current run are honored. Also hoists current_settings up so the new secondary_has_compatible_persisted closure can reuse it across all three call sites (warning, filter, shrink-loop skip). E2E: new assert_all_secondary_persisted_revalidates_on_settings_change runs once with fail_on_revert=false, flips it to true, re-runs and asserts the suite roll-up shows 2/2 invariants broken — proving the secondary was re-evaluated rather than silently filtered out. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): drop hollow [FAIL] when only secondaries break under assert_all When the selected invariant test passes but a secondary breaks under assert_all, the report previously rendered a hollow '[FAIL]' header (no reason, no counterexample) for the primary and the suite roll-up overcounted broken invariants as '1 + other_failures.len()', attributing a non-existent primary failure. Now key the primary header on whether the primary actually broke (`reason.is_some() || counterexample.is_some()`) and skip the header when it didn't. Roll-up uses the same flag so the count reflects only invariants that actually broke (e.g., 1/2 instead of 2/2). JSON shape is unchanged: top-level reason/counterexample stay null when the selected primary didn't break, with full secondary detail in other_failures. E2E: new assert_all_secondary_only_failure_no_hollow_fail asserts a secondary-only break renders 'Suite assert_all: 1/2 invariants broken' followed by the secondary's '[FAIL: ...] <name>' block, with no hollow primary header. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): scope assert_all hollow [FAIL] suppression to secondary-only case Previous commit suppressed the '[FAIL]' header whenever the primary had no top-level reason or counterexample, which also matches DS-style failures (they signal via the 'failed' flag and log events rather than through TestResult.reason). That regressed failure_assertions::ds_style_test_failing and test_cmd::core::legacy_assertions in CI. Now the suppression is scoped strictly to the assert_all secondary-only case: skip the primary header only when no primary failure AND assert_all is in play AND there is at least one secondary to render. DS-style, plain unit and single-invariant failures keep the original '[FAIL]'/'[FAIL: ...]' rendering. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): attribute failure event to first broken invariant in declaration order The structured JSON 'failure' event emitted to stderr at campaign end (consumed by benchmark and CI tooling) used 'errors.values().next()' on a HashMap to pick its 'reason' field, while hardcoding the 'invariant' field to the primary's name. With HashMap RandomState, the same broken set of invariants produced a different reason string across runs, and the event was self-inconsistent (e.g., 'invariant: invariant_balance, reason: fee miscalculation'). Three sites used this pattern: in-run break path, afterInvariant break path, and the preflight check fallback. Now they walk 'invariant_contract.invariant_fns' in declaration order (a Vec, deterministic) and pick the first one with a recorded failure. Both 'invariant' and 'reason' fields refer to the same function, and the event is stable across runs. A new 'first_broken_event' helper centralizes the lookup. E2E: assert_all_failure_event_uses_declaration_order declares three invariants (a, b, c) that all break on the same call, runs with '--mt invariant_c' (primary is the last declared) and asserts the emitted event names invariant_a with reason 'a broken'. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * refactor(invariant): rename to InvariantSecondaryFailure / invariant_secondary_failures and serialize sparsely Renames TestResult.other_failures -> invariant_secondary_failures and the underlying InvariantOtherFailure struct -> InvariantSecondaryFailure. The previous names were generic ('other relative to what?'); the new names align with the existing 'primary/secondary' terminology used throughout the assert_all rollout and follow the Rust Vec<Foo>/foos plural-of-singular convention. Also marks the field with #[serde(default, skip_serializing_if = 'Vec::is_empty')] so it is omitted from JSON output for any test that has no secondary failure data — plain unit tests, fuzz tests, passing tests. Pre-PR JSON consumers continue to see the same shape on those results. invariant_failure_dir and assert_all_invariant_count already had Option::is_none guards. Updates the SimpleContractTest{NonVerbose,Verbose}.json fixtures to drop the now-skipped empty field. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * test(invariant): assert_all + fail_on_revert=false attributes assert() to all live invariants Amp-Thread-ID: https://ampcode.com/threads/T-019dd262-ed81-723c-aaaa-8e1314bed45a Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): decouple handler-side assertions from invariant predicates Handler-side assertion failures are now tracked in a dedicated broken_handlers map keyed by (reverter, selector) instead of being attributed to every live invariant. They surface in their own "Suite handlers:" report section, keeping invariant predicate breaks rendered separately. Under assert_all = true (the new default), the campaign continues for the full budget after a preflight invariant failure so handler-side bugs and still-live invariants can be discovered. The legacy abort-on-preflight behavior is preserved when assert_all = false. Live progress (progress bar + JSON pulse events) now surfaces unique handler bug counts alongside invariant failure counts so both classes are visible during the campaign. Tests: - New regression test assert_all_handler_assertion_routed_to_handler_section asserting the "handler bug != invariant break" semantics. - Existing handler-assert tests (invariant_fail_on_assert_panic, invariant_fail_on_vm_assert_*, etc.) updated to expect the new "Suite handlers:" rendering while keeping the failure-reason line. - should_exit_early_on_invariant_failure now sets assert_all = false explicitly so it continues to exercise the legacy abort-on-preflight path. Refs: foundry-rs#14437 * fix: avoid private intra-doc link in HandlerAssertionFailure Amp-Thread-ID: https://ampcode.com/threads/T-019dd3bb-9f77-7224-9d25-acdf2c0dd095 Co-authored-by: Amp <amp@ampcode.com> * refactor(invariant): fold broken_handlers into InvariantFailureMetrics Move the unique handler-bug counter into InvariantFailureMetrics alongside failures / unique_failures so all campaign-level failure counters live in one struct, and drop the separate parameter from build_invariant_progress_json. Snapshot is refreshed each iteration from failures.broken_handlers. Addresses review comment on foundry-rs#14482. * refactor(invariant): dedup handler-side assertion bugs by edge-coverage fingerprint - Handler-side assertion bugs are now deduped by edge-coverage fingerprint of the asserting call, not by `(reverter, selector)`. Distinct paths → distinct bugs (Medusa/Echidna semantics). - Smaller-reproducer rule: on fingerprint collision, the entry with the shortest `call_sequence` wins. - Graceful fallback: when edge coverage is disabled (no `corpus_dir`), fingerprint falls back to `keccak256(reverter || selector)` — preserves prior behavior. * feat(invariant): shrink handler-side assertion bug counterexamples - Each handler bug's call sequence is shrunk post-campaign to the minimal prefix that still triggers the asserting (anchor) call. Anchor is pinned and never dropped; intermediate calls that themselves assert are rejected so we don't promote a different finding. - DRY refactor in `shrink.rs`: extracted `run_shrink_loop` (shared by `shrink_sequence` and `shrink_handler_sequence`) and `replay_sequence` (shared by `check_sequence` and `handler_sequence_still_asserts`). `check_sequence_simple` + `check_sequence_with_accumulation` collapsed into one `check_sequence`. - Error policy: invariant shrink keeps legacy "keep removed on error" semantics; handler shrink restores on error so a replay failure can never silently produce a non-reproducible counterexample. - Belt-and-suspenders: shrunk handler sequences are replayed once more before being stored; on any failure we fall back to the original. - Renderer: `HandlerAssertionFailure` now carries `original_sequence_len` so the existing `(original: N, shrunk: M)` format works for handler bugs. * feat(invariant): persist and replay handler-side assertion bugs - Each broken handler is written to `<failure_dir>/handlers/<edge_fingerprint>.json` (reusing `InvariantPersistedFailure` with `assertion_failure: true`); `(reverter, selector)` is stripped off and recovered from the last call's `(target, calldata[..4])` at replay time. - At campaign start, every file in `handlers/` is replayed via `check_sequence` with `expect_assertion_failure: true`. Bugs that still reproduce are merged into the campaign's `handler_errors` (shortest-wins on collision); files that no longer reproduce are deleted in place; settings-incompatible files are left untouched. - DRY refactor in `runner.rs`: extracted `replay_persisted_call_sequence` (shared by primary-invariant and handler-side replay) and a small `ReplayContext<'_>` bundle so both call sites stay short. `HandlerAssertionFailure::from_replayed_sequence` constructor centralises the (reverter, selector) recovery. - E2E coverage in `handler_assertion_persisted_to_disk`: confirms the file is written on first run, that a `runs = 0` rerun fails purely from the persisted replay (asserting the new `Replayed handler-side assertion bug` warning), and that the file is deleted when the underlying contract is patched to no longer assert. * perf(invariant): only hash edge coverage for asserting calls - Narrow the `pre_merge_edges_hash` snapshot gate from `call_result.reverted` to `assertion_failure`. The hash is only consumed by the handler-bug dedup path; for every other reverted call (vm.assume, MAGIC_ASSUME, plain `require` failures, fail_on_revert-ignored reverts) we were keccak-ing the edge buffer for nothing. Net effect: a `keccak256` over the per-call edge buffer is now skipped on the dominant non-asserting revert path. - `did_fail_on_assert` is hoisted above the snapshot and reused by the non-discarded branch, so the predicate is computed once per call instead of twice. `!discarded` short-circuit also reclaims the wasted hash on MAGIC_ASSUME-discarded calls. - Drive-by `cargo fmt` on four unrelated lines touched by earlier commits. * feat(invariant): pre-seed campaign with persisted handler bugs for live telemetry * test(invariant): handler-bug coverage for multi-handler, post-shrink replay, settings invalidation - `multi_handler_bugs_each_persist_independently`: two distinct handler contracts in the same campaign, each producing its own assertion bug. Confirms both surface in `Suite handlers: 2 assertion bug(s) found` and that the `handlers/` directory holds one JSON per fingerprint. - `handler_bug_replay_is_idempotent_after_shrink`: noop-prefixed handler so the discovered sequence has to be shrunk. Reads the persisted JSON to assert it holds the post-shrink (anchor-only) sequence, then re-runs with `runs = 0` and asserts the report renders `(original: 1, shrunk: 1)` instead of growing back. - `handler_persisted_failure_skipped_on_settings_change`: flips a tracked `InvariantSettings` field (`fail_on_revert`) between runs. With `runs = 0` and the persisted file present, asserts the `settings have changed` warning fires, the test passes (no replay), and the file is left intact for a future run with the original settings to pick up. * fix(invariant): strict handler-bug replay rejects stale files and divergent shrinks - Persisted handler-side assertion bugs were replayed via `check_sequence(expect_assertion_failure=true)`, which accepted any sequence-level failure as proof the bug still reproduced. Stale files were silently kept whenever an earlier call now asserted, when only the invariant predicate now broke, or when the anchor still asserted on a different code path. - Shrinking checked only that the anchor still asserted; dropping a setup call could push the anchor through a different branch and produce a different bug under the original fingerprint, breaking dedup semantics and persisted identity. - Add `replay_handler_failure_sequence` as the single strict path used by both startup replay and post-shrink verification: requires no pre-anchor assertion, anchor must assert, and recomputes the normalized edge fingerprint. - Persisted-replay keeps a file only if the recomputed fingerprint matches the filename; the shrink predicate rejects candidates whose anchor fingerprint diverges from the originally recorded one. - Move the public `CheckSequenceOptions` and new `HandlerReplayOutcome` to the top of `shrink.rs` next to the other shared types, and split the handler-bug e2e tests into their own `invariant/handler.rs` module. * perf(invariant): drop per-call clone in campaign loop and restore O(k) replay fast path - Replace the per-call `BasicTxDetails::clone()` in the campaign hot loop with a one-shot extraction of `(handler_target, handler_selector)` (Address + 4-byte Selector). The clone existed solely so `&tx` could be passed to `can_continue` alongside `&mut current_run`; with `can_continue` now taking the two scalars directly, helpers (`execute_tx`, `record_metrics`, `collect_data`) re-borrow the input from `current_run.inputs.last()` via field-level split borrows. Net effect: the `Bytes` calldata buffer is no longer copied on every fuzzed call. - Also collapses the duplicate inline `(target, selector)` extraction in the `else` branch of `should_check_invariant` — both branches now reuse the snapshot. - Restore the O(k) fast path in `replay_sequence`: when `accumulate_warp_roll == false` we iterate only the kept indices and pass `&calls[idx]` directly, skipping both the full-length scan over `calls` and the `BasicTxDetails::clone()` per kept call. The accumulating arm still walks the full sequence so warp/roll from skipped txs lands as a concrete delta on the next kept tx. Shrinking calls this in a tight loop, so for a length-`n` candidate shrunk to `k` calls the per-iteration cost goes from O(n) + k clones back to O(k) + 0 clones. * feat(invariant): site-granular dedup for handler bugs + live pulse-metrics fix - Change `broken_handlers` dedup key from edge-coverage fingerprint to `(reverter, selector)`, matching Echidna/Medusa per-function semantics. Multiple paths through the same handler collapse to one bug; persisted reproducers stay minimal via shortest-`call_sequence`-wins on collision. - Persist handler bugs via stable site hash `keccak256(reverter || selector)` in `record_handler_failure` / `replay_persisted_handler_failures` so the on-disk artifact name is independent of edge coverage. - Pulse metrics: bridge any newly-recorded invariant breaks into `failure_metrics` regardless of the `can_continue` outcome via `record_new_invariant_failures`. Previously `unique_failures` stayed at 0 until *all* invariants in an `assert_all` run broke, since `can_continue` only returns false at that point. Now live pulses match the final report. Removed `first_broken_event` (no longer needed). - E2E tests updated to assert site-granular behavior (multiple paths through one selector collapse to one bug). - TODO noted for finer-grained dedup of multiple distinct `assert(...)` failures within the same handler. * fix(clippy): use values() instead of iter() with unused key Amp-Thread-ID: https://ampcode.com/threads/T-019df149-03af-722b-abda-d8ab6332ee3b Co-authored-by: Amp <amp@ampcode.com> * fix(clippy): drop redundant references in trimmed_hex format args Amp-Thread-ID: https://ampcode.com/threads/T-019df149-03af-722b-abda-d8ab6332ee3b Co-authored-by: Amp <amp@ampcode.com> * feat(fuzz): generate msg.value for payable functions in invariant fuzzing Adds automatic msg.value generation for payable functions during initial call generation and corpus mutation. Stacked on top of foundry-rs#14482. - Add value: Option<U256> field to CallDetails and BaseCounterExample (serde-default for corpus back-compat) - Add fuzz_msg_value() proptest strategy and generate_msg_value() runner helper, biased toward small values (85% none / 10% wei / 4% milli-eth / 1% eth for proptest; 60/30/9/1 for runner) - Detect payable mutability in fuzz_contract_with_calldata and compose the value strategy - Forward value through execute_tx with balance-aware fallback to 0 when sender has insufficient balance - Mutate value with 15% probability in abi_mutate for payable functions - Render value in counterexamples (regular: value=X; solidity: {value: X}) - Add invariant_msg_value cli test Excludes the all-call mutation strategy, sender-mutation, and max_deal config from the original PR foundry-rs#13177 — those are independent concerns. Based on foundry-rs#8644 Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com> * fix(fuzz): bound msg.value by sender balance, use UintStrategy for value generation --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: George Niculae <george@gxn3ql7y5j.tail388b2e.ts.net> Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com> * fix(invariant): gate handler success on pending changeset only Avoid mutating GLOBAL_FAIL_SLOT in committed backend state to clear the poisoned flag after recording a handler bug. Instead, route the invariant runner's view-call success checks through is_success_handler_gate, which only counts the slot as failed when this call's own changeset writes it, ignoring a stale committed 1 from a previously-recorded handler bug. Preserves deterministic fuzzing paths and stops the poisoned flag from suppressing later assert_invariants / afterInvariant evaluations. --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: George Niculae <george@gxn3ql7y5j.tail388b2e.ts.net> Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com>
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
May 24, 2026 00:30
8a199c9 to
479afad
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
May 31, 2026 00:33
479afad to
15585f9
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
June 7, 2026 00:35
15585f9 to
30565c0
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
June 14, 2026 00:37
30565c0 to
b4ea824
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
June 28, 2026 00:33
7bd90eb to
f4a9ab5
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
July 5, 2026 00:28
f4a9ab5 to
3f308a0
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
July 19, 2026 01:53
c39993f to
e2bd775
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
2 times, most recently
from
August 2, 2026 01:57
cc7b911 to
affb344
Compare
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
August 9, 2026 01:03
affb344 to
bb0fb13
Compare
Flake lock file updates:
• Updated input 'fenix':
'github:nix-community/fenix/f374034' (2026-04-25)
→ 'github:nix-community/fenix/7f3ed5f' (2026-08-15)
• Updated input 'fenix/rust-analyzer-src':
'github:rust-lang/rust-analyzer/8954b66' (2026-04-21)
→ 'github:rust-lang/rust-analyzer/baabc58' (2026-08-13)
• Updated input 'nixpkgs':
'github:NixOS/nixpkgs/01fbdee' (2026-04-23)
→ 'github:NixOS/nixpkgs/6b5e5b7' (2026-08-13)
github-actions
Bot
force-pushed
the
update_flake_lock_action
branch
from
August 16, 2026 00:47
bb0fb13 to
2095a3b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated changes by the update-flake-lock GitHub Action.
Running GitHub Actions on this PR
GitHub Actions will not run workflows on pull requests which are opened by a GitHub Action.
To run GitHub Actions workflows on this PR, close and re-open this pull request.