Skip to content

SOME ONE IS STEALING MY CRYPTO#1

Open
caraabearzz wants to merge 2607 commits into
caraabearzz:masterfrom
foundry-rs:master
Open

SOME ONE IS STEALING MY CRYPTO#1
caraabearzz wants to merge 2607 commits into
caraabearzz:masterfrom
foundry-rs:master

Conversation

@caraabearzz

@caraabearzz caraabearzz commented Jul 22, 2025

Copy link
Copy Markdown
Owner

Motivation

Solution

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

@caraabearzz caraabearzz left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this was really good feedback. Thank you

decofe and others added 29 commits June 22, 2026 21:25
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
* feat(symbolic): persist replayable counterexample artifacts

* fix symbolic artifact replay semantics

* fix sequence artifact replay result kind

* fix sequence artifact replay revert count

* fix symbolic artifact replay validation

* fix(forge): validate symbolic replay senders

* fix(forge): allow replay-created invariant targets

* fix(forge): validate symbolic replay calls against ABI

* fix(forge): respect selector filters in symbolic replay

* fix(forge): reject zero sender in symbolic replay

* fix(forge): report symbolic replay failure counts

* fix: bless

* fix(forge): validate symbolic replay sender

* fix(forge): harden symbolic artifact replay

* fix symbolic artifact replay path matching

* fix invariant replay executed call counts

* fix single-call symbolic artifact replay

* strengthen symbolic artifact filename identity

* tighten symbolic artifact replay validation

---------

Co-authored-by: figtracer <me@figtracer.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
* perf(fuzz): avoid eager observed call calldata

* fix(fuzz): make should_record_observed_call const

Satisfies clippy::missing_const_for_fn (CI runs with -Dwarnings) and matches
the surrounding const fn style.

Amp-Thread-ID: https://ampcode.com/threads/T-019eeea2-e582-73e8-87c5-34c4dc836990
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Switch the edge index lookup used during coverage merging to the map entry API and mark it inline.

This keeps stable edge index assignment while avoiding a second map lookup when a new edge is assigned.

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
* perf(invariant): drain fuzzer values in place

* refactor(fuzz): remove unused drain_collected_values

The two invariant callsites now use InvariantFuzzState::collect_fuzzer_values,
which drains in place and reuses the buffer allocation. drain_collected_values
had no remaining callers and preserved the old allocation-dropping behavior, so
remove it to avoid reintroducing the churn.

Amp-Thread-ID: https://ampcode.com/threads/T-019eeea2-e582-73e8-87c5-34c4dc836990
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
* fix: add signed jwt bearer when used with --curl

* fix collapsible_if

* add JWT verification in tests

* add shared helper to safely build jwt

* run linter

---------

Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
* feat(config): make isolate the default

* Remove stale isolate-by-default feature references

* test(forge): update isolate trace expectations

* fix: address isolate default review comments

* fix(bench): keep isolate benchmark modes explicit
* feat(lint): add tautological-compare detector

Addresses #14381.

A relational or equality comparison whose two sides are the same
side-effect-free expression (x >= x, a[i] < a[i]) has a constant result,
usually a typo or dead code. Flags it for idents, member access, and
indexing; calls are excluded so sides that could differ are not flagged.

* fix(lint): catch literal index self comparisons

* fix(lint): skip user-defined operators in tautological-compare

A user-defined value type can only be compared through a user-defined
operator (`using {f as ==} for T global`), which dispatches to an
arbitrary function rather than built-in equality, so `x == x` is not
guaranteed to be constant. Skip operands whose type is a UDVT so the
lint no longer flags these comparisons as tautological.

* feat(lint): flag tautological self-comparison through identical casts

* feat(lint): cover binary, unary, ternary and payable in tautological-compare

---------

Co-authored-by: 0xMars42 <195151467+0xMars42@users.noreply.github.com>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
* perf(invariant): skip terminal corpus input generation

* fix(invariant): handle empty corpus sequence after skipping terminal input

Skipping terminal input generation removes the never-executed input that
previously padded current_run.inputs. A run where every executed call is
discarded (magic assume) or popped (reverts without fail_on_revert, handler
assertions) can now leave an empty sequence while new_coverage is still set
(edge coverage is collected before the input is popped), which hit
assert!(!inputs.is_empty()) in process_inputs_inner.

