feat: high-signal comparison output with expected-difference rules - #24
Merged
Conversation
Make `runner compare` produce smaller, higher-signal output and stay robust against flaky/throttling endpoints. - comparison rules: a unified `comparison.rules` list declares expected differences (ignore paths with `[*]` wildcards, numeric_tolerance for hex quantities, error_code_only / error_presence_only). eth_estimateGas gets a built-in 10% relative tolerance by default. - error classification: bucket namespace_disabled / no_state / range_cap so capability errors are filtered as config, not correctness. - retry + non-fatal transport: bounded retry with exponential backoff for transport errors and 5xx (honors clients.yaml max_retries and new --max-retries / --retry-base-delay); a final transport failure is recorded per-call instead of discarding the whole run. - lean output: --diff-only, --omit-matching-responses, --max-response-bytes gate what results.json and the HTML report serialize. - summary + CI gate: end-of-run category tally and --fail-on-diff exit code. - sync-gap awareness: --skip-above-head skips calls pinned above the lowest client head. - block pinning: `block_override` / --block-override rewrites latest/pending tags to a static block and appends a block arg to calls that omit one. - corpus helper: --from-jsonl / --sample builds a config from rpc-calls/*.jsonl with deterministic sampling and proof/head-dependent/debug exclusions. - provenance: the effective config is written to a comparison-provenance.json sidecar and surfaced in the report. All new config blocks and flags are optional; defaults reproduce the prior behavior aside from the additive retry.
Bump the builder to golang:1.25.12-alpine (fixes the Go stdlib os.Root symlink CVE in the runner binary) and add a documented .trivyignore for the same CVE in the upstream grafana/k6 base binary, which we cannot recompile until grafana/k6 ships a Go >=1.26.5 build.
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.
Summary
Implements the
COMPARISON-IMPROVEMENTS.mdhandoff brief in full, motivated by a real archive-vs-archive Nethermind correctness run (v1.39.1 vs v1.40) where three problems dominated: expected differences (e.g.totalDifficulty, sub-1%eth_estimateGasdrift) flooded the report, a single transport blip discarded an 800-call run, and the report was unopenable (~769 MB HTML).Everything is backward compatible: new config blocks and flags are optional, and defaults reproduce prior behavior aside from the additive retry.
What's included
comparison.ruleslist:ignore(with[*]array wildcards),numeric_tolerance(abs/rel on hex quantities),error_code_only,error_presence_only.eth_estimateGasgets a built-in 10% relative tolerance by default (diffs ≤10% treated as correct).namespace_disabled/no_state/range_capso capability errors are filtered as config, not correctness.clients.yamlmax_retries, plus--max-retries/--retry-base-delay); a final transport failure is recorded per-call instead of aborting the whole run.--diff-only,--omit-matching-responses,--max-response-bytesgate whatcomparison-results.jsonand the HTML serialize.--fail-on-diffexit code.--skip-above-headskips calls pinned above the lowest client head.block_override/--block-overriderewriteslatest/pendingto a static block and appends a block arg to calls that omit one.--from-jsonl/--samplebuilds a config fromrpc-calls/*.jsonlwith deterministic sampling and proof/head-dependent/debug exclusions.comparison-provenance.jsonsidecar and surfaced in the report.Files
New:
runner/comparator/{rules,block_override,corpus}.go(+ tests). Modified: diff engine (threads a rules context), orchestrator, transport, config loader, report, and both CLI commands. Documentedcomparison:example added toconfig/compare/example.yaml.Testing
go build ./...,go vet ./...clean; comparator unit + integration tests pass (tolerance boundaries, ignore[*], error rules, classification, block-override rewriting, corpus sampling, retry-recovers, non-fatal transport, diff-only serialization).--diff-onlykept only the real mismatch, the estimateGas drift was absorbed by the tolerance, provenance sidecar written, HTML small,--fail-on-diffexited non-zero.Notes for review
block_override/--from-jsonlarecompare-only (they live in the compare YAML); retry,--diff-only, summary,--fail-on-diff,--skip-above-headapply to bothcompareandcompare-openrpc.Clients[0]) transport-fails on a call, the reference falls back to the first client that answered.