feat: k-branch parallel exploration (closes #14) - #15
Conversation
Same-round k-branch parallel exploration. Governor.run_once_parallel launches k independent worktrees per round, each running plan → execute → evaluate; the highest-fitness survivor is promoted to evolution/accepted, the rest are recorded under ledger/failed/. - config.parallel.k_branches (default 1); k=1 delegates to run_once - evaluator role emits float fitness; back-compat synthesizes fitness from hard_gates_passed when an older evaluator omits it - per-round cost/tokens are summed across all k branches for hard-stop bookkeeping - 13 new tests (67 total, 54 baseline preserved) covering parity at k=1, k>1 multi-worktree fan-out, fitness ranking, winner promotion, loser ledger demotion, worktree cleanup, cost aggregation, partial scope violation, all-fail no-promotion, and an end-to-end k=3 over 3 rounds CLI loop Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds k-branch parallel exploration: a new Governor.run_once_parallel runs k sibling branches per round, ranks them by a new evaluator fitness float, promotes the highest scorer to evolution/accepted, and demotes the rest into ledger/failed/. CLI dispatches to the parallel path whenever parallel.k_branches > 1 (default 1 preserves prior run_once behavior byte-for-byte). The evaluator role and prompt now emit/back-compat-synthesize fitness, and per-round cost/tokens are summed across branches for hard-stop accounting.
Changes:
- New
ParallelConfig(parallel.k_branches, default 1) with strict validation; CLI threads it through both single-run and--looppaths. - New
Governor.run_once_paralleland helpers_run_single_branch/_select_winner; evaluator role gains afitnessfield with back-compat synthesis fromhard_gates_passed. - 13 new tests in
tests/test_issue14.pyplus four fixture scripts (per-branch fitness evaluators/executors, unique-marker executor, scope-violator executor) exercising config, k=1 parity, k>1 fan-out, winner promotion, loser demotion, worktree cleanup, cost/token aggregation, partial scope violations, all-fail rounds, and a 3-round CLI integration test.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| evolution_kernel/config.py | Adds ParallelConfig dataclass and _parse_parallel with positive-int validation. |
| evolution_kernel/governor.py | Adds run_once_parallel, _run_single_branch, _select_winner; duplicates much of run_once to support per-branch decisions, fitness ranking, and aggregated cost/tokens. |
| evolution_kernel/cli.py | Dispatches to run_once_parallel when cfg.parallel.k_branches > 1 in both single-run and loop paths. |
| roles/evaluator.py | Documents new fitness field in the prompt and back-compat-derives it from hard_gates_passed when absent. |
| README.md / README.zh.md | Marks goal-evaluator and k-branch features as ✅ and documents the new parallel: config block. |
| tests/test_issue14.py | 13 tests covering config parsing, k=1 parity, k>1 fan-out, winner/loser handling, cleanup, cost summation, scope violations, all-fail rounds, and CLI loop. |
| tests/fixtures/evaluator_branch_fitness.py | Reads fitness from EVOLUTION_MARKER.txt; emits hard_gates_passed/fitness/cost/tokens. |
| tests/fixtures/evaluator_src_fitness.py | Same evaluator pattern but reads src/marker.txt for scope-violation test. |
| tests/fixtures/executor_branch.py | Writes per-run fitness hint into EVOLUTION_MARKER.txt. |
| tests/fixtures/executor_oob_for_run.py | Writes out-of-scope file only for the configured violating run_id. |
| tests/fixtures/executor_unique_marker.py | Produces unique content per run_id so each branch creates a real diff. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Governor.run_once_parallel(goal, k)launcheskindependent worktrees per round, each running plan → execute → evaluate; the highest-fitness survivor is promoted toevolution/accepted, the rest land inledger/failed/.parallel.k_branchesconfig field (default1);k=1delegates to the existingrun_onceso the v0.2 + phase-2 behavior is byte-for-byte preserved.fitness; back-compat synthesizesfitnessfromhard_gates_passedwhen an older evaluator omits it.kbranches so hard-stop bookkeeping in_run_loopstill ticks correctly.Test plan
tests/test_issue14.pycovering:k=1parity withrun_oncek>1fan-out (3 run dirs with full per-branch artifacts)evolution/acceptedfailed/<run_id>-summary.json)acceptedunchangedk=3over 3 rounds via CLI--loop→ 9 runs, 3 accepted, 6 failed summariesWhy
This is the population-level exploration primitive — the kernel can now escape local optima the way FunSearch / AlphaEvolve do: by running siblings in parallel and keeping the best.
🤖 Generated with Claude Code