Spotlights decides what to optimize; evolvers (evolutionary code-search backends) do the how. The prep-evolve subcommand is the bridge: pick a candidate from a finished run and it generates a self-contained, ready-to-run evolve bundle for one of three external evolvers. Each bundle contains the native config, a seed/target laid out the way that evolver expects, the findings/proposals digest folded into the prompt the evolver reads, and an evaluator scaffold. It does not run the evolve; it hands you a directory to cd into plus the exact launch command.
← Back to README
Tip
Not every candidate needs a search. Every bundle below ships with a
deliberately unfinished evaluation (the "evaluation gap"), and completing it is
project-specific work. When a candidate warrants one attempt rather than an
evolutionary search, spotlights-engine apply is the cheap
arm: one Claude Code session in a throwaway worktree produces apply.patch plus
the recorded verification recipe — a proposal faithfully implemented, with no
fitness loop. Use an evolver when you need a measured win.
Evolver (--evolver) |
Edit scope | Native config | Run command |
|---|---|---|---|
skydiscover |
single file (mutates the # EVOLVE-BLOCK-START/END region) |
config.yaml + seed.<ext> (you write evaluator.py) |
skydiscover-run seed.<ext> evaluator.py -c config.yaml |
coral |
multi-file (agent edits a git worktree) | task.yaml (you write grader/grader.py) |
coral start --config task.yaml |
nous (alias agentic-strategy-evolution) |
multi-file (experiment arms with code_changes[]) |
campaign.yaml (you set ground_truth.pass_condition) |
NOUS_CAMPAIGN_PARENT=$PWD/nous_runs nous run campaign.yaml |
Pass --evolver all to emit one bundle per compatible evolver (skydiscover is reported as skipped for multi-file selections).
prep-evolve consumes a completed run and a target repo. Point --result at the
run directory (it finds result.json) and pass --repo at the checkout you want
the bundle to target; omit --candidate to generate a bundle for every candidate:
# every candidate → spotlights-out/evolve/<module>/<candidate>/skydiscover/
spotlights-engine prep-evolve --result ./spotlights-out \
--repo ../vllm --evolver skydiscover
# one candidate
spotlights-engine prep-evolve --result ./spotlights-out \
--repo ../vllm --candidate cand-vllm_v1_kv_offload-0002 --evolver skydiscover
# top 10 of a ranking → still under spotlights-out/evolve/… (not inside sorted/)
spotlights-engine prep-evolve --result ./spotlights-out/sorted \
--repo ../vllm --evolver skydiscover --top-n 10Tip
Prefer --repo over the index.md fallback. If you omit --repo,
the repo is read from the Repo path: line in the run's index.md. That path
is whatever was recorded when the run was produced, so it is often stale or
wrong on another machine or checkout — it may not exist, or (worse) point at a
different checkout whose code no longer matches the recorded line ranges,
which surfaces later as a confusing staleness error. Passing --repo
explicitly makes the target unambiguous.
Check out the same commit the run was produced against. prep-evolve
validates each candidate's recorded symbol/line range against the live repo
before writing a bundle. Point --repo at a checkout on the commit recorded
in the run's run_manifest.json under target.commit_sha (from
target.repo_url). On any other commit the code may have shifted, and the
gate fails with a staleness error even though the run itself is fine:
# commit the run targeted, e.g. from examples/vllm_subset/run_manifest.json
git -C ../vllm checkout 83ad767eed3be3ee7f2df63be693bfaca5c7c922
spotlights-engine prep-evolve --result ./spotlights-out --repo ../vllm --evolver skydiscover--result accepts any run artifact and self-locates the rest: a run directory, a
result.json file, an index.md, a sorted/ directory, or a
sorted_candidates.json/.md. --candidate takes the candidate id from the
module page and is optional (omitting it processes all candidates); the module is
inferred from the id, so --module is no longer required. When --result points
at a sorted source, omitting --candidate processes the ranked candidates in rank
order, and --top-n <N> builds only the top N (default all); --top-n with a
plain result.json is an error. The target repo is resolved from --repo, else
from the Repo path: line in the run directory's index.md. Bundles are written
to <base>/evolve/<module>/<candidate>/<evolver>/, where <base> is --out when
given, else the run directory. Candidates an evolver cannot handle are skipped
with a warning, and existing bundles are skipped unless --force is set.
Bundles are written to <base>/evolve/<module>/<candidate>/<evolver>/, mirroring
the modules/ tree. Alongside the evolver-native files, every bundle includes:
README.md— the copy-paste run command, the in-scope files, and the evaluation-gap warning.
The findings/proposals digest is embedded directly in each evolver's native config (the grader/evaluator prompt or system message) rather than written as a standalone file.
Important
The evaluation gap is real. Every evolver needs a project-specific measurement loop (build the target, run a benchmark, parse the metric). prep-evolve parses the correctness/performance oracle out of the candidate's evolve_rationale and objective and pre-fills what it can, but the part only you can supply ships as a clearly-marked TODO: for skydiscover and CORAL that is the performance measurement inside the evaluator/grader; for Nous, which needs no evaluator code, it is ground_truth.pass_condition in campaign.yaml — the rule deciding whether a measured number counts as a win. Spotlights knows the metric and its direction, never the threshold. Every bundle is launchable end-to-end immediately, but results are not meaningful until you close that gap — each bundle's README.md states what Spotlights believes the oracle is and exactly what to fill in.
| Flag | Default | Purpose |
|---|---|---|
--result |
(required) | A finished run's result.json, the run directory containing it, an index.md, a sorted/ dir, or a sorted_candidates.{json,md}. |
--module |
(inferred) | Slash-form qualified name; inferred from the candidate id when omitted. |
--candidate |
(all) | Candidate id, e.g. cand-…-0002. Omit to process every candidate. |
--top-n |
all |
For a sorted --result: build only the top N ranked candidates. Error with a plain source. |
--out |
(run dir) | Base directory; the evolve/… tree is written under it. Defaults to the run directory. |
--evolver |
(required) | skydiscover | coral | nous | agentic-strategy-evolution | all. |
--repo |
(none) | Target repo path; wins over --index. |
--index |
(none) | Rendered index.md, used only as a --repo fallback. |
--scope |
candidate |
candidate or module-main-files (CORAL/Nous only — adds the module's main_files as editable targets). |
--direction |
(inferred) | minimize | maximize; overrides the direction inferred from the objective verb. |
--model |
(evolver default) | Override the default evolver LLM model. |
--force |
off | Re-run over an existing bundle, overwriting every generated file. |
Re-runs resume cleanly: an existing bundle directory is skipped (not an error)
unless --force is set. With --force, every generated file in that bundle is
overwritten — including a hand-edited evaluator/grader — so copy out any evaluator
work you want to keep before re-running.
The --evolver flag selects one of three external evolvers. They differ in edit
scope, what you must hand-author, and how they install — all three share the same
prerequisites (bottom of section).
LLM-driven evolutionary search over a single marked code region.
- Repository: https://github.com/skydiscover-ai/skydiscover
- Edit scope: single file — only the region between
# EVOLVE-BLOCK-STARTand# EVOLVE-BLOCK-ENDchanges. - Native config:
config.yaml+seed.<ext> - You must write:
evaluator.py(what to write) - Install:
pip install skydiscover - Run:
skydiscover-run seed.<ext> evaluator.py -c config.yaml
Agentic multi-file evolver — an LLM agent edits a git worktree of the repo.
- Repository: https://github.com/Human-Agent-Society/CORAL
- Edit scope: multi-file — the agent may edit any in-scope file in a git worktree.
- Native config:
task.yaml - You must write: the grader (
grader/grader.py) + seed directories - Install:
# Shell installer curl -fsSL https://raw.githubusercontent.com/Human-Agent-Society/CORAL/main/install.sh | sh
# Claude Code plugin /plugin marketplace add Human-Agent-Society/CORAL /plugin install coral@coral-marketplace - Run:
coral start --config task.yaml - Quickstart: open the target repo (make sure
task.yaml'srepo_pathpoints to the same location — update it if needed), then ask Claude Code — replacing<CORAL_BUNDLE_PATH>with the path to wherever you unpacked this bundle:use coral to optimize this — start from the bundle at
<CORAL_BUNDLE_PATH>. Don't change what task.yaml defines — the goal, in-scope file, oracle, metric, and direction are fixed. Fill only the gaps: write the grader, set up the seed, and add whatever's needed so the grader cleanly scores the seed. The seed should be the full repo so the agent can read everything, but the grader must reject any attempt that modifies or adds a file outside the allowlist — configured astarget_files(orallowed_paths) undergrader.argsintask.yaml.
Runs experiment "arms" that apply code_changes[] across the target.
- Repository: https://github.com/AI-native-Systems-Research/agentic-strategy-evolution
- Edit scope: multi-file — experiment arms with
code_changes[]; the agents discover metrics and evaluate on their own. - Native config:
campaign.yaml - You must write:
ground_truth.pass_conditionincampaign.yaml— a concrete pass/fail rule. - Install:
pip install "git+https://github.com/AI-native-Systems-Research/agentic-strategy-evolution.git@reflective" - Run:
NOUS_CAMPAIGN_PARENT=$PWD/nous_runs nous run campaign.yaml
Python 3, an LLM API key for the model named in the config (e.g. ANTHROPIC_API_KEY),
the target repo checked out at the run's commit, and a server with all the hardware
the benchmark needs (e.g. a GPU) to build and measure the target.