Evolve → run round-trip
What
Loop: episodes → evolve → archive → seed run → write-back fitness.
Why
It’s not enough for castellan evolve to find a better genome once — the round-trip closes so a production run can seed from that genome, and its outcome feeds the archive for the next evolve pass. See Topology evolution and Organism model.
Overview
Castellan closes the loop between topology evolution and production runs in three steps:
castellan evolve— mutates harness topology from the episode corpus; writes.castellan/topology_archive.json.castellan run --seed-archive— seeds topology/coordination from the nearest archive entry (matched by plugin + goalenvironment_fingerprint); logs agenome_idand agenome_seedevent in the episode.- Write-back — after a successful run, merges the new episode’s fitness into the matching archive entry (matched by
genome_id), then re-sorts the archive by success score.
Write-back is enabled automatically whenever --seed-archive is used, or explicitly with --write-back. Pass --no-write-back to disable the automatic path (useful when you want to seed from a known-good genome without letting a noisy or exploratory run pollute its fitness history).
Anatomy: what’s in an archive entry
Each TopologyEntry in .castellan/topology_archive.json carries:
| Field | Meaning |
|---|---|
id | Archive entry UUID |
coordination | HarnessCoordination — the topology + wake/decay parameters this entry expresses |
fitness | FitnessVector — plugin-grounded scores, updated by write-back |
generation | Which evolve generation produced this entry |
environment_fingerprint | Plugin + goal signature used by nearest() to match seed candidates |
genome_id / parent_genome_id | Lineage — see Organism model |
quarantined | Excludes this entry from nearest() regardless of fitness — see Organism model |
decay_lambda, zone_decay, zone_weights | Per-zone tuning this genome expresses |
flowchart LR
EP[episode corpus] --> EV[castellan evolve]
EV --> ARCH[topology_archive.json]
ARCH --> SEED[castellan run --seed-archive]
SEED --> RUN[episode: genome_seed event]
RUN -->|Satisfied + fitness| WB[write_back_fitness]
WB --> ARCH
classDef evolve stroke:#d97706,stroke-width:2px
classDef runtime stroke:#0969da,stroke-width:2px
class EP,EV,ARCH,WB evolve
class SEED,RUN runtime
How operators use it
1. Quick reproduce from a fixture corpus:
EVOLVE_FIX="tests/fixtures/evolve-episodes"
mkdir -p .castellan/episodes
cp "$EVOLVE_FIX"/*.json .castellan/episodes/
castellan evolve --episodes 10 --workspace .castellan/episodes
castellan run --goal "reach target" --plugin gridworld --seed-archive --json
Expect in the resulting episode JSON:
genome_id— seeded from the nearest archive entryfitness— plugin-grounded success score for this runevents[]containing agenome_seedevent, and — when write-back applies — anarchive_write_backevent
2. Seed from a specific genome instead of “nearest”:
castellan run --goal "reach target" --plugin gridworld --genome <genome_id>
--genome and --seed-archive are mutually exclusive — pick one.
3. Seed without letting the run mutate the archive (e.g. reproducing a past result exactly):
castellan run --goal "reach target" --plugin gridworld --genome <genome_id> --no-write-back
4. Point at a non-default archive path (e.g. testing a candidate archive before promoting it):
castellan run --goal "reach target" --plugin gridworld --seed-archive --archive .castellan/topology_archive.candidate.json
Write-back semantics
- Trigger:
GoalStatus::Satisfiedand bothgenome_idandfitnesspresent on the episode — a failed or inconclusive run never writes back. - Merge:
TopologyArchive::write_back_fitnessupdates the matching entry byid, then re-sorts the archive by success score so futurenearest()calls prefer the freshest evidence. - Archive path: controlled by
--archive(default.castellan/topology_archive.json).
JSON events
With --json, archive write-back emits a typed archive_write_back CastellanEvent line, immediately before episode_end. Automation watching the NDJSON stream can key off this event to know the archive changed without re-reading the file.
Recipes
When proving the round-trip works end to end (e.g. for a demo or CI check), use the fixture corpus reproduction above — it’s deterministic and doesn’t depend on a live LLM or long-running episodes.
When you want continuous improvement in a daily-driver loop, always pass --seed-archive on production runs (write-back is then automatic) so every successful run compounds into the next evolve generation.
When comparing “does seeding actually help,” run the same goal once with --seed-archive and once without, then diff fitness in the two episode JSONs — the delta is the seeding effect for that goal/plugin pair.
When you need a stable baseline for regression testing, seed with an explicit --genome <id> and --no-write-back so the archive never drifts between test runs.
Failure paths / troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
--seed-archive seeds nothing (no genome_seed event) | No archive entry matches the plugin+goal environment_fingerprint, or all matching entries are quarantined | Run castellan evolve first to populate the archive; check castellan genome list for non-quarantined entries |
error: specify only one of --genome or --seed-archive | Both flags passed together | Pick one — explicit genome or nearest-match auto-seed |
No archive_write_back event even though the run succeeded | Episode’s fitness or genome_id missing, or --no-write-back was set | Confirm the plugin’s verify_goal sets GoalStatus::Satisfied; drop --no-write-back if present |
| Write-back seems to “lose” a good fitness score | A later, worse run wrote back to the same genome_id and got re-sorted lower | Use --no-write-back when reproducing/benchmarking a known-good genome so exploratory runs can’t overwrite it |
--archive <path> run can’t find the file | Path doesn’t exist yet — archive is created by castellan evolve, not by run | Run castellan evolve --workspace .castellan/episodes once to create the archive file first |
Related
- Topology evolution — how the archive gets populated and gated
- Organism model — genome lineage and the immune gate
- CLI reference
castellan runreference