Skip to content

feat: coverage-replayer — minimal mainnet block set maximizing mega-evm branch coverage#153

Draft
flyq wants to merge 37 commits into
mainfrom
liquan/coverage-replayer-design
Draft

feat: coverage-replayer — minimal mainnet block set maximizing mega-evm branch coverage#153
flyq wants to merge 37 commits into
mainfrom
liquan/coverage-replayer-design

Conversation

@flyq

@flyq flyq commented Jul 11, 2026

Copy link
Copy Markdown
Member

Summary

Adds bin/coverage-replayer, an offline tool that replays mainnet blocks under LLVM branch instrumentation and derives the minimal block set covering every branch counter ever observed in mega-evm — the source of truth for keeping test_data/mainnet maximally representative. Also adds the zero-validation "light" witness decode to stateless-core/common and switches debug-trace-server onto it. Carries PR #152 (stateless-r2) via a merge; those files are reviewed there.

coverage-replayer

backfill fetches blocks + witnesses (RPC or straight from R2 with --witness-source r2), resident worker subprocesses replay each block with per-block counter isolation (reset → replay_block → capture; process isolation keeps the dispatcher out of the bitmaps), and a judge dedups per-block coverage bitmaps into patterns in a redb store, keeping one ~106KB sparse profdata per non-dominated pattern. Never-skip semantics: fetch retries forever, slow blocks are warned about but never killed, replay errors / header-sanity divergence fail-stop the run and are retried on re-run. set-cover computes the greedy minimal cover (antichain pruning, incumbent-biased tie-breaks, redundancy elimination); report renders llvm-cov for the selected set; merge combines per-machine shard stores (dense remap via content-addressed counter ids); inspect prints store statistics. Stores are namespaced by a build fingerprint (mega-evm rev + toolchain/host) and the symbol filter.

Light witness decode

LightWitnessFromSalt (stateless-core) decodes kvs+levels directly from full SaltWitness bytes via a layout-mirroring deserializer — no curve points constructed: ~1.4ms vs ~110ms wall (~700x CPU) on a 6.3MiB mainnet witness. Locked by byte-exact tests against every mainnet fixture and the committed test_data/mainnet/bench/6906405.zst. get_witness_light / decode_witness_payload_light in stateless-common.

debug-trace-server

Both RPC witness paths switch to the light decode (the server never verifies proofs): uncached trace requests lose ~100ms on large witnesses; chain-sync prefetch stops burning ~1 core·s/block. Note: the witness_generator size metric now records a documented lower bound (excludes parent commitments), shifting the histogram down ~40-60%. Unused salt dep dropped.

Verification

267 workspace tests green (incl. new set-cover/judge/merge/R2/light-decode units and a fixture integration test replaying all test_data/mainnet blocks with gas/receipts/bloom equality). Validated on mainnet: 241k blocks replayed with zero sanity failures; branches coverage 47.90% for the selected 45-block set vs 32.63% from tip-only traffic.

flyq and others added 30 commits July 8, 2026 20:50
New workspace member bin/coverage-replayer: replays mainnet blocks against
mega-evm with branch-granular coverage instrumentation and maintains the
minimal block set that maximizes coverage.

- backfill: RPC fetch -> spool -> resident worker pool (per-block counter
  reset/capture via LLVM profiler runtime FFI) -> non-zero-counter bitmap ->
  pattern dedup + representative archiving (redb store)
- set-cover: greedy minimal block set with incumbent-biased tie-breaking
- report: llvm-cov summary for the selected set (only place llvm-cov runs)
- executes via stateless-core replay_block over LightWitness; skips witness
  verification/state-root update; free gas/receipts/bloom sanity checks
- [profile.coverage] added for instrumented builds; zero source changes to
  existing crates
…evm sources

- write_atomic: concurrent fetch tasks resolving the same contract hash
  collided on the shared .tmp name; embed pid+seq so both writers succeed
- report: llvm-cov segfaults reporting over the full covmap (LLVM
  instantiation-group bug on some dependency files); restrict SOURCES to the
  mega-evm checkout auto-detected from Cargo.lock (--source-dir to override)
- set-cover/backfill: emit per-block worker timing stats (E3 measurement)
- add replay_fixtures integration test: replays all test_data/mainnet blocks
  through the worker execution path and asserts the gas/receipts/bloom sanity
  triple
- document that -C link-dead-code must NOT be used (breaks revm const-eval)