Replace the assert with explicit empty-sequence handling: still update
mutation stats and optimization state, but return without persisting a
corpus entry when no surviving inputs remain. Add a regression test for the
empty-sequence path and document why next-input generation is conditional.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019eeea2-e582-73e8-87c5-34c4dc836990

* fix(invariant): preserve terminal input for optimization sequences

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
* perf(invariant): cache metric selector keys

* fix(invariant): keep metric-key cache coherent with target set

The metric-key cache keyed cached results (including None) as stable for
the whole campaign. But invariant targets are updatable: newly deployed
contracts are added via collect_created_contracts mid-run and cleared at
end of run, and an address can be reused for a different artifact across
runs. This made cached entries go stale and silently mis-record metrics.

- Cache only resolved keys (no negative caching), so a tx whose target
  isn't a known target yet is re-resolved once the target is added.
- Invalidate cached entries for addresses added by collect_created_contracts
  (mid-run reuse) and removed at end_run (cross-run reuse).

Amp-Thread-ID: https://ampcode.com/threads/T-019eee95-59d3-76df-8984-f085cd504c65
Co-authored-by: Amp <amp@ampcode.com>

* chore(invariant): fix clippy and trim comments

- Make InvariantMetrics::record_call a const fn (missing_const_for_fn).
- Shorten the metric-key cache comments.

Amp-Thread-ID: https://ampcode.com/threads/T-019eee95-59d3-76df-8984-f085cd504c65
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
* perf(fuzz): avoid rebuilding storage layout map

* perf(fuzz): remove now-unused get_storage_layouts

The storage layout lookup is now done per-address directly against
TargetedContracts, leaving get_storage_layouts with no callers. Remove
it and its now-unused HashMap import.

Amp-Thread-ID: https://ampcode.com/threads/T-019eee95-59d3-76df-8984-f085cd504c65
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
* minimize symbolic counterexamples

* fix symbolic minimizer clippy

* test symbolic minimizer echidna fixtures

* test(symbolic): add echidna-derived minimizer fixtures

* fix(symbolic): defer hard arithmetic branch unknowns

* fix(symbolic): preserve minimizer failure identity

* fix(symbolic): skip reasonless minimization

* fix(symbolic): require minimizer replay reason
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
* fix(lint): include info rules by default

* test(lint): use snapbox for default severity test

* test(lint): bless default info snapshots
* fix(tempo): avoid receive-policy recipient collisions

* fix(tempo): derive template recipient from token
* feat(forge): configure fuzzer ensemble defaults

* chore(fuzz): tune corpus fresh sequence default

* chore(fuzz): harden configurable corpus tuning

* chore(fuzz): expose payable value weighting

* chore(fuzz): use weighted mutation distribution

* chore(fuzz): avoid default field reassignment

* chore(invariant): clamp sampled run depth

* fix(invariant): preserve fixed zero depth

* test(invariant): stabilize handler assertion snapshot

* fix(invariant): vary corpus freshness per worker

* fix(invariant): tune corpus freshness ensemble

* fix(invariant): satisfy worker config clippy

* fix(forge): clamp invariant depth and validate mutation weights

* test(forge): update zero-depth invariant snapshot

* fix(forge): address fuzzer config review

* fix(forge): preserve zero-depth and config provenance
* test: add T6 local regression coverage

* test(tempo): use named T6 activation constants

* test(tempo): cover T6 tempo-check flows
* feat(debugger): show failed-test debug hint

* test(debugger): update invariant output snapshot
stevencartavia and others added 30 commits July 7, 2026 07:18
Reports a modifier that exactly one function invokes, constructors
included. Invocations are taken from the resolved modifier lists, so
base-constructor calls sitting in the same syntactic position are
never confused with modifier calls; they are counted across the whole
unit, dependencies included, while only modifiers declared in the
project's own sources report, mirroring the unused-error pass.

Exemptions: virtual modifiers and overrides, which exist for dynamic
dispatch and cannot be inlined; zero invocations stay out, dead code
being a different concern.

Co-authored-by: 0xMars42 <195151467+0xMars42@users.noreply.github.com>
Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com>
ci: detect Tempo hardfork in check script
…3342)

* feat: init

* chore: lexing/parsing

* chore: visiting contracts

* chore: wip

