Organism model
What
Castellan treats a harness as a living organism, not a static config file: a genome expresses coordination parameters, circulation vitals track field health tick-by-tick, and an immune gate rejects harmful topology mutations before they reach production.
Why
This layer turns “evolve topology” from a black box into something you can inspect, quarantine, and trust. Prefer organism terms over animal-pack or feudal metaphors — lexicon.
Read this page if you’re trying to understand what castellan vitals is actually showing you, why a mutation got rejected instead of accepted, or how genome lineage relates to the topology archive.
Status: Phases 1–4 are complete — circulation, DNA lineage, immune gate, and per-zone expression are all live in castellan-evolve/castellan-runtime.
Anatomy: where organism state lives
| Location | Holds |
|---|---|
castellan-core/organism.rs | DNA types, circulation vitals, per-zone expression |
castellan-substrate/pheromone | Per-zone decay (falls back to a global lambda if unset) |
castellan-runtime/circulation | Vitals computation + immune_fever tracking |
castellan-runtime/engine.rs | Wires vitals, genome_id, fever, and decay into the run loop |
castellan-evolve/genome.rs | Lineage, crossover, archive reconstruction |
castellan-evolve/immune.rs | Threat detection (ThreatPattern), auto_reject, quarantine recommendation |
castellan-evolve/mutator.rs | Crossover + cold-zone decay adjustment |
castellan-cli/organism_cmd.rs | castellan vitals, castellan genome list|lineage|map |
Bio-novelty extensions (H1–H9)
| Mechanism | Seam |
|---|---|
| Quorum phase locks | QuorumSensor + FalseQuorum immune |
| Response thresholds | AgentNode.response_threshold + surplus dispatch |
| Cement morphogenesis | cement deposits → MorphogenesisEngine |
| Trail / Physarum corridors | CorridorField |
| MAP-Elites archive | MapElitesArchive + castellan genome map |
| GRN expression | RegulatoryNetwork::express(vitals) |
| Live quarantine | ZoneQuarantine mid-run |
| Clonal immune memory | .castellan/immune_memory.json |
Circulation vitals also expose role_entropy, role_stickiness, inflammation, and mean_corridor_length when those mechanisms fire.
Organism loop
flowchart LR
RUN[castellan run] --> VITALS[circulation vitals]
VITALS --> EP[episode JSON]
EP --> EV[castellan evolve]
EV --> IMM[immune_report]
IMM -->|accept| ARCH[genome archive]
IMM -->|reject| QUAR[quarantine]
classDef runtime stroke:#0969da,stroke-width:2px
classDef evolve stroke:#d97706,stroke-width:2px
classDef gate stroke:#1a7f37,stroke-width:2px
class RUN,VITALS,EP runtime
class EV,ARCH evolve
class IMM,QUAR gate
- Every run expresses a
CastellanGenome— the coordination and decay parameters active for that episode. - The
PheromoneField’s circulation health lands in the episode asCirculationVitals. castellan evolvemutates or crosses over (requires a Pareto front with ≥ 2 non-quarantined entries) candidate genomes, then gates every candidate throughimmune_report().- Quarantined genomes are excluded from
archive.nearest()— they can never be selected as a seed for a futurecastellan run --seed-archive. - Per-zone decay gets tuned from the proposer’s cold-zone heatmap, so zones that go quiet decay differently from zones under constant pressure.
The immune gate, concretely
ImmuneReport carries two independent signals per candidate:
| Field | Meaning | Effect |
|---|---|---|
auto_reject | Set when threats include RunawayParallel or other severe patterns | Candidate never enters the archive this generation |
quarantine_recommended | Set whenever any threat pattern is detected | Candidate is marked quarantined: true; excluded from future nearest() seeding even if accepted |
A genome can be accepted into the archive but still quarantined — quarantine is about seeding safety, not archive membership. Check castellan genome list output for the quarantined flag per entry.
How operators use it
1. Run a goal — a genome is expressed automatically, no flags needed:
castellan run --goal "reach target" --plugin gridworld
2. Inspect circulation health for the most recent (or a specific) episode:
castellan vitals
castellan vitals --episode .castellan/episodes/<goal-id>.json
3. Evolve genomes from the episode corpus — this is where the immune gate runs:
castellan evolve --episodes 10 --workspace .castellan/episodes
4. List genomes and trace lineage:
castellan genome list
castellan genome lineage <genome_id>
5. Seed a new run from a surviving (non-quarantined) genome:
castellan run --goal "reach target" --plugin gridworld --seed-archive --write-back
Phase completion
| Phase | Deliverable | Status |
|---|---|---|
| 1 Circulation | circulation_vitals, castellan vitals, fitness dimension | ✅ |
| 2 DNA | genome_id lineage, archive seed reconstruction, castellan genome | ✅ |
| 3 Immune | Drift + fitness + immune gate, quarantine, fever deposit | ✅ |
| 4 Expression | Per-zone decay, crossover, flux-grid test, fever unit test | ✅ |
flux-grid (optional, experimental)
- Feature flag:
castellan-substrate/flux-grid— a vendored 64×64Stigmergygrid. - Not enabled in default builds. The zone-keyed
PheromoneFieldremains the default substrate. - Enable for experiments:
cargo build -p castellan-substrate --features flux-grid
Recipes
When a run’s fitness looks fine but the field “feels” unhealthy, check castellan vitals before trusting the number — circulation vitals surface stalls and dead zones that a single scalar fitness score can hide.
When castellan evolve keeps rejecting your best-looking candidates, dump immune_report reasoning via --plan (read-only, zero writes) before assuming the mutator is broken — the drift or immune gate may be catching a real structural problem.
When you want to know if a genome is safe to seed from, always check quarantined on castellan genome list, not just whether it’s present in the archive — presence and safety are separate signals.
When investigating a lineage regression, castellan genome lineage <id> walks parent_genome_id; note that crossover sets a full parent_ids list internally, but the CLI currently surfaces only the primary parent (see Known deferred below).
Failure paths / troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
castellan vitals reports no data | No episodes yet, or wrong --workspace | Run castellan run first; confirm .castellan/episodes/ has files |
Genome accepted but castellan run --seed-archive never picks it | Genome is quarantined: true | Check castellan genome list; quarantined entries are excluded from nearest() |
| Crossover never happens | Pareto front has fewer than 2 non-quarantined entries | Run more episodes/generations to build a larger accepted pool |
| Lineage shows only one parent for a crossover genome | CLI limitation — parent_ids is tracked internally but only parent_genome_id is surfaced | Known deferred; inspect the archive JSON directly for full parent_ids if needed |
flux-grid feature won’t build | Not enabled in default feature set | Build with cargo build -p castellan-substrate --features flux-grid explicitly |
Known deferred
flux-gridis not wired into theswarm-demodefault path.- Full
parent_idslineage is not yet exposed by the CLI (only primaryparent_genome_id). RAH_MAX_DEPTHcycle avoidance betweencastellan-evolveandcastellan-rahlives incastellan-corebut isn’t documented end-to-end yet.
See also
- Coordination model — the substrate whose parameters genomes express
- Topology evolution — the evolve loop that produces and gates genomes
- Evolve round-trip — seeding a run from the archive and writing fitness back
- Quickstart — first episode
- Episode flywheel on Introduction