Local mainnet E2E (200 blocks): 200/200 replayed, zero sanity failures,
worker avg 136ms/block, 76 patterns / 1522 branch counters, set-cover picks
8 blocks, llvm-cov branch coverage 29.87% for tip-only traffic.
Execution-only consumers (debug-trace-server, coverage-replayer) read state
from a witness but never verify its proof — yet decoding a SaltWitness pays
one Element::from_bytes (modular sqrt + subgroup check) per parent commitment
plus the IPA proof parse: ~110ms wall / ~1 core-second per large mainnet
witness even with salt's parallel validation, all for output that is
discarded.

stateless-core: add LightWitnessFromSalt, a serde adapter that decodes
LightWitness DIRECTLY from full SaltWitness bytes. Layout-mirror types keep
the deserializer byte-synchronized while commitments (NodeId -> [u8;32]) and
the IPA proof (Vec<u8>) are consumed as raw bytes and dropped — no curve
point is ever constructed. ~1.4ms single-threaded for the same witness
(~80x wall, ~700x CPU). Mirror congruence is locked by tests against all
mainnet fixture witnesses (byte-exact stream consumption, config-agnostic,
and a corrupt-point stream that must fail the full decode but pass light).

stateless-common: decode_witness_payload_light / decode_witness_response_light
(the latter also returns the raw compressed payload for archival), plus
RpcClient::get_witness_light{,_with_deadline}. Includes an end-to-end test
light-decoding the committed real-mainnet payload
test_data/mainnet/bench/6906405.zst.

coverage-replayer: the backfill fetcher now uses get_witness_light — no EC
work per block, and the spool archives the exact wire payload instead of
re-encoding one.