* chore: wip

* chore: wip

* chore: visitor not visiting anything

* chore: visitor visiting

* chore: quick refactor before its too late

* chore: quick refactor

* chore: mutation gen part1

* feat: mutation collection

* feat: visitor refactor

* feat: visitor refactor

* feat: temp folder mgmt

* feat: temp file creation logic

* feat: compiling mutants

* chore: wip future multithread

* feat: mutation set building

* feat: multithread compile

* chore: fmt

* chore: fmt

* chore: fmt

* chore: fmt

* feat: refactor for test runner

* chore: test runner wip

* feat: working poc

* chore: doc

* chore: fmt

* chore: fmt

* chore: fmt

* feat: assign mut gen

* feat: unary mut

* feat: binary mut

* feat: members unary mut

* feat: members unary mut

* feat: rm delegatecall mut

* chore: refactor modular mut wip

* chore: refactor modular mut wip

* chore: refactor modular mut wip

* feat: mutator assign trait

* chore: fmt

* chore: fmt

* feat: bin mutator

* feat: other mutator mod

* feat: visitor refactor registry

* chore: merge fix

* chore: comment

* fix: solar visitor use

* chore: clippy and min refactors

* feat: assign mutator tests

* feat: test gen mut ident

* feat: test binop and delete expr

* feat: test delegate unaryop mut

* feat: test unaryop mut

* feat: wip generic test

* refactor: add mutate-path and mutate-contract optional args, keep mutate args working like before

* feat: enable contract match filter in mutation tests

* feat: optional mutators

* feat: generic mutator test(length)

* feat: generic mutator test(content)

* fix: unary mut for bool

* test: unary mut

* test: all mutators

* test: add neg case

* chore: refactor visitor

* chore: clippy

* chore: typos

* feat: integ test (hacky)

* feat: basic reporting

* fix: copy only src

* fix: copy only src

* fix: rel path

* fix: add walker in visitor

* fix: assign mutator

* chore: dbg cleanup

* fix: dyn test linking

* fix: missing mutant fmt

* fix: path in mut ctxt

* feat: save and resume mutation tests

* chore: typo and doc

* chore: remove cache file

* merge master

* clippy

* update

* use serde instead of manual dto for json caching

checkpoint

* fix cache filenames

* initial pass at adaptive testing - skip surviving sibling or child spans

* clippy && fmt

* feat(forge): parallel mutation testing with isolated workspaces

Implements parallel mutation testing for Foundry, building on PR #11996.

Key features:
- Parallel execution via rayon thread pool with configurable --mutation-jobs
- Isolated TempDir workspaces per mutant for safe concurrent execution
- Adaptive span skipping shared across workers (skip mutations in spans
  where a mutation already survived)
- Fail-fast per mutant (stops on first test failure)
- Symlinks for lib directories to avoid expensive copies
- Preserves project layout (custom src/test/libs paths supported)

Safety improvements:
- Path traversal protection (rejects .. components)
- catch_unwind prevents single panic from aborting entire run
- Symlinked directories skipped in copy to prevent traversal attacks
- 16MB stack size for rayon threads to avoid overflow

Performance: 2.5x speedup observed with 4 workers on 150 mutants

Closes #478

* fix: clippy, rustfmt, and config test for mutation_dir

* feat(mutation): add progress display with Ctrl+C support

- Add progress bar showing runs/total and job count
- Show relative file paths instead of absolute paths
- Add spinner for active mutants with line number and mutation diff
- Capture original expression context in Mutant struct
- Center code truncation around mutated operator
- Graceful Ctrl+C: cancel pending, show report for completed, exit 130
- Enhanced reporter with Solidity diffs and security suggestions
- Add mutation_testing_with_show_progress CLI test

Amp-Thread-ID: https://ampcode.com/threads/T-019be5ee-0615-7048-97dc-7ba7b354d384
Co-authored-by: Amp <amp@ampcode.com>

* chore: rename 'runs' to 'mutation runs' in progress bar

* fix: mark doc code block as text to fix doctest

* refactor: address grandizzy review comments

- Move mutation logic from test/mod.rs to mutation/orchestrator.rs
- Implement test helper for mutator tests
- Add snapbox assertions to CLI mutation tests with full output
- Update Solar LitKind comment (still valid - no int/uint distinction)
- Use 4 workers in show-progress test for consistency

