feat: add --json flag for machine-readable output - #215
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Interrupted runs and unmatched contract filters can produce incorrect documented JSON statuses.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds machine-readable JSON output for automated Diffyscan consumers.
Changes:
- Adds
--json/-J, structured reports, and suppressed stdout logging. - Exposes comparison details and previously swallowed contract errors.
- Documents and tests the JSON schema.
File summaries
| File | Description |
|---|---|
diffyscan/diffyscan.py |
Implements JSON reporting and error aggregation. |
diffyscan/utils/logger.py |
Adds stdout suppression. |
diffyscan/utils/binary_verifier.py |
Routes output through the logger. |
tests/test_json_output.py |
Tests JSON generation and CLI behavior. |
tests/test_diffyscan_allowlist_runtime.py |
Updates expected error metadata. |
docs/json-output.md |
Specifies the JSON schema. |
docs/cli.md |
Documents the new flag. |
docs/how-to.md |
Adds automation guidance. |
README.md |
Links the JSON documentation. |
CLAUDE.md |
Adds a machine-readable command example. |
.claude/skills/debug-diff/SKILL.md |
Recommends JSON for automated debugging. |
Review details
- Files reviewed: 11/11 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Codex: Two findings at 8e9a74c:
Validation: 397 tests passed. Both findings were reproduced offline; no live explorer or RPC checks were run. |
|
Claude: What ranAll runs used the PR head with real Etherscan, GitHub and RPC credentials from this workspace. Secrets are replaced with
Note for anyone repeating the SIGINT test: a background job started from a non-interactive bash script has SIGINT ignored, so Code claims in Findings1. Credentials leak into
|
|
Codex Both original findings are fixed: completed results survive later fatal errors, and opcode diff rows reach the log in JSON mode. Both reproductions now pass. Three additional issues from the discussion were independently confirmed:
Validation: 401 tests passed, plus offline reproductions. No live explorer or RPC checks were run. |
…eport setup interrupts in --json
c773c14 to
a455579
Compare
TheDZhon
left a comment
There was a problem hiding this comment.
LGTM!
Still open, all non-blocking:
- The PR body still says the exit code is unchanged, and the exit-status paragraph in docs/cli.md is stale. Human mode now exits 1 in four new cases: interruption, zero checked contracts, a directory without configs, and both comparisons disabled. All are improvements and should be documented.
- An any rule that suppresses a simulation error still hides the error text.
- Whole HTTP response bodies are copied into error. The Etherscan 404 put its full HTML page into every error field. Truncating the body would keep reports small.
- The contract that aborted a run is named only when the exception text happens to include its address.
- Redaction is exact string replacement, so a library that re-encodes the URL in its message would defeat it. Fine for today's alphanumeric keys.
- Earlier doc nits and the provenance suggestion stand.
Side finding unchanged. The Earn factory mainnet config is red on main because a constructor-created immutable cannot be reproduced by simulation. It needs an immutables rule for the two slots.
Summary
Adds
--json/-J: instead of the human-readable log, stdout carries a single JSON report meant for scripts and coding agents. Logs still go todigest/<ts>/logs.txt(path included in the report), prompts are skipped, and the exit code is unchanged.The format is specified in
docs/json-output.md(linked from README,docs/cli.md,CLAUDE.md, thedebug-diffskill, and--help). Highlights:status(passed | failed | error),exit_code,duration_seconds,log_file,summarycounters, flatcontractslist with aconfigfield per entrysource/bytecodewith status,facets+reasonof the matchingallowed_diffsrule, diff hunks with line ranges (only files that differ or are missing),uncoveredbytecode ranges, compile/simulation error text, and a readysuggested_rulenullor empty values are omitted, so a clean contract is just{"status": "exact", "files": 26}A fatal error (missing config, bad token) still produces a report with
status: "error"; the traceback goes to stderr.Side finding
With
fail_on_bytecode_comparison_error: false, a contract that errors out (e.g. explorer 401) was silently dropped from all stats and the run exited 0. Such errors are now recorded in the result and surface in the JSON ascontracts[].error/summary.contract_errors, turningstatusintoerror. The exit code itself is not changed here.Other changes
Logger.stdout_enabledswitch; two rawprintcalls inbinary_verifier.pyrouted through it so JSON mode stays clean_collect_config_pathsVerification
lido-earn/mainnet/earn-factory.yamlgives clean JSON and empty stderr vs 422 log lines;hypernative-guard/mainnet/config.yamlreports 4 contracts allowed viaimmutablesin 86 linescodex exec review --uncommitted: no actionable bugs found