Improvement Plane
Source audit: 2026-09-19. Research-preview workflows; dated measurements below retain their original scope and are not release or hosted-production qualification.
0 has several different improvement mechanisms. They do not form an unattended system that discovers its own ground truth, rewrites itself, and proves that it became better at cybersecurity. The useful contract is narrower: retain observations, propose changes, evaluate them against operator-owned criteria, and select exact versions for later work.
- Learning retains revision-aware notes and execution feedback.
- Source evolution proposes edits to copies of source snapshots, evaluates them, and selects immutable worker versions.
- Executable plugins let an enabled agent submit, run, compose, evolve, and roll back its own TypeScript tools, skills, and agent-like programs. New calls can use a new version without restarting the session.
- Live harness generations can replace the active
agent.driverandui.view, including view, command, and settings contributions. Core and OpenTUI consumers are wired; this is not merely a wire-type proposal, nor does that wiring establish crash-safe or hosted end-to-end qualification. See Live harness component contract.
Source candidates execute in fresh Docker containers or local smolvm guests with bounded resources. Activation changes subsequent work at a defined boundary while preserving prior version identities and receipts.
task observations and development feedback -> authored source or component candidate -> isolated execution and evaluation -> retained version and evidence -> explicit or configured automatic selection -> activation at the consuming runtime's boundary -> continued work, further feedback, or rollbackChoose an improvement workflow
Section titled “Choose an improvement workflow”| Need | Entry point | Default and activation boundary |
|---|---|---|
| Retain source-grounded notes | Eligible native research runs and remember_codebase | Untrusted revision-aware hints, not verified findings |
| Evaluate a source worker | 0 evolve run --config evolution.json | Source access and automatic promotion default off; accepted versions serve future pinned executions |
| Deploy an evolved finder | 0 deep-review ./repo --evolution-config evolution.json | Opt-in per review; independent verification remains separate |
| Create an executable tool or skill | Agent’s self_extend tool | Enabled by default for non-verifier sessions; Docker/smolvm prerequisites still required |
| Install community code | 0 plugin install, then project enable | Separate Hackstore child-process contract; not sandboxed self-extension |
| Replace a session driver/view | self_extend harness actions and OpenTUI live-harness controls | Session boundary activation; trusted ESM requires separate workspace consent |
| Evolve finder prompts | 0 lens-synth, or the opt-in TUI watcher | Curated corpus and promotion approval; changes future hunts, not FoxGuard |
Start with the complete source-worker config for offline evaluation or executable-plugin setup for session tools. Model credentials configure proposal generation; guest images configure execution. Neither substitutes for the other.
Implementation map: improvement/config.ts,
improvement/loop.ts,
commands/evolve.ts,
plugins/executable.ts,
console/turn-engine.ts.
Engagement boundary
Section titled “Engagement boundary”Existing source workers retain their snapshot and configuration after promotion. Executable plugins activate on subsequent calls. Live driver and UI replacement use the generation contract below.
Sandboxed components use host-mediated services. Workspace-trusted ESM requires separate authorization and executes with host permissions.
- Retain version identities, observations, and receipts across changes.
- Keep evaluation answers and credentials out of candidate source snapshots.
- Do not treat a candidate’s own success claim as an independent verifier.
- Do not automatically replay already-issued tool effects after a driver fails.
- An automatic-promotion policy or self-extension setting is not a grant of trusted host execution.
Development engine replacement
Section titled “Development engine replacement”The development console can reload trusted packages/core/src changes without
discarding its conversation. This host-code path is not the sandboxed source
evaluator or executable-plugin admission path.
It requires a console started with ZERO_DEV_SOURCE_ROOT and the global
allowDevSourceUpdates setting. The setting defaults off, cannot be granted by
a project override, and is independent of allowModelSelfExtension.
Before the next turn, the wrapper snapshots changed source, builds an immutable generation, checks its session checkpoint, and drains engine-owned resources before activation. History, scope decisions, task state and accounting survive. Contained regular-file aliases are copied into the snapshot; directory aliases and aliases escaping Core source are rejected. Failed builds or incompatible checkpoints retain the current engine. Turning the setting off prevents later replacement without reverting the active generation.
The UI shell, injected provider/MCP clients and shared dependencies stay pinned. Rebuild and restart for changes to those components. A development engine runs with the host process’s permissions; source replacement is not a sandbox grant.
Local qualification exercised a continuing session across source activation, broken-source rollback and flag disablement, with retained scope metadata and rejection of an escaping source alias. The provider was deterministic and local; this does not qualify hosted inference, desktop installation or provider-generated improvements.
Upstream aliases, serving changes, and runtime fallbacks can change model behavior. Retain observed outcomes and re-evaluate version-pinned candidates.
Verification evidence
Section titled “Verification evidence”CoT faithfulness varies by model and task. Enforce scope and promotion rules outside the model. Model-supported claims and model-only rejections remain unresolved in the shared hunt ledger. Existing ledger files remain unchanged; re-evaluate older model-only disprovals before using them as settled evidence.
Revision-aware codebase learning
Section titled “Revision-aware codebase learning”Scoped native-API research runs use remember_codebase for architecture and
dataflow notes in the private, redacted hunt-memory store. The host selects the
repository root and hashes cited files.
A fresh eligible run receives up to six current notes as untrusted hints. Changed, missing, out-of-scope, symlinked, or multiply linked evidence invalidates the note. Hashes identify source versions; interpretations still need verification.
Verification runs neither receive these notes nor get the learning capability.
ZERO_DISABLE_HUNT_MEMORY=1 disables recall and persistence. Resumed runs keep
their existing context rather than silently receiving new notes.
Source access consent
Section titled “Source access consent”Because the model proposes edits to its own source tree, the operator must explicitly opt in:
allowModelSourceAccess: truein the evolution config — the model sees the source files listed insourcePaths(as a digested, read-only content reference in the proposal prompt, never as a live filesystem path during generation). Without this flag,runEvolutionrefuses at startup.--allow-source-accesson the CLI overrides the config for one-off runs.
The model receives selected snapshot text, not unrestricted host filesystem
access. Keep credentials and private evaluation data out of sourcePaths;
selection is not a secret-redaction mechanism.
Setup invariants
Section titled “Setup invariants”- Keep config JSON and private evaluation answers (expected values) outside
all selected
sourcePaths. The snapshot copies every file insourcePaths— config fields likeexpectedwould be readable by the model if placed inside a source directory. - Do not put hidden fixtures, oracles, or answer keys inside the worker
source tree. Even with
allowModelSourceAccess: false, a snapshot including a file with expected answers is a data leak. - The CLI rejects config files that would be included in selected source. This includes resolved symlink targets: if the config file resolves inside selected source, the CLI config loader rejects it before generation. Programmatic callers must keep private answers outside selected source too.
Feedback across evolution passes
Section titled “Feedback across evolution passes”Each evaluated proposal is retained beside its execution receipt. A later pass can recover development observations from the most recent compatible evaluated version in the active lineage, including an active version after promotion. Recovery checks proposal, receipt, configuration, and snapshot integrity; missing, incompatible, or damaged history does not enter the model context.
The objective, selected/editable paths, execution contract, and fixture corpus must remain compatible. Changing the proposal model or remaining spending budgets does not erase otherwise compatible observations. Held-out and negative-control attempts, scores, and expected answers are not recovery feedback.
evolve run --watch accumulates spend within one process and stops after a
no-change pass. Restarting loses that campaign budget state. Unattended restarts
require a separate budget control.
Trust boundary
Section titled “Trust boundary”The offline source-evaluation path isolates candidate workers from the operator-owned controller and evolution store. Within that configured sandbox boundary, a candidate process is not granted access to:
- the operator’s network, credentials, or engagement workspace;
- writes to the evolution store (the controller publishes snapshots and receipts);
- persistence outside the disposable worker.
Host root access or write access to the evolution store permits tampering with snapshots, receipts, and registry history. Hash chaining detects inconsistent edits; it provides no external signature or protection against a complete rewrite.
Operational plugins can request authorized tool/model services through the host broker. Workspace-trusted ESM executes with host permissions. The offline worker’s restrictions apply only to that evaluation path.
Automatic promotion
Section titled “Automatic promotion”autoPromote: true permits promotion after all gates and canary trials pass.
The default is false.
Future-worker version pinning
Section titled “Future-worker version pinning”Every promoted version produces a content-addressed snapshot in the evolution
store (storePath). The 0 evolve exec command pins and executes that
snapshot against arbitrary JSON input, with the stored backend’s offline,
credential-free isolation:
0 evolve exec --config ./evolution.json --run-id <id> --input '{"file": "src/main.ts"}'The command:
- pins the active version for a new run ID, or reuses that ID’s existing version;
- copies its files into a fresh isolated worker;
- runs the config’s
commandwith the provided input; - for known config cases, validates stdout against the stored
expectedanswer and rolls back the active version to its parent on mismatch (operator cancellation excluded); - returns the execution result.
Unknown inputs have no ground truth and are never auto-labelled. Execution is always offline — no provider credentials, no network, no engagement tokens.
Choose a fresh --run-id for a new input or to pick up a later promotion.
Reusing an ID retains its original code and input digest, even after
promotion or rollback; changing the input under the same ID is rejected.
The ID is your execution identity, not a candidate UUID to select. Editing the
supplied config cannot change the stored command/image/limits of an existing
version.
Deploying an evolved source finder
Section titled “Deploying an evolved source finder”deep-review can opt into the active source version instead of its native
prompt-backed finders:
0 deep-review ./target-repo --evolution-config ./evolution.jsonThe evolution config must evaluate the same source-finder protocol used by the review. Each case input has exactly these fields:
{ "schemaVersion": "0.finder.input/v1", "file": { "path": "src/handler.js", "content": "db.query(req.query.sql);\n" }, "lensId": "injection", "challengeHint": "Inspect whether untrusted input reaches SQL execution."}The command emits one JSON value with exactly these fields:
{ "schemaVersion": "0.finder.output/v1", "findings": [{ "title": "Potential SQL injection", "severity": "high", "line": 1, "analysis": "Request input reaches the query; verify parameterization and reachability." }]}These are unconfirmed leads. Empty findings use []; fixture expected values
use the same output protocol and the usual controller-owned exact-JSON oracle.
The worker cannot assign finding status, another file path, or verifier results.
The host binds each lead to the supplied file and checks its line range.
Ordinary independent verification still runs after finder execution.
The review pins the active version before starting work. Each finder invocation gets a child pin bound to that same version and its own input. A promotion or rollback changes future reviews, not an in-flight review. The stored version’s command, image identity, and limits remain authoritative; editing the supplied config cannot change an already-promoted worker.
Execution uses the same configured backend as evolve exec, with no host
execution fallback. The worker receives one scoped file of at most 1 MiB, not a
target-directory mount. Invalid protocol output is a worker failure and enters
the existing rollback path. A missing active version fails closed.
--models cannot be combined with --evolution-config. Local compute uses the
stored maxEvaluationCostUsd budget for the review, separately from the model
token ledger. Reports include evolution_version_id and
evolution_compute_cost_usd. Controller records retain input digests and private
execution receipts; worker output may quote target source and remains sensitive.
Canary and rollback
Section titled “Canary and rollback”After evaluation passes, a candidate enters canary:
canaryTrials(default 2) counts additional complete executions of the sealed corpus, not fresh datasets or live target traffic. All must pass.- A canary that fails is automatically rolled back to its parent version.
- Acceptance of successive candidates leaks a weak signal about a reused held-out set. This is not proof against adaptive overfitting over many generations. Operators should periodically supply fresh, independently curated cases to each lane.
0 evolve rollback --store <path> --version <id>retires the specified version (must be the current active or canary version) and restores its parent.--versionnames the version to retire, not a desired historical destination. Rollback preserves the retired version’s snapshot and receipt in the registry alongside the parent.
Worker admission and scale
Section titled “Worker admission and scale”Source-evolution workers and executable-plugin guests share one admission gate per controller process, across Docker and smolvm. It reserves worker slots, configured guest memory, and CPU until teardown completes. It does not pool or reuse guest state. Evaluation cases still run sequentially in fresh guests.
Set these environment variables before the controller’s first worker starts:
| Variable | Default | Meaning |
|---|---|---|
ZERO_WORKER_MAX_ACTIVE | 4 | Maximum active guest reservations |
ZERO_WORKER_MAX_QUEUED | 64 | Maximum waiting root calls |
ZERO_WORKER_MEMORY_MB | Half of available process memory, capped at 8192 MiB and floored at 32 MiB | Aggregate guest-memory budget |
ZERO_WORKER_CPUS | Node’s available parallelism | Aggregate configured guest CPU budget |
Overrides must be positive finite numbers; all except CPU must be integers. A request larger than its resource budget fails immediately. Root calls wait in FIFO order; a full queue rejects new work. The existing execution timeout covers queue wait, preparation, and execution. Queue time is not added to the backend execution duration used for compute-cost accounting.
Cancellation removes queued calls before they can boot. Active reservations remain held while the backend cancels and cleans up. A nested plugin call gets its own reservation immediately or fails for insufficient capacity: it never waits behind a parent that is holding the resources it needs.
Unconfirmed teardown stops further admissions and rejects queued work. Inspect the reported container or retained smolvm run directory, verify that the remaining guest resources have been removed, then restart the controller. An elapsed cleanup timeout is not proof that resources were released.
This is not a host-wide or distributed scheduler. Multiple controllers need explicit per-process budgets whose sum leaves room for host/runtime overhead, plus host or orchestrator resource limits. For a remote Docker daemon, configure budgets for the worker host rather than relying on controller-host defaults. Plugin metadata operations such as listing and rollback do not boot guests; admitting new plugin code does, because validation imports that code.
Runtime cost qualification
Section titled “Runtime cost qualification”A local Linux/KVM measurement on 2026-09-17 used the same cached Node 24 Alpine image, one CPU and 512 MiB per guest, non-root execution, no guest network, and read-only snapshots through the actual sandbox APIs. Three sequential short jobs per backend, with alternating backend order, measured:
| Backend | End-to-end duration range |
|---|---|
| Docker | 297–318 ms |
| smolvm 1.14.6 | 2.72–3.69 s |
These are small-fixture lifecycle timings on one i9-13900 host, not fleet throughput, production-toolbox timings, or isolated kernel-boot measurements. The same qualification exercised 26 guests, nested calls, a two-worker ceiling, cancellation, source immutability and fresh guest state, with no new containers or smolvm staging directories left afterward.
Docker remains the default. Choose smolvm for its separate guest kernel, not an assumed speedup. Do not share dirty guests between evaluation cases or tenants to remove startup cost. No Rust rewrite or warm-pool performance claim follows from these measurements.
Runtime prerequisites
Section titled “Runtime prerequisites”With the default Docker backend, the config’s image must be:
- available locally (pulled in advance);
- contain the runtime for the configured
command(e.g.node,python3); - ship no credentials, provider keys, or engagement scope.
Evolution runs resolve the image tag to an immutable image ID. Stored worker configurations retain that ID; resumed workers do not follow a subsequently retagged image.
Docker execution requires access to a local Docker daemon; source proposal generation separately requires a configured model provider. The Docker sandbox uses:
docker create+docker startlifecycle — each execution is a separate container; cleanup runs with a bounded timeout after the execution finishes.--network none— no egress- Read-only root filesystem except:
/workspace— writable, executable tmpfs where the snapshot files are placed and builds run/tmp— noexec tmpfs (direct execution blocked; interpreters can still read files)
- Input delivered over stdin — no host-side input file inside the container
- The non-root host UID/GID,
--cap-drop=ALL, and--security-opt no-new-privileges --pids-limit=64, bounded memory and swap, and configured CPU limits
Containers share the host kernel. These restrictions reduce privilege; they are not a guarantee against container escapes. Use a dedicated worker host or VM when evaluating hostile code.
Local smolvm backend
Section titled “Local smolvm backend”Set "backend": "smolvm" and "imageArchive": "/absolute/path/to/node.tar"
in the same evolution config. Keep image as the image’s descriptive name for
the initial run, or provide the expected sha256:<archive-hash> to require exact
bytes. The controller resolves it to the archive’s SHA-256 before recording a
version. Stored execution and approval use that recorded identity, not a newly
resolved replacement image.
This backend is qualified for non-root Linux, KVM, Node 24+, util-linux
setpriv, and smolvm 1.14.6. Other smolvm versions and host platforms are rejected
until their lifecycle is qualified. Install the complete upstream runtime bundle,
not just smolvm-bin, and put its launcher on PATH. Existing processes need
restarting after group membership changes; do not make /dev/kvm world-writable.
The toolbox stays an OCI image. A docker save archive is one way to provision
it; Docker is not used to execute smolvm workers:
docker pull node:22-alpinedocker save node:22-alpine -o node.tarThat is a small Node worker image, not the security toolbox. To provision the declared pentest/identity/Foxguard inventory without building the CLI application:
docker build --target toolbox -t 0-toolbox:local .docker save 0-toolbox:local -o toolbox.tarnode scripts/smoke-smolvm-toolbox.mjs ./toolbox.tarThe runtime target remains the full distribution: it adds the CLI to the same
toolbox. Both targets default to non-root execution. A Docker daemon is needed
for these build/export commands, not for the subsequent smolvm run.
The toolbox qualification checks startup of 37 commands, system and AD Python imports, Nmap TCP-connect discovery and curl against a guest-owned loopback server, and Foxguard detection of an unsafe-eval fixture with a clean control. It checks UID/GID 1000 and source immutability. It does not authenticate to real identity providers, scan external targets, or establish general scanner accuracy. The exercised profile is 2 vCPUs, 3072 MiB RAM, 4 GiB writable storage, a 1 GiB overlay and a 180-second host deadline. Optional SecLists and browser/privileged tooling are not covered by that qualification.
Use the toolbox archive in the same evolution config when a worker needs those
tools; set cpus: 2, memoryMb: 3072 and timeoutMs: 180000 for this profile.
The small Node image remains appropriate for narrow source-evolution fixtures.
Full toolbox import costs more than importing the small runtime image.
Neither image selection makes console/PTY execution globally sandboxed; see
Execution isolation.
The archive must contain /bin/sh and the runtime/dependencies needed by the
worker. Provision it before evaluation. No automatic pull or dependency download
runs inside a candidate. Start with cpus: 2, memoryMb: 2048, and
timeoutMs: 60000; CPU counts must be integers. Smaller profiles are not guaranteed
to accommodate image import and guest startup.
Each invocation:
- copies and hashes the archive into a private run directory before boot;
- starts an offline, UID/GID 1000 guest with
--unprivileged; - mounts the sealed source read-only at
/snapshot, then copies it to writable, guest-local/tmp/0-workspacefor builds and execution; - sends only the case input through stdin and returns bounded stdout/stderr;
- uses 4 GiB writable storage and a 1 GiB VM overlay, with configured CPU/RAM limits;
- ignores ambient project Smolfiles and isolates host runtime state and caches;
- enforces a host deadline covering preparation and execution, then waits for VM and cleanup-helper termination before deleting its private state.
Timeout, cancellation, output overflow, image mismatch, and cleanup failure are
failures, never permission to run on the host or switch to Docker. Cleanup failure
retains its private directory and reports the recovery path. evolve exec forwards
SIGINT/SIGTERM into this cancellation path.
These controls are not identical to Docker’s: the guest has its own kernel and
a disposable writable filesystem; Docker’s PID limit and noexec tmpfs settings
are not claimed for smolvm. VM isolation is not a proof against hypervisor escapes.
This is the evolution-worker backend, not a global redirection of console/PTY,
replay, or exploit commands into a VM.
Execution protocol
Section titled “Execution protocol”The configured command receives the case input as a single JSON line on
stdin. If buildCommand is specified, it runs first (stdout redirected to
stderr so it never pollutes the answer channel), then command runs. The main
command must emit exactly one JSON value on stdout — that is compared
against expected using canonical-JSON equality.
Example: if the config specifies
"command": ["node", "fixture.mjs", "--mode", "eval"]the process receives {"n": 4}\n on stdin and must write
{"classification":"even"}\n to stdout (or {"classification":"odd"},
{"classification":"error"}, etc.).
Model for proposal generation
Section titled “Model for proposal generation”When config.model is set, source proposals are generated through that model
via LlmApiRuntime (the standard API runtime). When omitted, the configured
provider default is used. Model identity is used to price actual usage — the
maxModelCostUsd ceiling tracks real token spend.
Proposal generation can inspect up to eight recent retained versions of the
same artifact kind through read_source(versionId). Archives are reference
material: reads are restricted to paths still present in the current snapshot,
and edits must match the current baseline’s file digests. Archived source does
not grant access to evaluation receipts or hidden fixtures.
Within an evolution pass, the next attempt receives the previous proposal and bounded development-lane stdout/stderr. Held-out inputs, expected answers, and negative-control observations are not supplied to the proposal model.
maxAlternativeParents optionally enables bounded archive-parent selection for
ordinary source evolution as well as executable-plugin evolution. Omitted or
0 disables it; 1–10 bounds inspected retained candidates/retired versions.
Selection checks compatible configuration and artifact/receipt integrity, then
ranks development match fractions only. The chosen archived source is a
proposal starting point, not an active worker. Its edited candidate still has to
beat the current active baseline across every evaluation gate. parentId
records that baseline; alternativeParentId records the source provenance.
This is not open-ended archive search or permission to tune against held-out scores.
Research basis and remaining limits
Section titled “Research basis and remaining limits”A Programming Paradigm for Spatiotemporal Composability (Shi, Zhang, and Cui, 2026) supplies the component-lifecycle rationale. Its temporal composability tracks reversible effects so removing a component removes its registrations and owned resources. Its spatial composability makes dependencies explicit and reacts when providers appear, disappear, or change. Cordis implements the model with a component loader and hot module replacement; DSH’s architecture uses services for the agent loop, tools, model adapters, and session log.
- The paper’s sections 6.1 and 6.3 distinguish reversible, context-owned effects from external emissions and from sandboxing. Removing a listener can be reversed; sending a network request cannot simply be undone. Untrusted code still needs an execution boundary.
- The paradigm is language-independent (section 6.4); upstream Cordis is TypeScript and its API is explicitly unstable. A Python SDK client or Python code-execution tool is not automatically a Python component host.
- DSH’s custom/web profiles support live patch reload, but its shipped headless, SDK, and ACP profiles apply composition at startup. Do not infer same-session CLI replacement merely from DSH’s plugin architecture.
- This is a composability paper, not a benchmark demonstrating autonomous security-quality improvement or crash-safe, multi-day agent operation.
The current source pins @deepseek-ai/cordis@4.0.2, with 0 generation,
guest-bridge, accounting, and frontend contracts. Component cleanup and external
effects require separate qualification.
Research informing evaluator-driven iteration:
- Self-Harness (Zhang et al., 2026, v3) combines verifier-grounded weakness mining, fixed-model code proposals, and regression checks. Accepted edits preserve aggregate pass counts on both held-in and held-out splits and improve at least one. All nine tested model/benchmark pairs improved; the largest relative gain was 132%. The bounded, fresh-environment experiments establish neither cybersecurity gains nor open-ended improvement.
- Darwin Gödel Machine (Zhang et al., 2025) searches an archive of modified coding agents. Reported objective hacking and fabricated logs motivate controller-produced receipts. 0 has no equivalent open-ended archive search.
- AlphaEvolve (Google DeepMind, 2025) pairs generated programs with automated evaluators. Cybersecurity gains require separate measurement.
- GEPA (Agrawal et al., 2025) reflects on execution trajectories and combines candidates. 0 retains development feedback and source history; GEPA’s Pareto search is unimplemented.
- The reusable holdout (Dwork et al., 2015) addresses adaptive evaluation-data reuse. 0’s canaries measure repeatability on the configured corpus. Independent generalization and the paper’s privacy-based mechanism remain outside this implementation.
- Hierarchical Self-Improvement (Zhou, 2026) evolves harnesses and evolver strategies with a frozen outer anchor and a DeepSeek backbone. Results depend on feedback and model capability. DeepSeek Harness is a separate runtime project.
- EVOHARNESSBENCH (Ke et al., 2026) reports forgetting and inconsistent gains. Evaluate retention when changing tools, skills, or agents.
- Post-Hoc Reasoning in Chain of Thought (Cox et al., 2026) finds pre-committed answers and misleading rationalizations in the tested models and tasks. Candidate explanations require independent evidence.
- OpenAI’s Astra system card reports reduced monitorability with improved measured alignment; it establishes no causal role for recurrent depth. Enforce filesystem, credential, execution, and promotion boundaries outside model output.
- A Little Depth Goes a Long Way (Merrill and Sabharwal, 2025) studies transformer depth and expressivity. It supplies no equivalent result for repeated API calls. Measure task outcomes, latency, and cost.
0 uses fixed acceptance criteria, independently labelled controls, restricted execution, and versioned rollout. Operators supply fresh evaluation cases. Automatic ground-truth curation, held-out rotation, and general security-quality gains remain unestablished.
Tracked implementation work:
- Crash-safe campaigns and feedback-driven resume (#41)
- Longitudinal capability retention (#37)
- Adaptive holdout exposure and rotation (#40)
- Measured, development-only archive search (#39)
- Evidence provenance and incompatible comparisons (#38)
Autonomy and hot-reload boundaries
Section titled “Autonomy and hot-reload boundaries”These paths are distinct; an accepted source candidate is not automatically a replacement for the stock target-facing 0 process.
| Path | What changes | What remains fixed |
|---|---|---|
| Finder-lens overlay | Newly started hunts load promoted lens content without rebuilding the CLI. | Active hunts retain their captured lens content and version identity. |
| TUI evolution settings | The watcher can stop and reconfigure while the TUI remains open. | Disabling or restarting a watcher must not let stale callbacks publish a new result. |
| Source evolution | evolve run --watch --auto-promote can select a new accepted snapshot for subsequent evolve exec runs. | Existing run IDs retain their original snapshot, stored configuration, and input identity. |
| Source finder deployment | deep-review --evolution-config selects the active source snapshot for each new review. | Every finder call in that review inherits the parent pin; verification and host policy are not rewritten. |
| Skill/router installation | Training loops install exact authorized artifact bytes. | Authorization does not hot-swap a model already loaded by another process. |
| Executable plugin | An enabled agent submits or evolves actual code; later calls select the active retained version. | An invocation pins its version and declared capabilities; structural admission is not measured improvement. |
| Development engine replacement | An explicitly enabled development console loads changed Core source between turns without losing the session. | Trusted host execution; UI shell, injected clients and shared dependencies stay pinned. Build and checkpoint failures retain the current engine. |
| Live harness generation | Core/OpenTUI support replacing agent.driver and ui.view, including namespaced UI commands/settings, in the same session. | Session history and accounting survive; generation changes wait for a defined checkpoint. This is not durable campaign recovery. |
Observation capture is not independent truth: source consent and operator-curated positive, held-out, and clean-control fixtures still gate automatic synthesis. Promotion of a finder lens changes a prompt-backed detector, not FoxGuard’s compiled Rust engine or rules.
Lifecycle checks measure orchestration. Provider behavior, sandbox execution, and detection quality require their own checks.
Live harness component contract
Section titled “Live harness component contract”The live-harness implementation has core and OpenTUI consumers, including host-owned recovery controls. Treat it as a research-preview extension surface. The historical candidate measurements below cover particular executions, not every current frontend, desktop installation, real-provider route, or hosted deployment.
Sources: plugins/live-harness.ts,
console/turn-engine.ts,
tui/harness-context.tsx,
tui/harness-trust-controls.tsx.
Composition and language support
Section titled “Composition and language support”HarnessGenerationSpec contains a label and the complete desired provider
graph. Each HarnessProviderSpec declares an id, required provider IDs,
services, and one execution source:
| Source | Contract | Authority |
|---|---|---|
sandboxed | Retained executable pluginId, versionId, and toolName | Guest execution; operational effects use the existing authorized SDK broker |
trusted | Self-contained ESM entry and files, with optional ui.tui / ui.web entries | Explicit workspace grant; code executes with host permissions |
The current replacement services are agent.driver and ui.view.
Additional services require implementation before they enter the catalog.
Python already works through MCP and the language-neutral source-worker
command protocol when the selected image contains Python. The current
executable-plugin admission accepts .ts / .mts, not .py. First-class Python
components still need a runner/SDK implementing the same lifecycle and broker
protocol; they should not get a second registry or a Python-specific harness.
Backend selection and language selection are separate from plugin composition.
Activation, state, and UI interaction
Section titled “Activation, state, and UI interaction”The lifecycle contract is:
- Submit a complete candidate graph and retain its immutable source identity.
- Validate dependencies, services, trust, and source provenance before use.
- At a checkpoint, wait for old-generation invocations and prepare/migrate candidate state in dependency order.
- Publish one active generation only after preparation succeeds. Failed preparation leaves the old generation available.
- Remove retired registrations and dispose owned resources in reverse dependency order. Rollback selects a retained implementation and migrates current state; it does not erase task history or undo external effects.
HarnessControl carries submit, rollback, disable, and list. A submitted
generation can be pending before it becomes active; consumers must display
that distinction. HarnessSnapshot carries active/previous/pending generation
IDs, status, providers, views, namespaced commands/settings, and trust state.
It is the common frontend catalog, not an alternate source of authority.
HarnessView supplies bounded text, Markdown, table, progress, and action
blocks. Commands and settings carry provider identity. An explicit interaction
names its generation, provider, and contribution; a stale generation must not
dispatch against a replacement provider. Returned requestedPrompt content is
for an explicit interaction, never automatic submission while rendering.
Sandboxed view data is not host JavaScript. Trusted UI modules require the
separate workspace grant and must not remove the host-owned recovery controls.
Use and recover a live generation
Section titled “Use and recover a live generation”Start an OpenTUI chat with self-extension enabled and a provisioned
executable backend. If it was disabled when the chat
was created, enable it in Settings and use /new-chat; changing a setting does
not reconstruct that live session.
The model-facing self_extend actions are:
| Action | Input | Result |
|---|---|---|
harness_submit | generation: {label, providers: [...]} | Retains the complete provider graph and queues activation |
harness_list | No generation required | Current, previous and pending identities plus the contribution catalog |
harness_rollback | Optional generation_id | Selects a retained generation; omission selects the previous one |
harness_disable | No generation required | Queues restoration of built-in behavior |
A sandboxed provider references an exact retained executable version, not
whichever plugin version is active later. Its run receives
{phase: "activate" | "driver" | "view" | "dispose", state, input} and returns
{state, output}. This interface is separate from a normal tool’s arguments.
self_extend refuses harness actions where the caller has not supplied a live
harness host; the ordinary native-agent tool being present is not sufficient.
In the OpenTUI, /harness (default shortcut Ctrl+G) opens the host-owned
controls. Inspect current/pending generation details, show the conversation,
roll back, or disable a contributed generation there. The t action requests
workspace trust and requires explicit confirmation of the canonical workspace;
leave it off for sandboxed components. It authorizes arbitrary host ESM for
that workspace, not just one tool call. Revoking it does not undo external
effects already performed.
Source: agent/tools/system.ts
and agent/tools.ts.
Autonomy without a second permission system
Section titled “Autonomy without a second permission system”Within an authorized policy and spending budget, agents can author code, compose skills, compare alternatives, and replace admitted components.
New sessions default to model self-extension enabled through
DEFAULT_ALLOW_MODEL_SELF_EXTENSION = true in
packages/shared/src/desktop-console.ts. An explicitly selected or persisted
false takes precedence. The desktop checkbox initializes from the shared
default; existing disabled sessions remain disabled and are not reconstructed.
The lower-level SelfExtensionRegistry requires its caller to pass
enabled: true; that constructor contract is not the product default.
Autonomy is a separate control. The shared DEFAULT_AUTONOMY_MODE = "yolo"
constant does not mean every desktop session starts in YOLO: the existing
desktop authorization flow remains unscoped-standard/scoped-YOLO.
Self-extension enablement does not change that authorization flow or grant
workspace-trusted execution.
Source evaluation has separate controls: allowModelSourceAccess admits selected
snapshot text into proposal generation, and autoPromote authorizes candidates
that pass the configured gates. Those remain explicit opt-ins. Model
self-extension does not silently enable either one.
Workspace-trusted execution defaults off until the operator persists a grant for the canonical launch workspace. It is not inferred from YOLO, self-extension, a model-submitted manifest, or project-controlled configuration. Trusted code can access host resources, including credentials, and can crash its process; component lifecycle management is not an OS sandbox.
Long-horizon self-evolution
Section titled “Long-horizon self-evolution”Hot replacement addresses continuity within a live process. Long-running tasks additionally need:
- Durable recovery: checkpoints bind task progress, active generation, component state, pending work, and evidence. Restart must restore or explicitly reject incompatible state, not quietly restart with a new harness.
- Durable accounting: campaign budgets, usage, and evaluation exposure must survive restart rather than reset with an in-memory watch loop.
- Useful learning: revision-aware codebase notes, development feedback, and executable skills are retrieved when relevant and invalidated when stale.
- Retention: proposed changes face old capabilities as well as new tasks; archive search and repeated holdout reuse do not prove generalization.
- Long-session qualification: repeated upgrades, failed preparation, dependency changes, rollback, stale UI input, cancellation, and cleanup must preserve task state and must not duplicate model/tool effects.
Self-Harness suggests a concrete optimizer contract around this lifecycle: group recurring development failures by verifier outcome, causal status, and reusable mechanism; retain examples of passing behavior; then ask for distinct, targeted candidate changes. Record the model, harness generation, environment, targeted failure, changed surface, and expected regression risk for each proposal. Compare candidate and baseline under compatible conditions and retain rejected attempts. Re-evaluate the combined generation before publishing multiple individually successful edits. Keep held-out traces and answers out of proposal context; repeated acceptance still exposes information about the holdout and needs separate exposure management. These are optimizer requirements, not features supplied automatically by Cordis or by the current generation wire types.
Multi-day operation and crash-safe campaign recovery remain development goals. Ordinary session history, task ledgers, retained plugin versions and development-engine checkpoints are useful but do not constitute durable evolution spending, holdout-exposure accounting, or exactly-once tool effects. See feedback across passes and the campaign work. First-class Python components require further implementation.
Local candidate measurements
Section titled “Local candidate measurements”On 11 September 2026, an uncommitted candidate at observed base
be299cbe passed 12 compiled Node host/console scenarios with real per-phase
Docker execution and deterministic model fixtures. Two compiled native scenarios
used trusted ESM, deterministic models, and authorized bash.
The 12 replacement/rollback cycles produced these observations:
| Measurement | Result |
|---|---|
| Owned resources | 1 while active; 0 after close plus 50 ms |
| Runtime handles after close | No Timeout or ProcessWrap; 2 stdio PipeWrap remain |
| Heap, cycles 1 → 12 | 17,572,032 → 18,050,864 bytes |
| Heap, whole probe | 16,609,464 → 18,912,840 bytes |
| Distinct trusted module URLs imported | 21 |
Heap growth has no established cause. Module eviction, multi-day stability, real-provider behavior, hosted billing, and security-quality gains remain unmeasured. A fresh engine process is required for trusted ESM reclamation.
Configured limits are 32 retained generations, 256 KiB state/view/output JSON, and 256 KiB / 128 messages per executable SDK model request. At the generation cap, further submissions reject while the active generation stays usable.
A separate sandbox-only fixture on the same compiled candidate created and activated driver versions with host trust off. It retained session/history, pinned the first version until explicit activation, and migrated JSON state. Every sandboxed phase used a fresh guest; restart does not reconstruct active state.
Later shutdown repairs have source-unit coverage only. These compiled measurements apply to the earlier candidate, independently of frontend and installation checks.
CLI reference
Section titled “CLI reference”
0 evolve
Section titled “0 evolve”0 evolve Autonomous self-improvement run --config <path> Run evolution: propose, evaluate, and optionally promote [--watch] source candidates. Watch mode iterates sequential passes; [--json] stops on any failed pass (cannot safely retry unmetered failed generation). Budgets are cumulative across watch passes. [--auto-promote] [--allow-source-access] [--max-passes <N>] status --store <path> (required) Show evolution registry: active/canary/parent [--json] version, snapshot/receipt identities, and registry events. Does NOT aggregate historical costs — run output reports model and evaluation costs; histories and receipts retain them individually. promote --store <path> Run required canary trials against the pinned stored --version <id> config and activate the accepted exact candidate. [--json] Use for awaiting_approval results and training promotion guidance. No regeneration — activates only the named candidate version. rollback --store <path> Retire the current active/canary version and --version <id> restore its parent. --version names the [--reason <text>] version to retire, not a desired destination. Reason optional (default "operator rollback"). exec --config <path> Execute a pinned evolution version snapshot against an --run-id <id> isolated worker with no network. Pins the --input <json> active snapshot from the specified run. Known config [--json] cases are validated; mismatch rolls active back to parent. feedback capture --input <path> Capture an evidence-backed observation from a [--store <path>] JSON file (CaptureObservationInput fields: [--allow-source-access] classHint, sinkPattern, exampleFileLine, whyMissed, source, scanId, sourceRevisionDigest). Consent in the JSON is ignored unless --allow-source-access set. approve --id <id> Approve a pending observation with operator- --fixtures <path> curated ValidationFixture arrays. The curation [--store <path>] JSON requires positives, heldOut, and [--allow-source-access] negativeControls — all real ValidationFixture objects. Verifies evidence and explicit consent before allowing synthesis. status [--store <path>] Show retained observations and their [--json] approval/processing status. release --id <id> Recover a crash-held observation claim after --claim-token <token> stopping its previous worker. [--store <path>]Error codes: 0 = success, 1 = user error, 2 = runtime error, 3 = interrupt.
0 lens-synth
Section titled “0 lens-synth”Finder-lens evolution remains a separate command — see lens-synth help.
0 lens-synth Evolve appsec finder coverage from curated misses --miss-input <path> Curated miss-input JSON ({ misses, corpus }) --registry <path> Durable overlay path (~/.0/lenses/...) --max-register <n> Cap promoted champions per input revision -m, --model <id> Synthesis model override --promote Persist a validated champion to the durable overlay --watch Poll the miss-input and process each new revision --poll-interval <ms> Watch polling interval (minimum 100ms) --status Show the active durable overlay and promotion ledger --rollback <lens-id> Retire one previously promoted overlay lens --json Print machine-readable outputConfig shape
Section titled “Config shape”An evolution config is a JSON file passed to 0 evolve run --config <path>.
Fields with defaults may be omitted.
This small example exercises the lifecycle; it is not a cybersecurity
benchmark. Create worker/src/checker.cjs with an intentionally imperfect
baseline:
const { readFileSync } = require("node:fs");const { n } = JSON.parse(readFileSync(0, "utf8"));const classification = !Number.isInteger(n) ? "error" : n % 3 === 0 ? "odd" : n % 2 === 0 ? "even" : "odd";console.log(JSON.stringify({ classification }));Save this as evolution.json alongside, not inside, worker/. Install
node:22-alpine locally with Docker before running; evolution never pulls
an image automatically. Configure the usual 0 model credentials separately.
{ "schemaVersion": 1, "sourceRoot": "./worker", "storePath": "./.evolution-store", "image": "node:22-alpine", "kind": "source", "sourcePaths": ["src"], "editablePaths": ["src/checker.cjs"], "command": ["node", "src/checker.cjs"], "objective": "Classify every integer as even or odd; classify non-integer inputs as error.", "allowModelSourceAccess": false, "autoPromote": false, "canaryTrials": 2, "repeats": 3, "maxIterations": 3, "maxModelCostUsd": 5, "maxEvaluationCostUsd": 5, "computeUsdPerSecond": 0.0005, "cases": [ { "id": "dev-even", "lane": "development", "input": { "n": 4 }, "expected": { "classification": "even" } }, { "id": "dev-multiple", "lane": "development", "input": { "n": 6 }, "expected": { "classification": "even" } }, { "id": "dev-odd", "lane": "development", "input": { "n": 7 }, "expected": { "classification": "odd" } }, { "id": "held-odd", "lane": "held-out", "input": { "n": 13 }, "expected": { "classification": "odd" } }, { "id": "held-multiple", "lane": "held-out", "input": { "n": 18 }, "expected": { "classification": "even" } }, { "id": "held-even", "lane": "held-out", "input": { "n": 22 }, "expected": { "classification": "even" } }, { "id": "clean-null", "lane": "negative-control", "input": { "n": null }, "expected": { "classification": "error" } }, { "id": "clean-string", "lane": "negative-control", "input": { "n": "2" }, "expected": { "classification": "error" } }, { "id": "clean-fraction", "lane": "negative-control", "input": { "n": 1.5 }, "expected": { "classification": "error" } } ], "promotionPolicy": { "minimumCases": 3 }}The low sample floor is only for this demonstration. Real evaluation needs larger independently curated positive, held-out, and clean-control corpora.
Required fields
Section titled “Required fields”| Field | Description |
|---|---|
schemaVersion | Must be 1. |
sourceRoot | Directory containing the source to evolve (resolved relative to config file). |
storePath | Evolution store for snapshots, receipts, configs, and registry. Must differ from sourceRoot and must not overlap any selected source path. |
image | Docker image reference, or initial smolvm image label / expected archive SHA-256. Stored workers retain an immutable backend-specific identity. |
sourcePaths | Relative paths (files or directories) within sourceRoot that form the snapshot. At least 1, max 256. |
editablePaths | Subset of sourcePaths the model may propose edits to. Each must be inside a sourcePaths entry. |
command | Executable + arguments run in the sandbox. At least 1 argument, max 128. |
objective | Free-text goal for the model (up to 16000 chars). |
cases | At most 1000 total, with at least promotionPolicy.minimumCases distinct cases in each lane (10 per lane by default). No duplicate IDs or inputs. input and expected must be finite JSON. |
computeUsdPerSecond | Compute cost estimate used for budget tracking. |
Optional fields with defaults
Section titled “Optional fields with defaults”| Field | Default | Description |
|---|---|---|
backend | "docker" when omitted | "docker" or "smolvm"; no automatic fallback between engines. |
imageArchive | (none) | Required only for smolvm. Regular archive file, resolved relative to the config file; symlinked archive files are rejected. |
kind | "source" | Artifact kind: source, skill, router, or lens. Determines allowed extensions and promotion policy defaults. |
buildCommand | (none) | Optional build command run in the sandbox before evaluation. |
model | (none) | Model override for proposal generation. Uses the configured runtime by default. |
allowModelSourceAccess | false | Explicit consent: the model sees source file content in proposal prompts. runEvolution refuses without this. |
autoPromote | false | When true, a candidate passing all gates advances without per-candidate approval. Default false. |
canaryTrials | 2 | Additional evaluation repeats on the same configured corpus before the version becomes active; not fresh-data trials. |
repeats | 3 | Executions per case per variant (baseline/candidate). At least 2. |
maxIterations | 3 | Maximum candidate-generation iterations per run. |
maxModelTurns | 12 | Maximum model turns per proposal. |
maxModelCostUsd | 5 | Model cost ceiling per run (USD). |
maxEvaluationCostUsd | 5 | Evaluation cost ceiling per run (USD). |
timeoutMs | 60000 | Per-execution timeout (100–600000). |
memoryMb | 1024 | Worker memory limit in MiB (32–16384); smolvm also needs room for its kernel and image import. |
cpus | 1 | CPU limit, up to 16; smolvm requires an integer count. |
maxOutputBytes | 65536 | Max retained stdout/stderr per execution. |
maxSourceBytes | 67108864 | Max total source size in a snapshot. |
maxChangedBytes | 262144 | Max total changed bytes across all edits in a proposal. |
promotionPolicy | {} | Promotion gate thresholds (defaults use conservative values from DEFAULT_IMPROVEMENT_PROMOTION_POLICY). |
maxAlternativeParents | Disabled when omitted or 0 | Inspect up to 1–10 retained alternative parents; rank development results only and re-evaluate against the active baseline. |
Promotion policy defaults
Section titled “Promotion policy defaults”{ "minimumCases": 10, "minimumDevelopmentLift": 0.05, "minimumHeldOutLift": 0.03, "maximumNegativeControlFpDelta": 0, "maximumCostMultiplier": 1.5}| Gate | Default | Requirement |
|---|---|---|
minimumCases | 10 | Minimum distinct cases per lane (configurable down to 3 for small experiments). |
minimumDevelopmentLift | 0.05 (5 pp) | Minimum success-rate improvement on development cases. |
minimumHeldOutLift | 0.03 (3 pp) | Minimum success-rate improvement on held-out cases. |
maximumNegativeControlFpDelta | 0 | Negative-control FP rate must not increase at all. |
maximumCostMultiplier | 1.5 | Held-out cost per success at most 1.5× a successful champion; see zero-success recovery below. |
Example: run the config above
Section titled “Example: run the config above”Start with explicit approval rather than unattended promotion:
docker pull node:22-alpine0 evolve run --config ./evolution.json --allow-source-access --no-auto-promote --json0 evolve status --store ./.evolution-store --jsonInspect each iteration’s state, decision and receiptPath. A successful
command can retain rejected proposals or an awaiting_approval candidate;
exit code 0 does not mean a promotion occurred. For a candidate that passed
evaluation, use its exact candidateId:
0 evolve promote --store ./.evolution-store --version <candidate-id> --json0 evolve exec --config ./evolution.json --run-id example-after-approval --input '{"n":12}' --jsonpromote reuses the stored candidate/config and runs required canaries; it does
not ask the model to generate a replacement. A later
0 evolve rollback --store ./.evolution-store --version <active-version-id>
retires that active version, not a selected destination.
For deliberate automatic selection, replace --no-auto-promote with
--auto-promote. Add --watch --max-passes 3 for bounded sequential passes.
Watch costs accumulate only in that process; restarting is not durable campaign
budget recovery. No successful candidate is guaranteed for this toy fixture.
Sealed evaluation lanes
Section titled “Sealed evaluation lanes”Three independent case lanes must be populated in every config:
- Development — calibration and candidate iteration. The model receives only development-case observations as feedback; it never sees held-out or negative-control answers.
- Held-out — generalization evidence, not tuning evidence.
- Negative control — false-positive tracking on known negatives. The candidate must not regress on these.
Evaluation cycle
Section titled “Evaluation cycle”For each iteration:
- Snapshot the current source into a content-addressed, immutable directory
under
storePath/snapshots/<id>/. Symlinks, hardlinked/special files, credential-like filenames, and nested store paths are rejected. Filename checks are not content redaction: secrets embedded in ordinary source still need to be removed by the operator before selection. - Propose edits via the model (isolated, source-text only, no live path access). The proposal must carry a rationale, individual file edits with before/after digests, and a model-cost receipt.
- Create candidate by applying edits to a COPY of the baseline snapshot. Edits are validated for path boundaries, bounded changed bytes, duplicate prevention, and protected-code rejection.
- Evaluate in a fresh, offline worker with a read-only source snapshot. Expected
answers stay in the controller — never in the candidate or model input. The
evaluation alternates baseline/candidate order across repeats to avoid
warm-cache bias. Wilson 95% intervals count distinct fixtures, not repeat
executions; unstable repeats receive an uninformative
[0, 1]interval and fail the stability gate. These intervals do not correct adaptive holdout reuse. A candidate must also retain every case/repeat the baseline solved. Higher aggregate scores cannot compensate for losing established behavior; a retention failure rejects the candidate through the execution-check gate. - Promotion assessment — the pure function
evaluateImprovementPromotionchecks identity, artifact binding, evaluator integrity, evidence, sample size, development lift, held-out lift, precision, and cost. Candidates that pass becomeeligible_for_canary(policy) orrequires_human_approval(source). On failure, the reason is recorded per gate. - Canary — if
autoPromoteis true,canaryTrialsadditional repeated evaluations run. If all pass, the candidate becomesactiveand the previous version is retired. A canary interrupted by controller restart is rolled back before the next evolution pass. - Record — every transition (
recorded,canary_started,promoted,rolled_back) is appended to the registry’s hash-chained event log. The config, evaluation receipts, and snapshots are retained understorePath.
storePath/ registry.json -- version registry with hash-chained event log configs/ -- content-addressed evolution configs snapshots/<id>/ -- immutable source snapshots receipts/ -- evaluation receipts keyed by candidate ID history/ -- retained pass results and recorded costs/errors pins/ -- run IDs bound to exact versions inputs/ -- run IDs bound to input digests executions/ -- individual worker execution receipts controller.lock -- exclusive controller lease (PID-bound)Schema version and receipts
Section titled “Schema version and receipts”The principal evolution records carry a schemaVersion field:
EvolutionConfig—schemaVersion: 1EvolutionEvaluation—schemaVersion: 1, containsreceiptDigestfor content-addressed integrityEvolutionVersion—schemaVersion: 1, storesconfigDigestandreceiptDigestlinking to its immutable artifactsEvolutionRegistry—schemaVersion: 1, carries a hash-chainedeventsarray
Immutable JSON artifacts are published once through an exclusive temporary file, fsync and atomic link; replaying identical bytes is allowed, replacing them is not. The mutable registry instead uses a serialized temporary-file write and rename. Its event chain and artifact digests detect inconsistent local changes, not an attacker who can rewrite the entire store.
Sources: artifacts.ts
and registry.ts.
Promotion gates
Section titled “Promotion gates”The default policy rejects a candidate unless every check passes:
| Gate | Requirement |
|---|---|
| Identity | Candidate ID matches the sealed result. |
| Artifact binding | Distinct SHA-256 base and candidate artifacts. |
| Execution checks | Complete, repeatable scenario executions, including the configured build command when present, with every baseline-successful case/repeat retained by the candidate. This is not an attestation from an external CI service. |
| Evaluator | Identical evaluator digest before and after evaluation. |
| Evidence | At least one retained evidence reference. |
| Sample size | At least 10 distinct cases per lane by default. Repeating a case does not increase the distinct-case count. |
| Development lift | ≥ +5 pp success rate. |
| Held-out lift | ≥ +3 pp success rate. |
| Precision | Negative-control FP rate rises by at most 0 pp. |
| Cost | Finite, nonnegative challenger cost per success; at most 1.5× a successful champion. |
A fully observed champion with zero successes and zero inconclusive results has no finite cost per success. A candidate with measured finite cost can pass this comparison; missing cost for a successful or inconclusive champion still fails closed. Evaluation spending ceilings and every other promotion check remain enforced.
A passing policy candidate is only eligible_for_canary — not deployed. A
passing source candidate always needs either explicit autoPromote: true or
human approval. Any failed check -> rejected.
Candidate-worker contract
Section titled “Candidate-worker contract”The promotion assessment is a pure decision over retained results. The evolution runner executes candidate code separately under this worker contract:
- sealed input artifact and explicit command;
- fresh Docker container or local smolvm guest;
- no engagement credentials;
- no production target egress;
- bounded CPU, wall-clock, disk, and model budget;
- retained stdout, stderr, evaluator receipt, and artifact digests;
- promotion changes future worker selection, never the active process.
Self-evolving finder lenses
Section titled “Self-evolving finder lenses”Finder-lens evolution (0 lens-synth) works alongside the evolve system but
remains a separate command. It evolves additive appsec finder lenses from
curated misses into a user-owned registry. Promotions go to
~/.0/lenses/appsec-archetypes.json, never the bundled registry. Each
promotion or retirement is recorded in the registry’s hash-linked ledger.
TUI automatic mode
Section titled “TUI automatic mode”The OpenTUI can own the lens-synth watcher while the TUI remains open. It is
disabled by default. The two Security settings control different decisions:
autoEvolveFinderLenses starts evaluation; autoPromoteFinderLenses separately
permits installation of validated champions. Start in validation-only mode:
{ "autoEvolveFinderLenses": true, "autoPromoteFinderLenses": false}Save that settings object as lens-settings.json, then run
0 config import lens-settings.json --yes, or set the values in the TUI.
This is a TUI settings file, not the source-worker evolution.json above.
Evaluation can consume model usage even with promotion disabled.
The watcher reads the curated inbox at
~/.0/lens-synthesis/miss-input.json and approved observations from the
feedback queue. Enabling it does not manufacture fixtures or approve captured
misses. Use 0 evolve feedback capture, then operator-curated
0 evolve feedback approve, as described in the CLI reference.
After reviewing validation evidence, enable autoPromoteFinderLenses if desired.
Status distinguishes evolve:dry-run, evolve:auto, evolve:waiting input,
evolve:promoted, and evolve:error. Source:
tui/lens-evolution.ts.
Lens corpus and receipts
Section titled “Lens corpus and receipts”Supply separate, nonempty positives, heldOut, and negativeControls.
Positive and held-out fixtures need an expected CWE, normalized file path, and
an exact line or inclusive line range. A supplied range takes precedence over
the exact line; the exact line must lie within that range. File paths are
relative to the fixture directory (or a file fixture’s parent), not basename
matches. Negative controls need independent cleanProvenance. Curation binds
content digests; evaluation uses private copies and rejects missing files,
symlinks, hardlinks, duplicate content, overlapping splits, and input drift.
The complete corpus is limited to 64 MiB, with at most 10,000 files per fixture.
The host matches CWE, full relative file path, and location independently;
the probe’s surfaced flag cannot award itself a pass.
All findings on negative controls count toward false positives, including unrelated classes.
The baseline is the captured lens content, not a label such as “baseline.”
Repeated development and held-out results, finding evidence, cost, and latency
are retained in the receipt. Missing measurements are inconclusive, not free.
Token costs are priced usage estimates, not provider invoices.
Future hunts retain the installed lens-version digest alongside each finding record, dropped finding, and timeout coverage gap. The JSONL hunt corpus retains that identity with subsequent verifier outcomes. A scan copies its lens set before dispatch: editing or retiring an overlay cannot relabel an active run. Captured observations retain source-revision and detector-version provenance. Refuted findings and known duplicates are not relabelled as missed coverage. Processed observations retain installed versions, validation reports, synthesis warnings, and rejection reasons, including runs that promoted nothing.
Automatic promotions retain the report and installed archetype under
validation-receipts/<lens-version-digest>.json beside the overlay registry.
Existing corpora without the required identity and control metadata must be
curated before they can pass; the old permissive format is not silently accepted.
Every probe, including an injected one, must expose the actual baseline lens snapshot and its canonical digest. Receipts bind candidate content, corpus metadata and byte digests, both repeated lanes, original finder evidence, and per-probe usage. Registration checks that the receipt belongs to that exact candidate and rejects altered receipt contents. These checks establish local evaluation provenance, not independent proof that a discovered vulnerability is exploitable. The usual verification process remains separate.
The TUI consumes approved observations without merging held-out fixtures into development data. Completed evaluations, including rejections and dry runs, retain their results rather than automatically rerunning the same input every poll. On startup, the consumer can recover a claim whose recorded owner is demonstrably dead in the same process scope. Live owners and unverifiable, legacy, or different-scope claims are not automatically released. Stop the old worker before manually recovering those claims with:
0 evolve feedback status --json0 evolve feedback release --id <observation-id> --claim-token <token>Queue and overlay writers fail promptly on lock contention. If a process dies
while holding a .lockdir, remove that empty lock directory only after
confirming no writer remains. Do not clear a live writer’s lock.
Session extension snapshots restore descriptor identities atomically. Function-valued contributed guards cannot be serialized: resume fails rather than dropping those restrictions. Descriptors are not executable tool bodies; source execution belongs in the isolated evolution-worker path.
Skill and router artifact promotion (LearningPromotion bridge)
Section titled “Skill and router artifact promotion (LearningPromotion bridge)”Skills and triage-router models are promoted through the evolution registry’s
authorization gate. The artifact-bridge.mjs script (sibling to
check_skill.mjs in packages/benchmark/scripts/train/) shells out to the
core authorizeEvolutionArtifact function:
node artifact-bridge.mjs authorize \ /path/to/evolution-store \ <version-id> \ /tmp/candidate-skill.yaml \ agent/skills/vulnerabilities/sqli-advanced.yaml \ skillThe skill-refine and active-learning loops require three flags when --promote
is passed:
| Flag | Description |
|---|---|
--evolution-store <path> | Path to the evolution store (registry.json + receipts + snapshots) |
--evolution-version <id> | Registry version UUID that authorized this artifact (must be currently active) |
--evolution-artifact <path> | Relative path within the version’s immutable snapshot |
Without all three flags and matching active-version authorization, promotion is refused. Authorize the exact bytes to be installed, not a precursor that is later retrained or transformed.
The Python loops install through artifact_install.py: it verifies the
candidate bytes against the bridge’s returned digest and publishes atomically.
Changing the candidate after authorization fails instead of installing
unapproved bytes. The bridge alone authorizes; it does not install.
Executable plugin evolution
Section titled “Executable plugin evolution”Model-authored executable plugins (the ExecutablePluginManager in
packages/core/src/plugins/executable.ts) support their own evolution path.
Calling manager.evolve(pluginId, profile, deps, context) runs the improvement
loop over the plugin’s active version snapshot, sharing the same sandbox
isolation, evaluation receipt, and promotion gate as the standard evolution
pipeline.
Key differences from standalone evolution:
- Snapshot source: the evolution source root is the plugin’s sealed snapshot, not an operator-controlled working tree. Only the files included in the snapshot are visible to the model.
- Backend locked: the evolution profile must use the same backend (Docker or smolvm) and the same immutable image as the active version. Cross-backend promotion is refused.
- Evidence status: a directly submitted version has
evidenceStatus: "structural"; an evolved and promoted version hasevidenceStatus: "measured"with its evolution receipt digest recorded in the version record. - Evolution store: each plugin gets a dedicated evolution store under
<manager-root>/evolution/<pluginId>/<evolutionEpoch>/<profileId>. - Rollback: a promoted evolution version can be rolled back via
manager.rollback()like any other version; the evolved snapshot and receipt are retained.
The profile’s optional maxAlternativeParents uses the same bounded
development-only archive selection as
standalone evolution. It is not a plugin-only API. When a candidate is promoted,
subsequent iterations use the new active baseline; alternativeParentId
preserves any distinct archived source origin.
The manager publishes a new executable version only after source promotion and
fresh executable admission succeed. An evaluated but unpromoted candidate
returns state: "not_promoted" and leaves the plugin active version unchanged.
A measured label describes the configured fixture evidence, not general tool
correctness. Unlike evolve rollback --version, manager.rollback(pluginId, versionId) selects the retained executable version to restore.
External hosts
Section titled “External hosts”An external coding agent can launch the CLI for complete workflows, or connect as a stdio MCP client for a narrow live-target tool set. Those are separate paths: the MCP tool roster does not expose source evolution or every CLI command. The client owns its reasoning and configuration; 0 retains the applicable tool-side scope and evidence checks. See Integrations, Architecture, and Benchmark methodology.
Live lifecycle checks
Section titled “Live lifecycle checks”After building the CLI and its dependencies, run the real provider-backed checks:
node scripts/smoke-source-evolution.mjsnode scripts/smoke-lens-evolution.mjsnode scripts/smoke-codebase-learning.mjsnode scripts/smoke-source-citation.mjsnode scripts/smoke-executable-plugins.mjsBy default, the source check requires a non-root account with Docker access and the
node:22-alpine image. It exercises generated source, independent evaluation,
approval, canaries, deployment, existing-reader pinning, and rollback using a
small credential-detector benchmark. It does not measure general scanner quality.
The executable plugin smoke check (smoke-executable-plugins.mjs) requires the
built @0/core package and a local Docker daemon (or smolvm with
ZERO_SMOLVM_IMAGE_ARCHIVE). It exercises the full lifecycle: submission,
execution, TypeScript argument passing, multi-owner plugins, skill composition
with Promise.all broker calls, source replacement, retained version discovery,
persisted failure counters, cold manager restore, rollback, nested call-budget
termination, and malformed-source rejection. An optional
ZERO_EVOLVE_REAL=1 flag enables a real provider evolution stage using the
configured model.
This smoke does not yet qualify live root-driver replacement, browser/desktop registration, workspace-trusted ESM execution, or long-horizon crash recovery. Those need their own real integration scenarios, including a task that continues across generation changes without reconstructing its session.
A local plugin smoke or a provider-backed candidate-generation check does not qualify this lifecycle with hosted inference. Hosted model transport is separate from local Docker/smolvm execution and from a managed worker service. Parent SDK model calls use the parent’s configured runtime; that is not a blanket guarantee for arbitrary trusted ESM clients or every child route. See hosted inference and evolution accounting for routing, pricing and qualification boundaries. The historical checks here establish neither current production availability nor launched billing, and Self-Harness’s model-specific results establish no universal gains.
If the account already has approved Docker group membership but a persistent
process predates it, the Docker backend can use that existing group through
sg. It does not grant membership, elevate privileges, or fall back to host
execution. A manual check can use the same existing group:
sg docker -c 'node scripts/smoke-executable-plugins.mjs'This is not Docker provisioning or a grant of group membership. Do not make the
Docker socket or /dev/kvm world-writable, and do not store sudo credentials in
the repository.
To exercise that same source lifecycle with smolvm, use env (the setting names
start with a digit and therefore are not POSIX shell variable identifiers):
env ZERO_EVOLUTION_BACKEND=smolvm \ ZERO_SMOLVM_IMAGE_ARCHIVE=/absolute/path/to/node.tar \ node scripts/smoke-source-evolution.mjsThe provider-free runtime qualification is separate:
node scripts/smoke-smolvm.mjs /absolute/path/to/node.tarIt checks real guest stdin/argv, nonzero exits, stream separation, source
protection, fresh workspaces, credentials, ambient configuration, host loopback
denial, storage exhaustion, identity mismatch, absent runtimes, cancellation,
deadlines, output floods, and process teardown. It does not skip missing runtime
prerequisites. pnpm test:smolvm:e2e runs it when
ZERO_SMOLVM_IMAGE_ARCHIVE is set in the process environment.
The lens check exercises synthesis, labelled positive/held-out/clean fixtures,
promotion, next-reader reload, and retirement. Both consume real provider usage,
disable cross-run hunt memory, and fail rather than reporting skipped work as success.
The codebase-learning check uses a separate temporary memory store and real model
runs to learn a source-grounded note, recall it in a later run, and invalidate it
after a cited file changes. It tests the lifecycle, not a measured accuracy gain.
The citation check deliberately submits an out-of-range source annotation,
verifies rejection before database persistence, and lets the real model read the
file and submit a valid source-only finding. Interactive save_finding returns
repairable validation errors instead of saving invalid references as false
positives. read_file and annotation validation agree on actual source lines:
a terminal newline does not create another line, and an empty file has zero.
Post-hoc CLI parsing still retains invalid claims as downgraded findings.
The trusted-main Live evolution E2E workflow accepts lane=source,
lane=lens, lane=codebase, lane=citation, or lane=all and retains measured
outcomes and failure logs. pnpm test:evolution:e2e runs all four local checks.