Skip to content

Phase EJ Step 2: Mechanical null-diff screen #283

Description

@aberson

Build step 2 of 8 — Phase EJ (umbrella #281)

Build doc: documentation/plans/evolve-judging-plan.md §7 Step EJ.2

What to build

A dev-apply sub-agent run that makes zero .py edits is treated as SUCCESS (if not validate or not changed_py: return, src/orchestrator/evolve_dev_apply.py L309-313) — the byte-identical candidate then burns ~4 games and passes fitness ~50% of the time.

Behind --screen-null-diff:

  • detect null diffs (empty changed_py OR every changed file AST-equivalent to its before-snapshot — comment/formatting-only edits must count as null);
  • route them through the existing retry-with-feedback machinery (L325-339) with a "no semantic change" feedback message;
  • on final-attempt null, raise DevApplyNullDiffError.

In scripts/evolve.py, catch it (both the serial path and the --concurrency>1 worker path via scripts/evolve_worker.py) and emit a phase: "fitness", outcome: "screen-null-diff" results row WITHOUT playing games and WITHOUT incrementing retry_count (do NOT reuse the crash handler at L2932-2958 unchanged — it evicts and increments); the imp returns to active; evict after 2 consecutive null-diffs via a new additive PerItemState.consecutive_null_diffs field. Verify whether frontend/src/hooks/useEvolveRun.ts enumerates outcome strings; bump cacheKey if so.

Existing context

  • Zero-edit-as-success at evolve_dev_apply.py L309-313; changed_py = sorted(_diff_py_snapshots(py_before, py_after)) is a content-snapshot diff (L300), so comment-only edits count as "changed" today — AST-equivalence (not just zero-edit) is load-bearing or the screen is trivially evaded.
  • Existing retry-with-feedback machinery at L325-339 is reusable.
  • Crash handler (scripts/evolve.py L2932-2958) flips the imp to _EVICTED and increments retry_count — the null-diff outcome must NOT reuse this path.
  • AST-equivalence edge case: docstrings are AST-visible (ast keeps docstrings) → docstring-only edits classify as real changes. Accepted — docstring-only edits are near-null but rare; zero-edit + comment/format-only are the observed failure modes.
  • Risk (flagged): scripts/evolve_worker.py error-propagation shape is unknown — read it first at build time; DevApplyNullDiffError could surface as a generic crash at --concurrency>1 → wrong eviction path. Explicit worker-path test required in Done-when.

Files to modify/create

  • src/orchestrator/evolve_dev_apply.py (DevApplyNullDiffError + AST-equivalence check)
  • scripts/evolve.py (--screen-null-diff flag + outcome handling; catch error, emit row, no retry increment)
  • scripts/evolve_worker.py (propagate the error type across the concurrency>1 worker boundary)
  • PerItemState.consecutive_null_diffs (additive optional field, default 0)
  • frontend/src/hooks/useEvolveRun.ts (possibly — cacheKey bump only if it enumerates outcome strings strictly)
  • tests (unit + integration)

Done when

Unit tests cover: zero-edit → retry feedback → error; comment-only edit classified null; real edit passes through unchanged; screen outcome does not increment retry_count; 2nd consecutive null evicts; flag OFF → today's silent-success path byte-identical; worker path propagates the error type. Integration test drives the scripts/evolve.py loop (fake run_batch/claude fns, real dispatch code) with the flag on and asserts a null-diff imp produces a screen-null-diff row and zero games. uv run pytest / mypy src bots --strict / ruff check . clean; test count ≥ baseline.

Flags (recommended)

--reviewers code (backend + tests; any useEvolveRun.ts change is a one-line cacheKey bump with no rendered-UI surface to review)

Depends on

none

Parallel-safe with

none — strictly sequential (edits scripts/evolve.py argparse+loop shared with sibling EJ steps; plan §4 mandates serial execution)

Produces

DevApplyNullDiffError + AST-equivalence check in evolve_dev_apply.py; --screen-null-diff flag + outcome handling in scripts/evolve.py + worker propagation in scripts/evolve_worker.py; PerItemState.consecutive_null_diffs; tests

Operator workflow notes (autonomous + UI-bundle + parallel)

  • Runs end-to-end via /build-step; the Done when is the gate.
  • Read scripts/evolve_worker.py serialization shape FIRST (flagged risk).
  • Serial: shared scripts/evolve.py.

Synced from documentation/plans/evolve-judging-plan.md by /repo-sync at c6099d9

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions