evolve: skip no-op generations, guard re-scores, cap run budgets - #1621
Open
Evanfeenstra wants to merge 1 commit into
Open
evolve: skip no-op generations, guard re-scores, cap run budgets#1621Evanfeenstra wants to merge 1 commit into
Evanfeenstra wants to merge 1 commit into
Conversation
Observed live (gaia-evolve run 1788061734710, prod): generation 5's author degenerated into a filler loop and published nothing, echoing a garbage version string. The version fallback correctly refused the echo — and fell through to the candidate's ACTIVE version, which was generation 4's own publish. The harness then spent ~2.5h and ~$8 re-running an already-graded version over all 25 tasks. Because exact-match fitness is resampled, that re-run was on track to land above its own recorded 0.76 by luck and be written into the hill-climb as a new best. - gaia/harvey evolve-gen: a `published` gate (`vbefore` read before the author vs. the resolved version after) skips `candeval` when nothing was published; `result` fans in on the gate so it reports `noop: true` on that branch. Costs one author, not a whole task set. - eval/evolve-loop: no-op generations record no fitness (a 0 would libel an approach never tried) and are named as such in the next briefing; a version already scored in this run cannot become the best on a luckier resample (`isNewBest` + `scored` ledger, live and journal-replay paths). - eval/evolve-loop: `maxCost` / `maxMinutes`, checked between generations, null by default. Generation count is a poor budget — authors evolve toward more expensive architectures, so the observed run drifted from ~1h/gen to ~2.7h/gen. tsc clean; gaia + harvey evolve smokes pass, with new cases for the gate expression, the no-op branch, the re-score guard, and the caps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
What went wrong
Reviewing gaia-evolve run
1788061734710(prod, still running at 11h): generation 5's author degenerated into a filler loop — 56KB of</br>proceed.</br>meta_run_step</br>and zero-width spaces — and published nothing, echoing garbage into its structured output:The
vpin.version || vactive.versionfallback did its job and refused the garbage echo. But it fell through to the candidate's active version — which was generation 4's own publish,v11. So generation 5 spent ~2.5h and ~$8 re-running an already-graded version over all 25 tasks.Worse: exact-match fitness is resampled. That re-run of v11 was 13/13 at the time of review, against v11's recorded 0.76.
evolve-loophas no duplicate-version check, so it was on track to writegen 5, v11, improvedinto the lineage — produce-sampling noise recorded as a hill-climb step, and a final report naming a best version at an inflated accuracy.#1620 hardened the summary channel of this same author failure. This is the version/grading side of it, plus the budget problem the run exposed.
Fixes
1. Skip, don't re-score —
gaia-evolve-gen.yaml,harvey-evolve-gen.yamlA new
vbeforestep reads the candidate's active version before the author runs (with an ordering edge intoauthorso it can't race the publish). Apublishedif-gate compares it to the resolved version afterward; when they match, nothing was published, andcandevalis skipped entirely.resultfans in on the gate as well as the digest — vein only skip-propagates when every dep was skipped — so it still reports, carryingnoop: true.A no-op generation now costs one author instead of a full task set. At generation 0 the candidate doesn't exist yet, so
vbefore.versionis undefined and any real publish trips the gate true.2. Guard the best-update —
eval/evolve-loop.tsNO CANDIDATE PUBLISHEDinstead.isNewBest+ ascoredledger: a version this run already graded cannot become the best on a second, luckier sample. The gate above stops the common cause, but a deliberate republish of identical YAML under a new version string is indistinguishable from the loop — this is the backstop, and it pins the reported best to the run where that version was first measured. Applied on both the live and journal-replay paths. The resample is still reported honestly, flaggedrescoredVersion.3. Budget caps —
maxCost/maxMinutesChecked between generations, so they bound when the loop stops and never kill a generation mid-flight. Both null (uncapped) by default, wired through
gaia-evolve/harvey-evolveparams and overridable per run.Generation count alone is a poor budget: authors reliably evolve toward more expensive architectures (dual independent attempts + a reconciler, by gen 3). The observed run drifted from ~1h/gen to ~2.7h/gen and $16 to $32 cumulative — a "10 generation" run headed for ~22h and ~$65.
Testing
tscclean. Both evolve smokes pass, with new cases for:publishedgate expression across all four version states, including the exact live failure (vbefore v11/ bogus pin /vactive v11→ no-op) and undefined-safety againstmeta/get-workflow's{ error }returnbestuntouched, only the author budget spent, briefing wordingbestFitnessat 0.6maxCoststopping between generations, and absent caps changing nothing (incl.nullparams parsing as uncapped)Note on the live run
The in-flight run is on the old code and safe to let finish — v10/v11 are real gains over the 0.68 baseline. But distrust any "best" it reports at v11 from generation 5 onward; that's the resample, not a climb. Held-out validation before promoting was already the standing rule here and still applies.