Amp-Thread-ID: https://ampcode.com/threads/T-019be9ee-1c7c-734d-8e72-7e9b837b3945
Co-authored-by: Amp <amp@ampcode.com>

* Add time and report legend

* feat(mutation): add RequireMutator and source hash cache validation

- Add RequireMutator for security-critical require/assert mutations
  - require(x) -> require(true) - always passes
  - require(x) -> require(false) - always fails
  - require(x) -> require(!x) - inverted condition

- Add source content hash to SurvivedSpans cache
  - Prevents stale span reuse when source changes
  - Invalidates cache on hash mismatch
  - Handles legacy cache format migration

- Add comprehensive integration test (mutation_testing_require_mutator)
  - Demonstrates two-phase testing workflow
  - First run: basic tests (84% score)
  - Second run: comprehensive tests (98.1% score)
  - Shows only equivalent mutant survives

Amp-Thread-ID: https://ampcode.com/threads/T-019beb12-9720-740b-b4b6-de4f92eb10d6
Co-authored-by: Amp <amp@ampcode.com>

* Add unary_op_mutator_test module to tests

* fmt imports

* Update crates/forge/src/mutation/mutators/mod.rs

* nits

* Update crates/forge/src/mutation/mutators/assignment_mutator.rs

* nits

* refactor: replace ctrlc with tokio signal, update rstest to 0.26

Addresses review comments from @zerosnacks:
- Remove ctrlc dependency, use tokio's built-in signal handling instead
- Update rstest from 0.25.0 to 0.26
- Add tokio signal feature for Ctrl+C handling

The ctrlc crate is replaced with a background thread running a tokio
runtime that waits on tokio::signal::ctrl_c(). This avoids adding the
ctrlc dependency which is on the deny list.

Amp-Thread-ID: https://ampcode.com/threads/T-019bec4d-844c-7624-9054-1b7e6b056c2c
Co-authored-by: Amp <amp@ampcode.com>

* feat(mutation): add --json support for mutation testing

When --json flag is used with --mutate, outputs JSON with:
- Summary: total, killed, survived, invalid, skipped, mutation_score, duration_secs
- Survived mutants grouped by file with line, column, original expression, and mutant

All progress messages are suppressed in JSON mode for clean output.

Amp-Thread-ID: https://ampcode.com/threads/T-019bf8c4-8b62-741e-9226-e5cf23a050db
Co-authored-by: Amp <amp@ampcode.com>

