Module map¶
Every module, what it is for, and where its design is explained. The API reference is the generated companion to this page: this is why and how, that is what.
evolve() ── the one entry point
│
┌───────────────┬──────────┴──────────┬────────────────────┐
│ │ │ │
what evolves who does the work where it runs how it merges
│ │ │ │
strategies agents executor aggregator
filetree backends supervisor defaults
treestrategy runners workspec ledger
sampling sandbox verifier
dataloader sandbox_container evaluator
rewards sandbox_shared evalcache
parallel pipeline staleness
scheduler governance
policies ── the contracts, across all four
metrics · bench ── what a run cost, and comparing runs
The loop¶
| module | what it is | page |
|---|---|---|
evolution |
evolve(), the artifact, the actor, the result |
The evolve method |
evolvable |
Evolvable, Diff, EvidenceCard, Contract — the data model |
Data model |
skill |
evolve_skill() — dataset in, instruction out |
Quickstart |
skilldir |
evolve_skill_dir() / _agent_dir() / _agent_code() |
Directory evolution |
async_evolve |
the same loop without the round barrier | Async |
orchestrator, async_runtime, domains.router |
the reference domain the results were measured with — adapters over the engine above, not a second loop | Orchestrator |
What evolves¶
| module | what it is | page |
|---|---|---|
strategies |
SingleSlot, AppendRules, KeyedRules — the text strategies (re-exported from evolution, which is the published import path) |
Strategies |
filetree |
a directory ↔ artifact state, path safety, TreeSpec |
Directory evolution |
treestrategy |
FileTree, the <EDITS> proposal protocol, tree_reflector |
Directory evolution |
Who does the work¶
| module | what it is | page |
|---|---|---|
agents |
any prompt -> text is a completion; WorkspaceAgent adds a directory |
Agents |
backends |
a tool-using agent over a document too big to inline | Backends |
runners |
give a real agent the candidate directory, one workspace per rollout | Directory evolution |
dataloader |
datasets, splits, cached fetches | Data layer |
rewards |
the three scorers everyone writes, with the details right | Rewards |
How the work is spread¶
| module | what it is | page |
|---|---|---|
parallel |
DP / TP / PP — how a round's work is split | Parallelism |
sampling |
which task a worker rolls out next | Sampling |
scheduler |
duration-aware dispatch, stragglers, the audit queue | Scheduling |
pipeline |
the retirement, early-stop and backpressure rules both runtimes share | Async |
Where it runs¶
| module | what it is | page |
|---|---|---|
executor |
the rollout(spec) -> Result seam, and the in-process default |
Execution |
supervisor |
persistent worker processes, and deciding when one is gone | Execution |
workspec |
a rollout as data: named callables instead of closures | Execution |
sandbox |
workspace leases: one ceiling, one release path, reclaim what an owner abandoned | Sandboxes |
sandbox_shared |
one ceiling across processes, counted from the lease directory | Sandboxes |
sandbox_container |
the provider that makes a sandbox an actual boundary (needs docker/podman) | Sandboxes |
How a change is accepted¶
| module | what it is | page |
|---|---|---|
aggregator |
the optimizer: staleness → conflict → fusion → acceptance → commit | Aggregator |
defaults |
the shipped algorithm as replaceable pieces: conflict, fusion, acceptance, promotion | Aggregator |
stats |
the acceptance maths: Beta posterior, P(Δ>0), annealed δ, UCB, difficulty weight |
Aggregator |
verifier |
rule / learned / oracle, and the budget on the expensive one | Verifier |
evaluator |
the gate's own bounded, reusable concurrency, separate from the rollouts' | Verifier |
evalcache |
memoised evaluations: single-flight, environment-aware, shareable across processes | Verifier |
staleness |
what to do with a diff whose base version moved | Staleness |
ledger |
the git-backed, compare-and-swap artifact store | Ledger |
governance |
L0 frozen / L1 slow / L2 fast, by blast radius | Governance |
Across all of it¶
| module | what it is | page |
|---|---|---|
policies |
the contracts: which decisions are replaceable, and what each is given | Architecture |
metrics |
what the run cost: time, calls, staleness ratio, cache hits, sandbox waits | Usage |
bench |
the configuration matrix and the rules that make comparing them mean something | Efficiency |
Reading order¶
Depending on what you are doing:
Just using it. Install → Quickstart →
The evolve method → the one module you need to swap.
Evolving a folder or an agent's code. Quickstart — a directory → Directory evolution → Governance for the safety model.
Deciding whether to trust it. Concepts → Aggregator → Orchestrator (how the claims were measured) → Results.
Extending it. Data model → Strategies → Aggregator → the algorithm ports, each of which replaces a different piece.
Provided, tested, and not in any engine path¶
Some of what import agentdescent gives you is a primitive for a
configuration that does not ship yet — the design calls for it, it is
implemented and tested in isolation, and no loop reaches it today. Each one says
so in its own docstring, which meant finding out cost a read of the source, one
class at a time. They are all in one table instead:
| name | why it exists | what would reach it |
|---|---|---|
Ledger.commit_atomic |
2PC across several artifacts, for a contract-breaking diff that must land with its adapters | a multi-artifact library; evolve() registers exactly one |
L1SerialGate |
"at most one L1 diff in evaluation anywhere" | concurrent merging; every shipped runtime merges on one thread, so the guarantee already holds by construction |
ResumeQueue |
turn-level checkpoints of a timed-out rollout | a rollout that exposes its turns; run(rendered, task) -> output is opaque, which is what lets any agent be plugged in |
AuditScheduler.pop |
draining the Ĝ-ordered audit queue out of band | AuditScheduler(collect=True); the default computes priorities without queuing, because nothing drains it |
EvidenceBuffer.settled |
discarded evidence stays addressable — the structural advantage of artifacts over gradients | re-filing settled cards into the trajectory pool; today it is a bounded diagnostic ring |
TaskScheduler × artifact axis |
the design's L-task is (task cluster × artifact) |
more than one artifact; TaskCluster has no artifact dimension. The cluster axis is reachable from evolve() via ClusterParallel |
PipelineParallel |
one artifact per stage, with upstream blame | a multi-artifact run; evolve() refuses it rather than degrading to DP in silence |
The rule they share: a primitive that is implemented and unreachable is honest;
one that is reachable and silently does nothing is not, which is why
PipelineParallel raises and Policies refuses a field it cannot honour.
Dependency shape¶
Nothing in the framework imports a provider SDK at module level, and the core imports nothing outside the standard library:
evolvable ── ledger ── aggregator ── evolution ── skill / skilldir
│ │ │ │
governance verifier staleness parallel · sampling · scheduler
│
agents ── backends ── runners
│
filetree ── treestrategy
anthropic and openhands-ai are imported lazily inside the functions that need
them, so the rest of the framework runs without either.