| name | improve-bot-evolve |
| description | Autonomous generation-phase improvement loop. Generates a pool of Claude-proposed improvements, fitness-tests each vs the current parent, stacks the winners onto a new snapshot (with an import-check gate), and regression-checks against the prior parent — repeating for hours until the pool or wall-clock budget is exhausted. Designed for overnight unattended runs. |
/improve-bot-evolve
Autonomous generation-phase improvement loop. Every generation:
- Fitness — each active pool imp is individually snapshotted, applied, and plays the current parent for
--games-per-eval games. Strict-majority wins (≥3/5) become fitness-pass; one-short (2/5) become fitness-close (resurrection-eligible, retry cap 2); anything lower evicts immediately.
- Stack-apply + promote — if ≥1 fitness-pass imp exists, snapshot the parent into a fresh
vN+1 directory, apply every fitness-pass imp in rank order, run python -c "import bots.vN+1.bot" as a pre-regression gate, and commit the promotion as one [evo-auto] commit. Import-check failures roll back the new directory and skip regression for the generation.
- Regression — if anything was promoted, new parent plays prior parent for
--games-per-eval games. On rollback, git revert the promote commit (also under EVO_AUTO=1) and restore the pointer.
- Pool refresh — close-loss and benched-pass imps go back to active (retry cap enforced; cap=3 total evals before eviction). Claude tops up the active pool back to
--pool-size with fresh orthogonal imps targeting the new parent.
Gate-reduction note (2026-04-23): The legacy third gate — a 5-game composition batch testing the stacked candidate against the parent before promotion — was removed per evolve-gate-reduction-plan.md. Regression already catches bad-interaction stacks as "new parent loses to prior parent", and the extra Bernoulli filter dominated promotion rate without adding unique detection capability. The import-check gate that used to live inside composition migrated into the stack-apply step.
Design goal: /improve-bot-evolve in a fresh context window with no additional input should produce a measurably stronger parent version after a few hours, with every promotion recorded as an [evo-auto] commit and every regression recorded as an [evo-auto] revert.
Zero-input contract: When invoked with no flags, this skill MUST NOT ask the user any questions. It proceeds immediately with safe defaults (pool size 10, 5 games per eval, 4-hour budget, Simple64). Every phase is designed to resolve ambiguity autonomously — pick reasonable defaults, log the choice, and keep going. The only exception is if a hard pre-flight failure makes the run impossible (e.g., SC2 not installed, claude CLI missing or unauthenticated).
Relationship to /improve-bot-advised: These two skills are siblings with different inner mechanics:
/improve-bot-advised picks one improvement, applies it, validates win-rate, commits — linear.
/improve-bot-evolve generates a pool of 10, fitness-tests each vs parent, stacks winners onto a new snapshot with an import-check gate — parallel with a regression safety net.
The outer phase shape (pre-flight → seed → loop → decision → report) is identical. The two skills must NEVER run concurrently (they both mutate bots/current/current.txt). The pre-flight explicitly refuses to start if an advised run is in progress.
Flags
| Flag | Type | Default | Purpose |
|---|
--pool-size | int | 10 | Number of improvements Claude generates (and tops up to between generations) |
--games-per-eval | int | 5 | Games in each phase evaluation (fitness / regression). Pass threshold = strict majority of this count. |
--hours | float | 4.0 | Wall-clock budget. 0 disables the check (test-only). |
--concurrency | int | 1 | Number of fitness-eval workers run in parallel by scripts/evolve.py. 1 is byte-identical to the historical serial path (Decision D-1 in documentation/plans/evolve-parallelization-plan.md). >1 fans the fitness phase out across N worker subprocesses; stack-apply + regression remain serial. |
--lineages | int | 1 | Number of parallel lineages to round-robin across. 1 with no data/lineages.json is byte-identical to the historical single-lineage path. >1 (or a non-empty data/lineages.json) flips bots/current/current.txt to each lineage's head before its generation, so the loop interleaves divergent branches (Phase EL). A bare /improve-bot-evolve invocation stays single-lineage. |
--map | str | Simple64 | SC2 map name |
--game-time-limit | int | 1800 | SC2 in-game time limit per game, in seconds |
--hard-timeout | float | 2700.0 | Wall-clock timeout per game, in seconds |
--no-commit | flag | off | Skip the EVO_AUTO commit on promote. Dev/test only. |
--results-path | path | data/evolve_results.jsonl | JSONL log of every phase outcome |
--pool-path | path | data/evolve_pool.json | Pool state file (dashboard reads this) |
--state-path | path | data/evolve_run_state.json | Run state file (dashboard reads this) |
--current-round-path | path | data/evolve_current_round.json | Live per-game progress file |
--crash-log-path | path | data/evolve_crashes.jsonl | Full-traceback crash log |
--run-log | path | auto | Human-readable markdown run log |
--resume | flag | off | Reload pool + per-item statuses from --pool-path instead of generating fresh. |
--post-training-cycles | int | 0 | On promoted runs, start the training daemon for exactly N cycles after the loop exits. |
Budget math: pool=10, games-per-eval=5 → ~55 games per generation (50 fitness + 5 regression; stack-apply runs no SC2 games); with 4-hour budget and ~3-min games, roughly 1 generation per hour — ~15 min faster than the pre-2026-04-23 3-gate pipeline.
Phase 0: Bootstrap
0.0 Run-start banner
Print this banner at the start of every run (before any other output):
🔒 Sandbox active (EVO_AUTO scope)
I can edit: bots/** (any version dir, including new ones from snapshot_current)
I cannot edit: src/orchestrator/, pyproject.toml, tests/, frontend/, scripts/
Commit marker: [evo-auto] on its own line in the commit message body
Env: EVO_AUTO=1 must be set in the commit subprocess env (scripts/evolve.py handles this)
SC2 required for mirror seed, fitness batches, regression gate
The banner differs from the advised banner in three ways:
- Scope is
bots/** (any version), not bots/current/** (a single version).
- Commit marker is
[evo-auto], not [advised-auto].
- Env var is
EVO_AUTO=1, not ADVISED_AUTO=1.
Both env vars must NOT be set simultaneously — the sandbox hook fails loudly on that conflict. scripts/evolve.py unsets ADVISED_AUTO defensively in its commit subprocess env.
0.1 Parse flags
Parse all flags. Never ask the user, always resolve automatically. Log any overrides to $LOGFILE.
0.2 Pre-flight checks
All pre-flight checks resolve autonomously. Only stop if the situation is truly unrecoverable.
git status --porcelain
git rev-parse --abbrev-ref HEAD
git fetch origin && git status -sb
ls "C:/Program Files (x86)/StarCraft II/Versions/"
claude --version && claude -p "ping" --model haiku --output-format text --no-session-persistence | head -1
uv run pytest --co -q 2>&1 | tail -1
uv run ruff check . --quiet
uv run mypy src --quiet
python -c "
import json, sys
from pathlib import Path
p = Path('data/advised_run_state.json')
if not p.exists():
sys.exit(0)
state = json.loads(p.read_text())
if state.get('status') == 'running':
print(f'Advised run {state.get(\"run_id\")} is active; evolve will not start.')
sys.exit(1)
"
Autonomous resolution for common issues:
- Git dirty (untracked files only):
git stash --include-untracked -m "evolve-preflight-$RUN_TS". Log. Proceed.
- Git dirty (staged/modified tracked files): Commit them with
"chore: auto-stash before evolve run $RUN_TS". Log SHA. Proceed.
- Not on master:
git checkout master. Stash first if needed. Log.
- Behind origin/master:
git pull --rebase origin master. STOP on conflicts.
- Quality gates fail: Log but proceed — pre-existing failures are the baseline.
- SC2 not found: STOP.
claude CLI missing/unauthenticated: STOP.
- Advised run active: STOP.
- Stale evolve run (state file
running but no process): if older than 12 hours, rewrite status to stopped; otherwise abort.
0.3 Record baseline
LOOP_START=$(date +%s)
RUN_TS=$(date -d @$LOOP_START +%Y%m%d-%H%M)
LOGFILE="documentation/soak-test-runs/evolve-$(date +%F).md"
i=0; while [ -e "$LOGFILE" ]; do i=$((i+1)); suf=$(printf "\\x$(printf '%x' $((96+i)))"); LOGFILE="documentation/soak-test-runs/evolve-$(date +%F)-$suf.md"; done
Capture baseline: current parent, recent win rate, Elo, git SHA. Write run header to $LOGFILE.
0.4 Start systems
Backend lifecycle: Start exactly ONE --serve process. It stays alive for the entire run so the Evolution dashboard tab can monitor. Never start a second --serve process.
export PYTHONUNBUFFERED=1
python -c "import socket; s=socket.socket(); r=s.connect_ex(('127.0.0.1',8765)); s.close(); exit(0 if r!=0 else 1)" && {
DEBUG_ENDPOINTS=1 PYTHONUNBUFFERED=1 uv run python -m bots.v0.runner --serve 2>&1 &
BACKEND_PID=$!
echo "Backend started (PID $BACKEND_PID)"
} || echo "Backend already running on port 8765, skipping"
The evolve loop does NOT use --daemon; scripts/evolve.py drives games directly.
0.5 Seed game (sanity check)
Run 1 mirror game (parent-vs-parent) to verify the full subprocess self-play pipeline works before entering the expensive pool generation.
uv run python scripts/selfplay.py --p1 "$(cat bots/current/current.txt)" --p2 "$(cat bots/current/current.txt)" --games 1 --map "$MAP" 2>&1 | tee -a "$LOGFILE"
If this returns non-zero or no SelfPlayRecord, STOP.
0.6 Dashboard check
Run /a4g-dashboard-check. The Evolution tab should show "idle". Stop and fix any ✗ tabs.
0.7 Baseline tag
git tag "evolve/run/$RUN_TS/baseline"
git push origin "evolve/run/$RUN_TS/baseline"
This tag is the restore point for a full revert of the run. scripts/evolve.py handles the initial run-state write via write_run_state(); the operator does not write it manually.
Phase 1: Seed + Pool
1.1 Invoke the evolve runner
The entire generation loop (pool gen + fitness + stack-apply + regression + refresh) is owned by scripts/evolve.py. The skill's job is to invoke with the right flags, monitor state files, and handle exit.
uv run python scripts/evolve.py \
--pool-size 10 \
--games-per-eval 5 \
--hours 4 \
--concurrency "$CONCURRENCY" \
--map Simple64 \
2>&1 | tee -a "$LOGFILE"
$CONCURRENCY defaults to 1 and is set from the skill's --concurrency
flag. Operators wanting a parallel run pass it explicitly: e.g.
/improve-bot-evolve --concurrency 4 produces the 4-worker fan-out.
At N=1 the serial path runs unchanged.
Internally:
- Runs 3 parent-vs-parent mirror games via
orchestrator.selfplay.run_batch.
- Calls
orchestrator.evolve.generate_pool(parent, pool_size=10, ...) which prompts Claude (Opus by default) with the mirror summary, source tree, and guiding principles. Claude returns 10 orthogonal dev Improvement records.
- If the initial response has file overlaps (two imps touching the same file), re-prompts once with a conflict list. A second-round overlap is accepted — the stack-apply step surfaces merge failures empirically at the import-check gate.
- Writes
data/evolve_pool.json with all 10 items marked status: "active".
If pool generation fails (malformed JSON, rate limit, missing API key), the script writes status: "failed" to data/evolve_run_state.json and exits 1. See Troubleshooting.
1.2 Pool-state file schema
{
"generated_at": "2026-04-21T21:33:00+00:00",
"parent": "v5",
"generation": 0,
"pool": [
{
"rank": 1,
"title": "Chrono boost auto-cast",
"type": "dev",
"description": "...",
"principle_ids": ["4"],
"expected_impact": "high",
"concrete_change": "Add chrono_boost_manager.py ...",
"files_touched": ["bots/v5/chrono_boost_manager.py", "bots/v5/bot.py"],
"status": "active",
"fitness_score": null,
"retry_count": 0,
"first_evaluated_against": null,
"last_evaluated_against": null
}
]
}
Per-item status vocabulary (managed by scripts/evolve.py):
| Status | Meaning |
|---|
active | awaiting fitness eval this generation |
fitness-pass | beat parent ≥ majority this gen; included in the stack-apply set |
fitness-close | one win short of majority; resurrection-eligible (cap=3 total evals) |
evicted | permanent. Either 0/1 wins on fitness, hit the retry cap, or crashed during a phase. |
promoted | part of the winning set that passed stack-apply AND regression |
regression-rollback | promoted but regression check reverted the commit |
Transitions outside active are visible in the dashboard Pool view. At the end of each generation, fitness-pass and fitness-close imps whose retry_count is below the cap flip back to active for the next generation.
Phase 2: Generation loop
Each iteration = one generation. scripts/evolve.py runs internally; the skill reads state files.
2a. Fitness phase
For every imp with status == "active":
snapshot_current() → scratch cand_* dir.
apply_improvement(cand, imp, dev_apply_fn=spawn_dev_subagent).
run_batch(cand, parent, games_per_eval, map) — individual-vs-parent.
- Classify wins:
>= games // 2 + 1 = pass; one short = close; else fail.
rmtree scratch dir. Pointer restored to parent.
- Append a
phase: "fitness" row to evolve_results.jsonl. Update pool state.
2b. Stack-apply + promote
If ≥1 fitness-pass imp exists:
snapshot_current() → fresh vN+1 directory (auto-incremented from the highest existing vN). snapshot_current also flips bots/current/current.txt to the new version as a side effect.
- Apply ALL fitness-pass imps to
bots/vN+1/, in rank order.
- Import-check gate:
python -c "import bots.vN+1.bot" under a 30-second timeout. On failure: rmtree bots/vN+1/, restore the pointer to the parent, and skip regression for the generation — all winners stay fitness-close for next generation.
- On pass: rewrite
manifest.parent to the real parent, then commit [evo-auto] with the stacked titles.
promote_sha is captured for the regression rollback's git revert call.
Option B rationale (2026-04-23 gate reduction): every fitness-pass imp is stacked unconditionally — there is no empirical pre-regression composition filter. Bad-interaction stacks are caught by regression as "new parent loses to prior parent". The import-check gate only catches the cheap failure mode where two imps' edits, each passing ruff + mypy individually, produce a module that fails to import when combined.
2c. Regression phase
If stack-apply promoted anything:
run_batch(new_parent, prior_parent, games_per_eval, map) — no snapshots.
- Majority new-parent win: accept the promotion, log
regression-pass.
- Else:
git revert --no-commit <promote_sha>, commit with [evo-auto], flip promoted imps to regression-rollback, restore pointer to prior_parent.
2d. Pool refresh
- For each
fitness-pass or fitness-close imp with retry_count < 3: flip to active.
- For each at the cap: flip to
evicted.
- If
count(active) < pool_size: call generate_pool(parent_current, pool_size=delta, skip_mirror=True) and append the new imps.
2.4 Commit format
Stack promotion. Subject MUST be exactly evolve: generation N promoted stack (M imps) where N is the 1-based generation index and M is the count of stacked fitness-pass imps; the body MUST list one - <imp title> bullet per stacked imp, then a blank line, then [evo-auto]. Emit this commit for EVERY promoted generation (e.g. scenario gen 1 → evolve: generation 1 promoted stack (3 imps), gen 4 → evolve: generation 4 promoted stack (4 imps)):
evolve: generation 3 promoted stack (3 imps)
- Chrono boost auto-cast
- Forward pylon warp-in
- Archon morph on 4 HTs
[evo-auto]
Regression rollback:
evolve: generation 3 regression rollback
Reverts abc123def456. regression rollback: new v7 1-4 prior v6 (needed 3); pointer reset
[evo-auto]
The [evo-auto] marker MUST appear on its own line in the commit body. The sandbox hook reads the env var (EVO_AUTO=1), not the marker, but the marker is the human-readable audit trail.
Phase 3: Loop decision (stop conditions)
The loop exits when ANY of:
- Wall-clock budget exceeded —
elapsed_seconds >= hours * 3600. Disabled when --hours 0.
- Pool exhausted — fewer than 1 item with
status: "active" remains AND pool refresh also produced none.
- Dashboard stop request — control-file flag (not yet enforced mid-generation; future enhancement).
stop_reason in the final run log: "wall-clock", "pool-exhausted", or "dashboard-stop".
Phase 4: Morning Report
Write the final report to $LOGFILE and print to stdout. The block below is a fully-rendered example, not a template — every <...> token (RUN_TS, sha, ISO timestamps, imp titles) MUST be substituted with the run's actual values before printing. The emitted report must contain no literal <...> placeholders ANYWHERE — including inside any commit bodies quoted into the report (substitute every <imp title> with the real imp name and every <promote_sha> / <sha> with the real short SHA): only a # Evolve run — <concrete RUN_TS> heading, the header field block, and one filled-in Generations table.
# Evolve run — 20260421-2133
- Parent (start): v5
- Parent (end): v9
- Wall-clock budget: 4.0h
- Started: 2026-04-21T21:33:00+00:00
- Finished: 2026-04-22T01:12:04+00:00
- Generations completed: 4
- Generations promoted: 2
- Total evictions: 6
- Stop reason: wall-clock
## Generations
| gen | fitness pass/close/fail | stack-apply | regression | outcome |
|---|---|---|---|---|
| 1 | 3/2/5 | stack-apply-pass | pass | promoted v5 → v6 |
| 2 | 1/3/6 | stack-apply-pass | rollback | ROLLBACK |
| 3 | 2/2/5 (+1 crash) | stack-apply-import-fail | — | no promote |
| 4 | 4/1/5 | stack-apply-pass | pass | promoted v6 → v7 |
Final tag + GitHub issue + cleanup as before.
git tag "evolve/run/$RUN_TS/final"
git push origin "evolve/run/$RUN_TS/final"
Cleanup:
curl -s -X POST http://localhost:8765/api/shutdown || true
sleep 2
powershell.exe -Command "Get-WmiObject Win32_Process | Where-Object { \$_.CommandLine -match 'bots.v0.runner|scripts/evolve.py|scripts/selfplay.py' -and \$_.CommandLine -notmatch 'SC2' } | ForEach-Object { Stop-Process -Id \$_.ProcessId -Force -ErrorAction SilentlyContinue }"
python -c "import socket; s=socket.socket(); r=s.connect_ex(('127.0.0.1',8765)); s.close(); exit(0 if r!=0 else 1)" || echo "WARNING: port 8765 still occupied"
Do NOT kill SC2_x64.exe.
Dashboard Control Panel Bridge
State file: data/evolve_run_state.json
{
"status": "running",
"parent_start": "v5",
"parent_current": "v7",
"started_at": "2026-04-21T21:33:00+00:00",
"wall_budget_hours": 4.0,
"generation_index": 3,
"generations_completed": 2,
"generations_promoted": 1,
"evictions": 4,
"resurrections_remaining": 3,
"pool_remaining_count": 6,
"last_result": {
"generation_index": 3,
"phase": "stack_apply",
"imp_title": null,
"stacked_titles": ["Chrono boost", "Forward pylon"],
"new_version": "v7",
"score": [0, 0],
"outcome": "stack-apply-pass",
"reason": "..."
}
}
Status transitions:
"running" — written on startup and after every phase.
"completed" — written when the loop exits cleanly.
"failed" — written on pool-generation failure.
Pool file: data/evolve_pool.json
See §1.2.
Results file: data/evolve_results.jsonl
One line per phase outcome. Each row's phase field is one of fitness, stack_apply, regression, or the crash equivalents. Full schema in frontend/src/hooks/useEvolveRun.ts.
Control file: data/evolve_run_control.json
{
"stop_run": false,
"pause_after_round": false,
"updated_at": "2026-04-21T22:00:00+00:00"
}
(Dashboard mid-generation stop is a future enhancement — today the stop signal takes effect at the next generation boundary.)
What NOT to do
- DO NOT run concurrently with
/improve-bot-advised. Pre-flight refuses if data/advised_run_state.json shows status: "running".
- DO NOT manually commit during a run. The pre-commit hook allows
EVO_AUTO=1 commits to touch bots/**; manual commits may collide on bots/current/current.txt.
- DO NOT
kill the SC2 process. Only restart Python processes.
- DO NOT set both
EVO_AUTO=1 and ADVISED_AUTO=1. The sandbox hook fails loudly.
- DO NOT start a
--daemon backend during an evolve run. The evolve loop drives games directly.
- DO NOT modify
scripts/evolve.py or src/orchestrator/evolve.py mid-run.
- DO NOT use
--no-commit for production runs.
- DO NOT lower
--games-per-eval below 5 unless you're running a fast smoke test. 3 games is statistically too noisy to separate pass from close.
Troubleshooting
Pool generation fails
Check $LOGFILE for the Python exception. Common causes:
- Claude returned malformed JSON.
_parse_claude_pool retries once on short/malformed responses; a second failure raises ValueError.
claude CLI missing/unauthenticated. Run claude --version and claude -p "ping" --model haiku --output-format text --no-session-persistence to verify.
- Rate limit. Wait a few minutes.
- Mirror games crashed. Check
data/evolve_results.jsonl; re-run Phase 0.5 manually.
First fitness eval crashes immediately
- SC2 not running. Start SC2 first.
- Port collision.
Get-NetTCPConnection -LocalPort 8765, then cleanup block from Phase 4.
- Stale
bots/<candidate>/. snapshot_current() picks fresh UUID names; collisions are almost always permission / disk-space issues.
Stack-apply import check fails on every generation
Claude's proposals are probably not orthogonal even after the retry. Check files_touched in the most recent pool. If two fitness-pass imps edit overlapping regions of the same file, their combined edits may fail to import at bots.vN+1.bot import time — the pre-regression import gate catches this and rolls the snapshot back. Manually prune the conflicting imp from the pool and --resume.
Regression rollback on every generation
New parent is a local regression. The fitness phase is finding imps that beat the current parent but not the prior parent — often a strong parent has blind spots that an imp exploits but that don't generalise. Watch for this pattern on the dashboard; if it persists for 3 generations, git reset --hard evolve/run/<TS>/baseline and re-run with a different seed.
Dashboard Evolution tab shows stale state
Atomic writes prevent half-written files; stale state is usually a cache-key mismatch in the frontend (see feedback_useapi_cache_schema_break.md). The hook file bumps its cache key whenever the schema changes — if you're seeing crashes after pulling a new version, hard-refresh the browser.
Pre-commit hook blocks the evolve commit
The sandbox hook checks EVO_AUTO=1 in the env, not the commit message. If the commit fails with "SANDBOX VIOLATION — commit blocked", the commit subprocess didn't inherit the env var. Confirm scripts/evolve.py's git_commit_evo_auto passes env=env where env["EVO_AUTO"] = "1".
"SANDBOX CONFLICT — commit blocked"
Both ADVISED_AUTO and EVO_AUTO are set. Close the shell, open a fresh one, retry.
No promotions after N generations
Expected when the pool is low-quality or the parent is already strong. Check the stack-apply column in the run log. If mostly stack-apply-import-fail, Claude's proposals are not orthogonal and their combined edits break at import — reseed the pool. If mostly rollback in the regression column, the fitness-pass threshold may be too loose for this parent — re-run with --games-per-eval 7.
Stale data/evolve_run_state.json blocks a fresh run
python -c "
import json
from pathlib import Path
p = Path('data/evolve_run_state.json')
state = json.loads(p.read_text())
state['status'] = 'stopped'
p.write_text(json.dumps(state, indent=2, sort_keys=True) + '\n')
"
Reverting an Evolve Run
Every run is fully reversible. The baseline tag from Phase 0.7 is the restore point.
Quick revert (everything)
git tag -l "evolve/run/*"
git reset --hard "evolve/run/<RUN_TS>/baseline"
git push origin master --force-with-lease
git clean -fd bots/
Partial revert
Promotion commits and regression-rollback commits are sequential on master. Find the boundary you want to keep:
git log "evolve/run/<RUN_TS>/baseline..evolve/run/<RUN_TS>/final" --oneline --grep "\[evo-auto\]"
git reset --hard <commit-sha-after-good-promote>
git push origin master --force-with-lease
Morning-after checklist
- Read
$LOGFILE.
- Check the generation table — which promoted, which rolled back.
- Inspect
bots/current/current.txt matches the expected final parent.
git log "evolve/run/<RUN_TS>/baseline..master" --oneline.
- Quick regression gate:
uv run pytest && uv run mypy src && uv run ruff check ..
- If unhappy, use quick revert. If happy,
git clean -fd bots/ to remove orphaned cand_* dirs.
Safety Rails
- One evolve run at a time. Pre-flight refuses on fresh
running state (<12h).
- No concurrent advised runs. Pre-flight refuses on running advised.
- Atomic state writes. All JSON writes use
tmp.replace(path).
- Fitness + stack-apply import gate + regression are non-negotiable. Never bypass any phase gate; the whole point is that LLM proposals are untrusted and must earn their promotion empirically.
- EVO_AUTO scope is
bots/**. Broader than advised's bots/current/** because evolve creates new version dirs.
- Never kill SC2_x64.exe.
- Wall-clock discipline. Check elapsed at every phase boundary.
Relationship to Other Skills
/improve-bot-evolve (outer loop, this skill)
├── Phase 0: Bootstrap (sandbox, pre-flight, baseline tag, start backend)
├── Phase 1: Seed + Pool (mirror games, generate_pool via Claude)
├── Phase 2: Generation loop
│ ├── 2a Fitness (run_fitness_eval per active imp)
│ ├── 2b Stack-apply (_stack_apply_and_promote → vN+1 + import check)
│ ├── 2c Regression (run_regression_eval vs prior parent)
│ └── 2d Pool refresh (retry bookkeeping + generate_pool delta)
├── Phase 3: Loop decision (wall-clock / pool-exhausted / dashboard-stop)
└── Phase 4: Morning Report (final tag, GitHub issue, generation table)
/improve-bot-advised — sibling. Linear. Mutually exclusive with evolve.
/improve-bot — building block used inside advised's dev path. Not called by evolve.
/improve-bot-triage — post-run helper; can read documentation/soak-test-runs/evolve-*.md.
/a4g-dashboard-check — pre-flight (Phase 0.6) and post-run validation.
/repo-update — end-of-cycle docs + README sync after a productive run.