* fix(mutation): symlink node_modules and soldeer dependencies in temp workspace (#13221)

Projects using npm (node_modules/) or Soldeer (dependencies/) for external
dependencies fail mutation testing because these directories are not copied
to the temporary mutation workspace.

This fix adds symlinking for these common external dependency directories,
following the same pattern already used for lib directories.

Fixes compilation errors like:
  error: 'node_modules/@uniswap/v3-core/...' not found

Tested with:
- Uniswap/universal-router (npm dependencies)
- morpho-org/morpho-blue (lib dependencies)

Amp-Thread-ID: https://ampcode.com/threads/T-019bf98e-1307-72f9-ac5f-a00d33518d40

Co-authored-by: Amp <amp@ampcode.com>

* fix(mutation): recursively symlink nested lib directories in temp workspace (#13224)

* fix(mutation): recursively symlink nested lib directories in temp workspace

Projects with nested git submodules (e.g., lib/euler-earn/lib/openzeppelin-contracts)
fail mutation testing because the temp workspace only symlinks top-level lib directories.

When libraries have their own dependencies in nested lib/ folders, and remappings
reference them with context-specific prefixes like:
  lib/euler-earn:@OpenZeppelin=lib/euler-earn/lib/openzeppelin-contracts/

These paths need to exist in the mutation workspace for compilation to succeed.

This fix adds symlink_nested_libs() which recursively walks through each library
and symlinks any nested lib/ directories, ensuring deeply nested submodules are
accessible in the temp workspace.

Tested against euler-xyz/evk-periphery which has multiple levels of nested submodules.

Amp-Thread-ID: https://ampcode.com/threads/T-019bfa2f-d0b0-703a-9822-945c87b1e2ab
Co-authored-by: Amp <amp@ampcode.com>

* test(mutation): add unit tests for symlink and copy functions

Tests cover:
- symlink_dir: basic symlink creation
- symlink_nested_libs: single level, deeply nested (3 levels), no lib dir, skip existing
- copy_dir_recursive: basic copy, skip symlinked dirs, nonexistent source
- relative_to_root: basic, same path, outside root

Amp-Thread-ID: https://ampcode.com/threads/T-019bfa2f-d0b0-703a-9822-945c87b1e2ab
Co-authored-by: Amp <amp@ampcode.com>

* style: format with nightly rustfmt

Amp-Thread-ID: https://ampcode.com/threads/T-019bfa2f-d0b0-703a-9822-945c87b1e2ab
Co-authored-by: Amp <amp@ampcode.com>

* fix(mutation): support custom lib directories in nested submodules

Read each nested library's foundry.toml config to get its actual lib paths
instead of hardcoding 'lib'. Falls back to default 'lib' if no config exists.

Amp-Thread-ID: https://ampcode.com/threads/T-019bfab1-a4b5-7008-8fb4-735aa3a560ac
Co-authored-by: Amp <amp@ampcode.com>

* clippy fix

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: zerosnacks <zerosnacks@protonmail.com>

* feat(mutation): add Yul opcode mutator (#13341)

* feat: make mutation test operators configurable (#13410)

make mutators configurable


Amp-Thread-ID: https://ampcode.com/threads/T-019c477b-60f0-71af-853e-f2b5902f0906

Co-authored-by: Amp <amp@ampcode.com>

* refactor: rename UnaryOperatorMutator to UnaryOpMutator (#13411)

* make mutators configurable

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c477b-60f0-71af-853e-f2b5902f0906

* refactor: rename UnaryOperatorMutator to UnaryOpMutator

Amp-Thread-ID: https://ampcode.com/threads/T-019c477b-60f0-71af-853e-f2b5902f0906
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>

* fix: address mutation testing issues (#13412)

* fix: address critical mutation testing issues

- Rename orchestrator MutationConfig to MutationRunConfig to avoid
  naming collision with foundry_config::MutationConfig
- Make generate_mutated_solidity and restore_original_source return
  Result instead of panicking, use byte-based splicing to avoid
  UTF-8 boundary panics
- Validate relative paths in copy_project_for_mutation with
  is_safe_relative_path to prevent path escape when config paths
  are not under the project root
- Fix cache key collisions by including a hash of the full contract
  path in cache filenames (prevents src/A.sol vs contracts/A.sol
  from sharing cache entries)
- Make generate_ast return Result and propagate parse errors instead
  of silently swallowing them

Amp-Thread-ID: https://ampcode.com/threads/T-019c477b-60f0-71af-853e-f2b5902f0906
Co-authored-by: Amp <amp@ampcode.com>

* fix: harden mutation testing with path validation and diagnostics

- Validate user-provided --mutate paths: normalize relative paths
  against config.root, check existence/is_file/.sol/non-test
- Fix --mutate-contract to match any contract in a multi-contract
  file (use .any() instead of .find().is_some_and())
- Add span bounds check in generate_mutated_solidity to prevent
  panics on invalid span positions
- Replace unwrap() with expect() in MutationContextBuilder calls
  with descriptive messages for each AST node type
- Fix cross-platform relative_path: use Path::components() with
  forward-slash joining for stable output across OSes

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c47bc-5807-7721-9051-7028de802b89

* refactor: address review feedback for mutation testing

Extract cache_file_path helper to consolidate repeated cache dir
and filename prefix logic across persist/retrieve methods.

Extract ensure_safe_relative_path helper to replace repeated
validate-and-bail pattern for path safety checks.

Remove dead code: generate_mutated_solidity and
restore_original_source are unused (runner.rs uses its own
apply_mutation with checked slicing and temp directories).

Amp-Thread-ID: https://ampcode.com/threads/T-019c47ec-22ab-75db-9c35-35e0e08ac396
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>

* chore: remove dead code from mutation module (#13413)

* fix: address critical mutation testing issues

- Rename orchestrator MutationConfig to MutationRunConfig to avoid
  naming collision with foundry_config::MutationConfig
- Make generate_mutated_solidity and restore_original_source return
  Result instead of panicking, use byte-based splicing to avoid
  UTF-8 boundary panics
- Validate relative paths in copy_project_for_mutation with
  is_safe_relative_path to prevent path escape when config paths
  are not under the project root
- Fix cache key collisions by including a hash of the full contract
  path in cache filenames (prevents src/A.sol vs contracts/A.sol
  from sharing cache entries)
- Make generate_ast return Result and propagate parse errors instead
  of silently swallowing them

Amp-Thread-ID: https://ampcode.com/threads/T-019c477b-60f0-71af-853e-f2b5902f0906
Co-authored-by: Amp <amp@ampcode.com>

* fix: harden mutation testing with path validation and diagnostics

- Validate user-provided --mutate paths: normalize relative paths
  against config.root, check existence/is_file/.sol/non-test
- Fix --mutate-contract to match any contract in a multi-contract
  file (use .any() instead of .find().is_some_and())
- Add span bounds check in generate_mutated_solidity to prevent
  panics on invalid span positions
- Replace unwrap() with expect() in MutationContextBuilder calls
  with descriptive messages for each AST node type
- Fix cross-platform relative_path: use Path::components() with
  forward-slash joining for stable output across OSes

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c47bc-5807-7721-9051-7028de802b89

* refactor: address review feedback for mutation testing

Extract cache_file_path helper to consolidate repeated cache dir
and filename prefix logic across persist/retrieve methods.

Extract ensure_safe_relative_path helper to replace repeated
validate-and-bail pattern for path safety checks.

Remove dead code: generate_mutated_solidity and
restore_original_source are unused (runner.rs uses its own
apply_mutation with checked slicing and temp directories).

Amp-Thread-ID: https://ampcode.com/threads/T-019c47ec-22ab-75db-9c35-35e0e08ac396
Co-authored-by: Amp <amp@ampcode.com>

* chore: remove dead code from mutation module

Remove unused symbols superseded by the orchestrator and parallel
runner architecture:

- MutationsSummary: update_valid_mutant, dead/survived/invalid/skipped
  string formatters, get_skipped, get_report_mut
- Mutant: line_number(source) method (shadowed by field),
  format_diff, description_with_context
- ParallelMutationRunner struct and run_mutations_parallel wrapper
  (replaced by orchestrator + run_mutations_parallel_with_progress)
- Stale re-exports and unused imports

Amp-Thread-ID: https://ampcode.com/threads/T-019c47ec-22ab-75db-9c35-35e0e08ac396
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>

* refactor(mutation): extract workspace utilities into shared module

Move project-copying logic from mutation/runner.rs into a new
crates/forge/src/workspace.rs module for reuse by other features
that need isolated temp workspaces (e.g., brutalization).

Extracted functions: copy_project, copy_dir_recursive, symlink_dir,
symlink_nested_libs, relative_to_root, ensure_safe_relative_path.

Tests moved from mutation/runner.rs to workspace.rs.

Amp-Thread-ID: https://ampcode.com/threads/T-019c4d7b-acac-75ef-9f69-f3619f6941b6
Co-authored-by: Amp <amp@ampcode.com>

* feat(mutation): add brutalizer mutator

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c2f6f-9824-74cc-a037-1bbcd139c9b5

* feat(mutation): add memory brutalization and FMP misalignment to brutalizer

Extends the BrutalizerMutator to inject inline assembly at public/external
function entry points, matching Solady's Brutalizer.sol modifier pattern.

Memory brutalization dirties scratch space (0x00-0x3f) and memory beyond
the free memory pointer, catching assembly that assumes zero-initialized
memory. FMP misalignment adds a small deterministic odd offset (1-31
bytes) to the free memory pointer, catching assembly that assumes
word-aligned pointers.

Only applied to functions that contain inline assembly blocks, and only
at call-frame boundaries (public/external) where the EVM guarantees
fresh memory. Injecting into internal functions would overwrite
legitimate caller state since internal calls share the call frame.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c2fc6-91f1-7202-b03e-4eb06c57f827

* refactor(mutation): restrict memory brutalization to external functions only

Public functions can be called internally (JUMP, shared caller memory)
or externally (CALL, fresh memory). At the source level we cannot
distinguish which path will be taken, so brutalizing a public function
risks overwriting legitimate caller state when called internally,
producing false positives. External functions can only be entered via
CALL/STATICCALL/DELEGATECALL, guaranteeing fresh zeroed memory.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c2fc6-91f1-7202-b03e-4eb06c57f827

* docs(mutation): improve brutalizer documentation

Explain what the mutator tests (dirty bits in sub-256-bit types, memory
safety assumptions), how to interpret mutation results (killed = code
validates properly, survives = gap in coverage or bug), and the Solady
equivalence. Document public function exclusion rationale.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c2fc6-91f1-7202-b03e-4eb06c57f827

* docs(mutation): document magic number functions in brutalizer

Add doc comments explaining the injected assembly (what scratch space
and FMP dirtying does), the splitmix64 constants (golden ratio, finalizer
multiplier, avalanche shifts), and why FMP offsets are forced odd.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c2fc6-91f1-7202-b03e-4eb06c57f827

* fix: add missing column_number field, fix clippy lints

Amp-Thread-ID: https://ampcode.com/threads/T-019c2ff5-b9f7-77b4-82a8-dd43d5eca9c3
Co-authored-by: Amp <amp@ampcode.com>

* refactor(mutation): use span-derived random values for memory pollution

Replace fixed not(0) pattern with deterministic random 256-bit words
derived from the span position via splitmix64. Each mutation site now
gets unique pollution values, catching bugs that might pass with all-1s
but fail with other bit patterns.

Extract shared splitmix64 and span_seed helpers, simplifying
deterministic_mask and deterministic_fmp_offset.

Amp-Thread-ID: https://ampcode.com/threads/T-019c2ff5-b9f7-77b4-82a8-dd43d5eca9c3
Co-authored-by: Amp <amp@ampcode.com>

* perf(mutation): use keccak256 chain to fill 1KB past FMP

Previously memory brutalization only wrote 2 words past the free memory
pointer. Now writes a splitmix64-derived seed word at the FMP and chains
keccak256 to fill 32 words (1024 bytes), catching assembly that reads
uninitialized memory well beyond scratch space.

Amp-Thread-ID: https://ampcode.com/threads/T-019c301f-203d-7282-9c23-580e91cf5be1
Co-authored-by: Amp <amp@ampcode.com>

* refactor(mutation): split BrutalizerMutator into value, memory, and FMP mutators

Split the monolithic BrutalizerMutator into three focused mutators:
- BrutalizerValueMutator: XOR type casts to dirty unused bits
- BrutalizerMemoryMutator: dirty scratch space and memory past FMP
- BrutalizerFmpMutator: misalign free memory pointer

Each has its own MutatorType variant (brutalizer-value, brutalizer-memory,
brutalizer-fmp), all excluded by default. Shared helpers extracted to
brutalizer_utils.rs.

Amp-Thread-ID: https://ampcode.com/threads/T-019c4d59-fe83-7063-8690-7c1f6887c84b
Co-authored-by: Amp <amp@ampcode.com>

* refactor: extract brutalizer from mutation framework into standalone module

- Move brutalizer logic out of mutation testing framework into
  crates/forge/src/brutalizer/ as a standalone source-level sanitizer
- Remove BrutalizerValue, BrutalizerMemory, BrutalizerFmp mutators and
  their tests from the mutation framework
- Create shared workspace module (crates/forge/src/workspace.rs) for
  project copying logic used by both mutation runner and brutalizer
- Move workspace-related tests from mutation/runner.rs to workspace.rs
- Add --brutalize flag to forge test with conflicts_with mutate
- Make --brutalize and --mutate mutually exclusive via clap

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c4d7b-acac-75ef-9f69-f3619f6941b6

* fix: brutalizer value masks and temp config resolution

- Fix value brutalizer to OR mask into discarded bits only (shift left
  by type width) instead of XORing the entire value, which corrupted
  kept bits and broke correct code under brutalization
- Fix compile_and_run_brutalized to call .sanitized() on temp config
  so paths are absolute, fixing source file exclusion when filters
  (e.g. --mc) are active
- Split brutalizer into sub-modules: value.rs, memory.rs, fmp.rs
- Add 7 CLI integration tests for --brutalize

Amp-Thread-ID: https://ampcode.com/threads/T-019c4d9e-9c66-723c-896e-65e641225266
Co-authored-by: Amp <amp@ampcode.com>

* test: condense brutalizer tests, add mixed asm/solidity coverage, improve --brutalize help

- Condense unit tests: merge special function exclusions and control
  flow variants into parameterized tests (27 → 20 tests)
- Condense CLI tests: keep only high-impact tests (12 → 5 tests)
- Add mixed Solidity/assembly patterns to robust contract: Solidity
  abi.encodePacked → assembly read, and assembly FMP alloc → Solidity
- Update --brutalize help text to explain user-facing value: catches
  latent bugs from zero-initialized memory and clean register assumptions

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c4d9e-9c66-723c-896e-65e641225266

* chore: clean-up imports

* refactor: clean up brutalizer module layout

* fix: avoid overlapping brutalizer cast transforms

* fix: normalize brutalized casts

* fix: preserve brutalize semantics and config

* fix: rebase brutalize config paths

* fix: rebase ignored compiler paths

* fix: brutalize doesn't support `replay_symbolic_artifact` for now

* fix: preserve brutalize rerun failures

* fix: address brutalize review comments

* fix(test): narrow brutalizer source rewrites

Skip test, script, and library sources when brutalizing broad source roots, and fill complete scratch words so high-half scratch reads are dirtied.

* test(forge): cover brutalized value bits

* fix(forge): preserve brutalized write dirs

Create project-local write-only fs permission directories in brutalized temp workspaces and canonicalize new write targets through their existing ancestor.

* fix(forge): copy symlinked brutalize sources

* fix(forge): persist brutalize rerun cache

* fix(forge): preserve brutalize copy aliases

* fix: via_ir warn

* fix(forge): address brutalize review blockers

---------

Co-authored-by: drgorillamd <83670532+drgorillamd@users.noreply.github.com>
Co-authored-by: Simon Something /DrGoNoGo <83670532+simon-something@users.noreply.github.com>
Co-authored-by: 0xchin <alanracciatti1220@gmail.com>
Co-authored-by: emo.eth <emodoteth@gmail.com>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: grandizzy <grandizzy.the.egg@gmail.com>
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Compile local interface targets with ABI-only output and avoid writing artifacts for the command path.

Benchmarked on a 3000-function/event/error Solidity fixture:

- cold local source: 1.092s -> 326.6ms (3.34x faster)

- after full build in prepare: 1.072s -> 321.1ms (3.34x faster)

The generated interface output matched master byte-for-byte, fresh runs did not create the target artifact, and preexisting artifact bytes were preserved.
Compile forge doc through the Solar-oriented project path and disable artifact writes, since the doc renderer needs resolved HIR but not bytecode artifacts.

Benchmarked on a 9006-line Solidity fixture with 3000 functions/events/errors:

- cold forge doc: 1.101s -> 353.6ms (3.11x faster)

- after full build in prepare: 1.106s -> 354.3ms (3.12x faster)

Generated docs matched master byte-for-byte. Fresh doc generation did not create target artifacts, and a preexisting full artifact remained byte-for-byte unchanged.
* feat(forge): add coverage attribution report

* fix(forge): respect report file for coverage attribution

* fix(forge): order attribution coverage reports

* perf(forge): reuse resolved coverage hit maps

* docs(forge): document coverage attribution payloads

* fix(forge): preserve attribution item anchors

* fix(forge): dedupe coverage file reports

* fix: DebugReporter anchor items filtering

---------

Co-authored-by: smartcontracts <smartcontracts@users.noreply.github.com>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com>
* fix(cast): sort source output for deterministic diffs

* fix(cast): make source output deterministic

* bump foundry-core for deterministic cast source
Pass the assertion-failure result computed in the invariant worker into can_continue so the failure path does not rescan the same call result and state changeset.

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
* perf(invariant): use entry for sender changeset

* chore: simplify sender changeset extraction

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
* perf(invariant): avoid eager block env clone

* Update crates/evm/evm/src/executors/invariant/mod.rs

---------

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Check cheap call override rejection conditions before reading the handler set, then test caller and target membership under one read lock. This trims lock traffic on the invariant call override path without changing override eligibility.

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Move owned replay logs, debug bytecodes, and line coverage out of RawCallResult while preserving the existing trace ownership used for reports and counterexamples.

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Build invariant target contract and selector maps together, and use unstable sorting for sender address filters where sort stability is not observable.

Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: autoopt[bot] <autoopt@tempo.xyz>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.