Also refreshes the stale "~240ms" performance notes in light_witness.rs
(pre-salt#137 numbers).
…on demand

Per review: the only consumer of a stored raw payload would have been PR
payload assembly, and E4 established the RPC serves witnesses for the full
history — archiving raw bytes that are re-fetchable on demand is waste.

- get_witness_light / decode_witness_response_light no longer return the raw
  compressed payload; the light fetch is now strictly (LightWitness,
  MptWitness)
- SpoolEntry loses witness_payload: spool and archive hold only what replay
  needs (block JSON + light witness + code hashes); full witnesses are never
  written to disk
- PR payload assembly (M3) will fetch full witnesses for the few selected
  blocks via get_witness at build time
Opens the store without the binary-id namespace check (read-only) so data
produced on another machine/build can be analyzed locally: block status and
worker-timing stats, pattern/hit-count distribution, counter-rarity, and the
current manifest summary.
…es archival

Server data showed the archive problem was profraw, not entries (1.8G vs
8.2M for 391 patterns): a raw profraw carries the whole binary's counter
array plus an incompressible (already zlib'd) name table, so plain zstd only
managed 2.3x. Promotion now converts to single-block sparse profdata
(llvm-profdata merge -sparse drops every zero-count function and its name
entry) + zstd: ~106KB per pattern, ~45x smaller, produced off the judge's
critical path (JoinSet of blocking tasks, drained before exit). report
inflates the profdata files and merges them as before — identical output.

Entries archival is removed entirely per review: representatives'
block/witness data is re-fetched from the RPC by block number (recorded in
the store) when a resweep or PR payload needs it. Nothing block-sized is
retained anywhere.
…ain prune

101k-block run data: patterns grow ~650 per 10k blocks with no saturation,
but 93% are strict subsets of an existing pattern — mathematically unable to
improve the minimal cover. Promotion now records dominated patterns' bitmaps
(dedup/stats) but skips the profile archive; set-cover excludes dominated
patterns from candidates (a dominated pattern could otherwise win a gain
tie-break and select a block with no archived profile) and deletes profiles
of patterns dominated after the fact. Archive steady state drops from
linear growth (~880MB per 100k blocks) to the antichain (~60MB, bounded).
…ives, profile-by-pattern

Prerequisites for the full-history backfill so it need not be redone when the
resident run mode lands:

- binary_id is now a fingerprint of the instrumented mega-evm build (locked
  rev from Cargo.lock + rustc version, captured by build.rs) instead of a
  whole-exe hash. Editing the dispatcher / adding resident mode no longer
  invalidates an existing store; only a real mega-evm or toolchain change
  does — exactly when counter ids actually shift.
- A pattern's representative is now the lightest (min replay time) block seen
  for it, re-homed whenever a lighter one appears — the best fixture
  candidate, and correct for size-based exclusion.
- Archived sparse profiles are keyed by pattern, not block, so re-homing the
  representative never moves or orphans a profile. report looks them up via
  the manifest's pattern field.

Note: the schema change (PatternRecord.representative_elapsed_ms) and new
binary_id mean pre-existing stores are not readable — the full-history scan
starts from a fresh data-dir anyway.
Full history (~20M blocks) is ~6 days single-machine for the real segments
plus ~2 weeks for the 6M-7M stress range (~19s/block). The merge subcommand
combines per-shard stores (same instrumented binary, disjoint ranges) so 4
machines finish in a few days.

Correctness: a pattern bitmap uses each shard's own dense indexing (counter
first-seen order differs per shard), so bitmaps cannot be OR'd directly.
merge remaps every bitmap via source-dense -> content-addressed counter id ->
unified-dense, then folds patterns by key (FxHash of sorted counter ids,
machine-stable). Unit tests lock the reversed-dense-order case and shard-order
independence; an E2E check confirms merge(2 shards) yields the same universe
as one sequential run over the union.

Also determined and documented: per-block coverage is deterministic, but
distinct-pattern count wobbles +-1-2 under multi-worker/sharding because of
mega-evm per-process caches whose hit/miss branch depends on worker warmth.
Universe (coverage) and minimal-cover size are INVARIANT across
1-worker/4-worker/sharded (1454 in 5 independent runs), so multi-worker
production is correct.

Prereqs also landed: store.write_bulk, BitSet::iter_ones, StoreSnapshot: Clone
Port the R2 witness primitives (keys / sigv4 / endpoint / client) from
mega-reth c290c3e into a dedicated leaf crate, replacing the git-rev
dependency; mega-reth will delete its copy and consume this one via git
tag, making the cross-repo dependency one-way.

- add keys::block_object_key as the single primary-key template
  (object_keys delegates, the validator's R2 source calls it directly)
- add golden wire-vector tests: a real migrated mainnet object key and
  a full SigV4 header set computed with an independent implementation
- unify the workspace on reqwest 0.12 (0.13 was only used by
  debug-trace-server dev-deps); stateless-r2 keeps an explicit 0.12 pin
  since &reqwest::Client is part of its public API
- r2_witness: carry Bytes end-to-end (no multi-MB copy per block),
  dedicated DecodePanicked variant, module-level retry consts, drop the
  unused deadline parameter and the per-fetch debug header log
- docs: neutral witness-source wording; R2 is a first-class source

Co-Authored-By: Claude Fable 5 <[email protected]>
… worker timeout, fail-stop judge

Full-scan policy per review: every block must eventually execute; a stuck or
failing block must be loudly visible, never silently skipped.

- fetch: infinite retry with 5s backoff and per-attempt WARN (was: warn+skip)
- worker: --block-timeout-secs replaced by --slow-block-warn-secs; a long
  block only logs 'still executing' periodically and is never killed; a
  crashed worker respawns and retries the SAME block indefinitely
- judge: replay error or sanity divergence records the block (spool kept for
  forensics) and ABORTS the whole run — with every block independently
  verified to replay cleanly, any failure is an infrastructure/chain-spec bug
  and scanning past it would leave a silent coverage gap. Replaces the
  20-consecutive-divergent streak heuristic.
- work list now skips only status=Ok blocks: Error/Divergent records are
  retried automatically on re-run (previously permanently excluded)
- Record R2 witness fetch metrics: fetch+decode duration histogram, GET
  retry counter, surfaced-error counter labelled by kind, and the same
  witness-size breakdown the RPC path reports (pre-registered at startup
  like the RPC method counters).
- Add an end-to-end happy-path test: a fixture witness encoded with
  encode_witness_payload served over the mock R2 server must decode back
  to the original tuple in exactly one GET (mock now serves byte bodies).
- Warn at startup when --witness-endpoint is set but ignored because
  --witness-source r2 is active.
- Fix rustdoc intra-doc links introduced by this branch.

Co-Authored-By: Claude Fable 5 <[email protected]>
The trace server never verifies witness proofs (it executes from LightWitness
and trusts its data sources), yet both RPC paths did the full SaltWitness
decode — one Element::from_bytes (modular sqrt + subgroup check) per parent
commitment, ~100ms wall / ~1 core-second on large witnesses — and then threw
the proof away via LightWitness::from.

- DataProvider::fetch_witness (stateless mode, on-demand): now
  get_witness_light_with_deadline; the Step-3 conversion stage is gone, so
  uncached trace requests lose ~100ms of latency on large witnesses
- TraceFetcher::fetch (local cache mode, chain sync): now get_witness_light;
  prefetch stops burning ~1 core-second of EC work per block
- WitnessSizeBreakdown::new_light for the size metric on light paths: a
  documented lower bound (parent commitments are never materialized, so their
  contribution is unknowable without the full decode)

The DB-cache read path already stored/loaded LightWitness directly and is
unchanged. All 94 debug-trace-server + stateless-common tests pass (the
integration suites exercise the full mock-RPC fetch -> light decode -> trace
pipeline).
Run outputs (store.redb, codes cache, manifests) are transferred as tarballs
over jumpserver from now on — far too large for git. The upcoming full-history
scan starts fresh anyway.
--witness-source r2 fetches each block's witness object straight from the R2
bucket (SigV4-signed GET via stateless-r2 primitives, same key layout as the
uploaders) and decodes it with LightWitnessFromSalt — zero elliptic-curve
work, unlike the validator's R2 path which needs the full decode. block /
bytecode / tip queries stay on --rpc-endpoint; --witness-endpoint is only
required for (and only used by) the rpc source.

The client deliberately has no internal retry: the backfill fetcher's
retry-forever loop (WARN + 5s, blocks are never skipped) is the retry policy,
and its flat 5s cadence is already gentler than a backoff ladder's early
rounds. Errors carry status/key/body context for the log.

Mirrors the validator's flag names with the COVERAGE_REPLAYER_ env prefix.
Tests: mock-R2 happy path (fixture payload light-decodes to the same light
parts as the full decode), status surfacing, corrupt-body decode error.

Also drops committed run artifacts (backfill.log, eras.log, bin.pid) and
gitignores *.log / *.pid.
Correctness / operational (the four-machine full-history scan depends on
these):
- judge: a new pattern's sparse profile is now archived BEFORE the pattern +
  Ok record are committed (and synchronously, replacing the fire-and-forget
  JoinSet). A crash between the two previously orphaned the pattern forever:
  the block was Ok so never retried, later same-bitmap profraws were deleted,
  and report hard-failed on the missing profile. Archive failure is now
  fail-stop; the spool entry is deleted only after the commit.
- binary_id: fingerprint now uses `rustc -vV` (release + host + LLVM
  version) instead of `rustc --version`, which carried neither the target
  triple nor the LLVM version — macOS and Linux builds previously shared an
  id, so merge would happily combine cross-target stores whose counter ids
  may differ. A missing mega-evm git rev in Cargo.lock (path-dep override) is
  now a hard build error instead of a silent "unknown" namespace collapse.
- store namespace now also stamps and enforces --symbol-filter (backfill and
  merge; the filter defines the counter universe, so mixing filters silently
  blended incompatible universes). merge requires all shards to agree.
- pattern keying extracted to store::pattern_base_key, shared by the judge
  and merge — the byte-for-byte mirror contract is now structural, not
  copy-paste.
- fetch retry rounds reuse the already-downloaded block (a witness-side
  retry loop no longer re-downloads the full block every 5s).
- --workers 0 now fails validation instead of hitting tokio's channel assert.
- R2 secret access key wrapped in a Debug-redacting type (same hardening as
  the validator's RedactedSecret).
- merge: deterministic dense assignment (sorted per shard), the per-pattern
  reverse-map rebuild removed (was O(patterns x total_counters)), collision
  re-key warning now covers the wrong-content rsync-union case, and the
  module docs no longer claim byte-identical output.
- archive_sparse_profile cleans up its tmp profdata on failure paths.

Tests (previously untested algorithm cores):
- set-cover core extracted as a pure function (fs side effects stay in run)
  with 5 tests: full coverage, dominated pruning, equal-bits survival,
  incumbent tie-break, redundancy elimination of a covered first pick.
- judge tests on a real temp store: known-pattern dedup + lightest-block
  re-homing, dominated-new-pattern (bitmap recorded, no archive), fail-stop
  on replay error / sanity divergence, and the shared keying contract.
- WitnessSizeBreakdown::new_light lower-bound test (gap == commitments term).

Docs / conventions:
- README + AGENTS.md: three binaries, coverage-replayer rows added.
- debug-trace-server: unused salt dependency removed.
- .gitignore: stray unanchored archive/codes/tmp entries removed.
- stale doc claims fixed (rpc_client light-metrics rationale, inspect
  'never writes', crate doc pointer to a gitignored file, R2 'full history'
  comment now warns about bucket lifecycle retention).

Note: the binary_id change invalidates pre-existing stores (none in
production — the full scan starts fresh).
@flyq flyq changed the title Liquan/coverage replayer design feat: coverage-replayer — minimal mainnet block set maximizing mega-evm branch coverage Jul 11, 2026
…ilds

Under --no-default-features `std` is the `alloc` alias and the prelude has
no `vec!`; cargo test -p stateless-core --no-default-features --lib failed
to compile the corrupt-point test. Explicit `use std::vec;`, same as
chain_spec.rs.
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.15789% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.2%. Comparing base (01613af) to head (6bf71fb).

Files with missing lines Patch % Lines
crates/stateless-core/src/data_types.rs 0.0% 6 Missing ⚠️
crates/stateless-core/src/light_witness.rs 95.7% 3 Missing ⚠️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

flyq and others added 6 commits July 11, 2026 15:16
…-loop wins

Reuse:
- pattern probing walk extracted to store::resolve_pattern_slot (with
  PROBE_STEP moved beside pattern_base_key): the judge and merge now share
  the byte-identical keying + probing mechanically instead of by comment;
  merge tests key via pattern_base_key instead of hand-rolled FxHasher
- strict-domination predicate unified as PatternRecord::dominates (was
  triplicated; the load-bearing 'bits >, never >=' now has one definition)
- inspect's antichain estimate + greedy preview replaced by one
  setcover::select_cover call — the preview can no longer drift from a real
  set-cover run (it previously omitted pruning and redundancy elimination)
- sorted-dedup code-hash collection hoisted to
  stateless_core::collect_code_hashes (spool + trace-server copies deleted)
- elapsed p50/p95 summary triplication -> store::elapsed_stats
- report locates the mega-evm checkout from the compile-time rev
  (COVERAGE_MEGA_EVM_REV) instead of re-parsing ./Cargo.lock at runtime —
  no cwd dependence, cannot disagree with the instrumented build
- rpc_client: fetch_witness_raw / fetch_witness_light collapse into one
  generic fetch_witness_with

Simplification:
- judge ingest: one block_record constructor for the three status paths and
  a single commit/cleanup tail (archive-before-commit invariant unchanged)
- select_cover applies redundancy elimination internally; callers no longer
  filter selected by redundant_removed
- Store::load / write_bulk table-loop triplication -> generic
  read_table/write_table
- dead code removed: SpoolEntry.block_hash (never read), owned
  From<SaltWitness> for LightWitness (no callers), BitSet::words (callers
  now use iter_ones); from_salt_witness module made private (newtype is the
  only consumer); current_binary_id no longer returns Result
- DataDir::new is pure path arithmetic; writers call ensure_layout() —
  inspect / merge shard inputs no longer scaffold empty dirs in foreign paths

Efficiency:
- BitSet::is_subset_of short-circuits on the first disproving word — the
  inner kernel of every dominance scan (~40x on the common non-subset case)
- merge remap uses flat per-shard dense->id/unified tables (one array index
  per set bit instead of two hash lookups; billions of bits at full scale)
- inspect counter-rarity uses a dense Vec instead of a HashMap

267 workspace tests green (incl. no_std lib tests); instrumented two-shard
E2E (backfill x2 -> merge -> set-cover -> report -> inspect) verified on
mainnet blocks.
…fore exit

Addresses the two open Codex review findings on this PR:

- R2WitnessClient now enforces --witness-max-concurrent-requests with its
  own semaphore (permit scoped per GET attempt, like the RPC path's), so
  the documented knob for bounding witness I/O works in R2 mode instead
  of being silently ignored. Queue wait on the cap is excluded from the
  fetch-duration histogram so it cannot masquerade as R2 latency.
  Concurrency-probe test asserts the in-flight high-water mark.

- run_with_signals sends one final validation report after the pipeline
  (and any signal drain) has fully stopped. The periodic reporter is
  cancelled the instant the pipeline completes, so an --end-block slice
  run could previously finish without ever reporting its tail upstream
  (no later restart re-reports it). The reporting round is extracted
  into report_range_once, shared by the tick loop and the final flush.
  New end-to-end regression test drives run_with_signals to a sync
  target and asserts the mock upstream saw the final tip; both new
  tests were mutation-tested against the reverted fixes.

Co-Authored-By: Claude Fable 5 <[email protected]>
The final shutdown report inherited the periodic reporter's best-effort
semantics (log + skip), but it has no next tick behind it: a transient
endpoint blip at exactly shutdown would permanently lose an --end-block
slice run's tail report while the process exits 0. report_range_once now
returns whether the round settled, and the final flush retries up to 3
attempts (1s apart) before giving up with an ERROR log; a detected
validation gap stays non-retried. Regression test fails the first report
call via the mock and asserts the retry lands the tail (mutation-tested
against FINAL_REPORT_ATTEMPTS = 1).

Also fixes both pre-existing rustdoc warnings: re-export ValidationTask
(it is ValidatorFetcher's public Output type) and backtick the
<metrics-port> placeholder that rustdoc read as an unclosed HTML tag.

Co-Authored-By: Claude Fable 5 <[email protected]>
Comment-only pass over PR #152: compress multi-sentence narratives to the
minimal statement of the constraint, keep every load-bearing "why" (throttle
rationale, redirect policy, wire-format contracts, golden-vector docs), and
give each rationale a single home instead of restating it at every call site.
No code changes.

Co-Authored-By: Claude Fable 5 <[email protected]>
Round 1 (review findings):
- write_atomic: fsync before rename (+ best-effort dir fsync), tmp cleanup on
  failure — the archive-before-commit invariant now survives power loss, not
  just process crashes; stale *.tmp swept at backfill startup
- fetch_block: decode-validate existing spool entries (delete + refetch on
  corruption) and re-resolve missing codes, so no on-disk artifact can wedge
  the run across restarts
- worker stdout: stray library prints no longer kill the worker (deterministic
  prints wedged the block forever); repeated crashes on one block escalate to
  error-level logs every 10 attempts
- light_witness: metadata mapped to None is a decode error, not unreachable!
  (witness bytes are unvalidated network input on the light path)
- judge: first_block min-folded on the occupied path (matches merge); elapsed
  samples bounded by a deterministic decimating reservoir; BLOCKS table loaded
  only for the scanned range (full-history stores hold tens of millions of rows)
- store: schema_version enforced on open/open_readonly; tests for the probe
  collision walk and namespace rejections
- set-cover: refuses a missing store (no more silent 0-block manifests);
  redundancy-eliminated picks actually logged; completeness contract documented
  (full-universe coverage always; minimality never at its expense)
- inspect: last growth bucket no longer drops patterns on exact-multiple spans
- merge: BlockRecord.pattern_key rewritten through re-keyed patterns
- misc: unused deps dropped, rebuild hint includes --target, R2 secret via CLI
  warns about process-list visibility, AGENTS.md test section corrected

Round 2 (adversarial verification of round 1):
- sweep runs only after Store::open's exclusive lock and skips files younger
  than 1h (a doomed double-start could delete a live writer's tmp files)
- response frames are salvage-parsed out of dirty stdout lines (an
  unterminated print! glued to the frame previously hung the round-trip)
- code files are content-verified (keccak) and refetched on mismatch — a
  truncated code file could previously fail-stop the run on every restart
- spool validation mirrors the worker's checks (block_json parses, number
  matches) and spool files carry a zstd content checksum, so any byte-level
  corruption routes into delete-and-refetch
- slow-block warning stays factual when non-protocol output was seen (no
  destructive restart advice for merely slow blocks)

Verified: 282 workspace tests, clippy clean, instrumented E2E (two-shard
backfill/merge/set-cover/report/inspect; planted corrupt spool and corrupt
code files heal in-run; full-universe cover 772/772).
flyq added a commit that referenced this pull request Jul 17, 2026
…erf docs

Review follow-ups on this PR:

- estimate_witness_size removed: its only caller was the data-provider
  path this PR migrates to WitnessSizeBreakdown::new_light; no other
  in-workspace or mega-reth usage exists.
- Doc comments no longer pre-reference the coverage-replayer (it lands
  with PR #153); consumers are named generically.
- Perf numbers in docs recalibrated to a reproducible measurement on the
  committed 6.3MiB mainnet payload (14-core M4 Pro, median of 20): full
  decode ~112ms wall / ~1.4 core.s CPU, light ~3.7ms single-threaded
  (bincode stage); payload-level incl. zstd: 124.8ms vs 6.0ms.

Co-Authored-By: Claude Fable 5 <[email protected]>
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.

1 participant