Skip to content

Latest commit

 

History

History
659 lines (552 loc) · 34.7 KB

File metadata and controls

659 lines (552 loc) · 34.7 KB

Operator overview

A stage is a fixed lifecycle slot. An operator is one reusable implementation at library/<stage>/<name>.py. A recipe is the code-free selection and configuration of operators. Canonical evaluation is framework-owned and cannot be replaced by a recipe operator.

select
  → rollout
  → analyze
  → mutate
  → validate
  → novelty
  → canonical evaluation
  → gate
  → record
  → reflect

Optional stages are skipped when their configuration block is absent. Canonical evaluation is framework-owned and is not an operator.

Operator Required Responsibility
select yes choose valid parent generations
rollout yes produce training behavior and execution evidence
analyze no transform rollout cases into bounded mutation feedback
mutate yes edit the candidate inside the declared surface
validate no run method-specific checks before canonical evaluation
novelty no reject candidate edits that duplicate prior work
gate yes decide whether a canonical evaluation is parent-eligible
record yes attach method-specific evidence to the archive
reflect no derive reusable insights from verified history

Discover and validate the library

Discovery is filesystem-only and never imports operator code into the framework process. Inspection runs each entry file in a subprocess:

evolve operator list
evolve operator list mutate --json
evolve operator describe mutate/hyperagents
evolve operator check mutate/hyperagents --config '{"runner":"local"}'

Create a complete SDK entry file in a source checkout with:

evolve operator new mutate my_operator

Every named library entry must expose --describe and --validate-config via sdk.main(..., config_schema=CONFIG). The declarative schema rejects unknown or invalid settings and reports defaults during inspection. Underscore-prefixed files and directories are helper modules, not discoverable operators.

Compose a recipe

Each enabled stage selects exactly one operator or script. Put every operator-specific setting under config:

operators:
  select:
    operator: greedy
    timeout_s: 600
    config: {}
  mutate:
    operator: hyperagents
    timeout_s: 3600
    config:
      runner: harbor
      editable_roots: [target]

Check the complete composition before initialization:

evolve recipe check /path/to/recipe/evolve.yaml
evolve recipe check /path/to/recipe/evolve.yaml --json

Recipe-local operator directories are rejected. A script: binding is still executable, but recipe check marks it non-portable because it depends on a filesystem path outside the shared named catalog.

Inspect initialized bindings

After initialization:

operators/       frozen active recipe-selected operator scripts
library/         frozen runtime helpers imported by selected library operators
evolve.yaml      normalized operator config
.evolve-components.json   source identity, digest, and portability

Inspect the active configuration with:

./evolve operator active .
./evolve operator active . --json
cat operators/README.md

Direct orchestration can invoke a configured stage and retains its artifacts:

./evolve operator run . rollout --genid 1 --parent 0
./evolve operator run . mutate --genid 1 --parent 0 --config '{}'
./evolve finalize . 1 --parent 0

Operators execute as subprocesses. They should write diagnostics beneath their generation run directory and return the typed result for their interface. They must not write evaluator truth, generation tags, or archive outcomes directly.

Agent Driven control

Start an Agent Driven session only after generation zero has a certified score and is a valid parent. Use agent prepare for the research workflow: it evaluates the development baseline without reading sealed data. Ordinary evolve run keeps its existing startup behavior, including the sealed baseline anchor regardless of anchor.final. These are distinct workflows:

./evolve agent prepare .
./evolve agent start . --optimizer /path/to/method --objective "Improve the candidate" \
  --max-actions 20 \
  --max-operator-calls 10 \
  --max-evaluations 3 \
  --max-cost-usd 10 \
  --max-wall-s 3600 \
  --require-clean-start
./evolve agent schema
./evolve agent status .

The outer agent submits one strict JSON action at a time. For example:

./evolve agent act . --action \
  '{"id":"fork-1","type":"fork","parent":"0","genid":"1"}'
./evolve agent act . --action \
  '{"id":"check-1","type":"checkpoint","parent":"0","genid":"1"}'
./evolve agent act . --action \
  '{"id":"publish","type":"publish_best","genid":"0"}'
./evolve agent act . --action \
  '{"id":"finish","type":"finish_research","reason":"completed"}'

The session writes an append-only action receipt at runs/agent-driven/actions.jsonl. Repeating a completed action ID returns its terminal receipt instead of executing it again. A process crash leaves the action visible as interrupted; after inspecting its side effects, mark that attempt failed with agent resolve-interrupted before choosing the next action. While the process still holds the session lock, status reports running instead. Recovery accounts for observable operator cost already written to its run artifacts.

Candidate edits still happen in the managed child worktree returned by the fork action. A checkpoint classifies target/** and operators/** changes and reports surface violations. gate and record are protected process paths. Other changed operators execute only after that candidate becomes a valid parent: an operator action for child gen/2 with parent gen/1 loads the implementation from gen/1, never from the dirty gen/2 worktree.

Agent actions cannot supply operator configuration or timeout overrides, and only controller-safe stages are accepted. The session checks the tracked control tree and archive between actions and blocks ordinary orchestration CLI routes while runs/agent-driven/ACTIVE exists.

The host owns action, operator-call, evaluation, observed-cost, and elapsed-wall limits. Cost and wall limits are checked between actions, so a single in-flight action can overshoot them. It also keeps candidate identity, evaluator stamps, lineage, gate, record, and final sealed evaluation outside the action vocabulary. The outer controller's own model usage is not observable when it is launched externally; include that usage in an experiment's matched budget separately.

Before a paired run, create two independent workspaces with no post-baseline generation history. Use the supported copy command instead of cp -a; it rejects a dirty or previously used source, copies the complete certified baseline evidence, and assigns the destination a fresh workspace identity:

./evolve agent clone-arm /path/to/baseline /path/to/fixed
./evolve agent clone-arm /path/to/baseline /path/to/agent

Then verify the pair with:

./evolve agent audit-arms /path/to/fixed /path/to/agent

This command checks independent Git storage and distinct evolve workspace identities, the same certified candidate and target/operator/evaluator trees, and clean generation occupancy. A filesystem copy retains its source workspace identity, so assign the copy a fresh identity (or initialize it independently) before using it as the second arm.

For a long controller run, start the bounded session first and launch the controller as an argument vector, without a shell:

./evolve agent start /path/to/agent --optimizer /path/to/method --objective "Improve the candidate" \
  --max-actions 80 --max-operator-calls 30 \
  --max-evaluations 6 --max-cost-usd 300 --max-wall-s 216000 --require-clean-start
./evolve agent run-controller /path/to/agent \
  --controller /path/to/RSIHub/scripts/codex_agent_controller.py \
  --controller-arg=--codex-arg --controller-arg=--model=gpt-5.4 \
  --max-attempts 3 --max-controller-tokens 1000000 \
  --max-controller-wall-s 216000

The launcher executes deferred actions between controller attempts until research is paused, finished, or blocked. It sets EVOLVE_AGENT_WORKSPACE, EVOLVE_CONTROLLER_ATTEMPT_DIR, and EVOLVE_CONTROLLER_USAGE_RECEIPT. Before exiting, the controller wrapper must write the receipt as {"total_tokens": <integer>, "cost_usd": <number-or-null>}. A null cost is retained as unpriced and blocks further controller calls. Every successful attempt must also write method-load.json matching the optimizer from EVOLVE_CONTROLLER_INPUT. Standard output, standard error, receipts, and cumulative controller/mechanism cost remain under runs/agent-driven/controller/. Re-run the exact command to resume. Inspect agent controller-status first; if the host died between start and terminal receipt, use agent resolve-controller-interrupted only after checking that no controller or child action remains alive. Supply the observed or conservative upper-bound --total-tokens and --cost-usd, so recovery cannot erase spend. The bundled Codex wrapper records Codex 0.149 JSON events and session ID, resumes that session on later attempts, and emits token usage. Its native subscription output does not include dollars; a trusted cost receipt or the host model broker is needed for accounted execution.

These checks are audit and misuse defenses for a trusted host controller, not a security sandbox. A same-UID process can rewrite session metadata or read workspace files. Run an untrusted controller under a separate OS identity or container with only a brokered action interface; do not describe the current host-shell mode as isolated.

After finish_research closes the session, a host operator may run the one-way final evaluation. The explicit confirmation is outside agent act, and the finished session cannot accept another action afterward:

./evolve agent seal-research . --confirm

When the frozen sealed cohort is empty, both driver final-anchor handling and seal-research are no-ops; the latter reports sealed: not_configured.

Deferred actions and continuous progression

Run the controller with agent run-controller. The controller requests one action using agent act --defer --action '<JSON>', then exits with its usage receipt. The launcher marks the controller environment so CLI actions are deferred even if the controller omits --defer; the bundled Codex wrapper appends the one-decision handoff instructions. RSIHub executes the action outside the model process and launches the next controller attempt after it finishes. Give the launcher enough --max-attempts for the intended decision count; each handoff uses an attempt and all attempts share the configured usage limits.

The durable handoff is runs/agent-driven/deferred-action.json; progression state lives under runs/agent-driven/progression/. Re-run the same command after a clean interruption. A crash after action completion but before handoff acknowledgment reuses the action receipt instead of repeating the operation. An interrupted action remains blocked for explicit inspection and resolution; this command never retries uncertain external effects. A controller that exits without finishing research or deferring work blocks progression instead of being invoked in a polling loop. This is a foreground execution loop: use a process supervisor when survival across host or terminal failure is required. It does not install a daemon.

finish_research requires a non-empty reason, recorded in research.finish.reason. The progression result reports evaluation counts separately from the certified champion. It does not certify that a user's experimental objective was satisfied. Controller token and dollar limits are checked between attempts, not during an individual model invocation; wall time is enforced during the invocation. Unknown controller cost remains unknown.

After inspecting a failed or unstarted deferred request, use agent resolve-deferred --reason '...' to release that handoff without retrying it. Resolution is recorded in deferred-resolutions.jsonl; it is rejected while an action is live or unresolved. For an interrupted action, inspect external side effects and use the existing resolve-interrupted flow first. Neither resolution erases the original action or its cost.

After inspecting and explicitly resolving an interrupted action and its deferred handoff, evolve agent resume-controller WORKSPACE reloads the persisted progression configuration and continues with the original cumulative budgets. It does not reset attempts, resubmit a terminal action, or infer that native Harbor recovery completed an operator action. Existing unresolved-action guards still apply. A host supervisor can invoke this command after restart, but the CLI does not install a service or resolve uncertain external side effects.

At a configured budget boundary, continuous progression records progression/budget-stop.json, releases unstarted queued work with an audit reason, and calls finish_research with reason: budget without another model call. An already queued finish action can still complete. Interrupted work or unknown controller cost requires reconciliation instead of automatic closure. Limits and usage receipts are retained across resume; this is not an in-flight dollar cap.

An observe action may include claims, a list of objects with path, sha256, keys, and value. The path must be listed in evidence and resolve inside the workspace; keys traverses JSON object keys. The host checks the file digest and exact value before recording verified claims. A mismatch fails the action and blocks its deferred handoff. For example, a claim with keys: ["errors"] and value: 0 cannot cite a JSON file containing {"errors": 2}. Hashes establish which bytes were checked, not whether a mutable source is authoritative. Free-form hypotheses and decisions remain explicitly unverified; claims are optional and do not constitute a natural-language fact checker.

Each controller attempt receives EVOLVE_CONTROLLER_FACTS, a host-generated session-facts.json snapshot of session identity, certified baseline/best, evaluation count, recorded action cost, completed evaluation receipts and failed action IDs. The bundled Codex wrapper asks observations to cite these facts. Claims with path: "@session" are checked against freshly derived host receipts, not a similarly named controller-written file. Use the snapshot's SHA256 and list @session in evidence. Action failure counts are not trial error counts; this projection does not infer missing task-level statistics. Arbitrary-file claims still only establish consistency with those bytes. This mechanism does not make the shared filesystem a hostile-code security boundary.

Agent session status and final progression results include health; the same projection is included in @session controller facts. It retains failed actions, explicit infrastructure_failed evaluation results, failed controller attempts, and recovered deferred handoffs, with paths to durable receipts. Completion does not clear this history. attention_required means pending or uncertain evidence; incidents_recorded means historical anomalies remain; no_recorded_incidents means no anomalies in the covered receipts, not proof of an error-free run.

A terminal action found before its deferred acknowledgment is recorded once in runs/agent-driven/handoff-recoveries.jsonl before acknowledgment. It proves reuse of the terminal receipt, not the cause of the interruption. Unknown causes stay unknown; only an evaluator's explicit infrastructure outcome is classified as infrastructure. Pending controller work is not labeled a confirmed crash. This projection covers action/controller/handoff receipts, not every native trial or silent verifier failure. Existing workspaces retain their vendored runtime; a source update does not retroactively invent missing recovery receipts.

Agent Driven evaluation receipts retain aggregate execution counts for observed/expected/scoreable trials, timeouts by owner, and exception types. These counts flow into @session host facts without task names or verifier messages. A scored evaluation with trial timeouts records a health incident; it does not automatically become an infrastructure failure. Health coverage is limited to receipts and reported evaluation timeouts, not every task problem. Older receipts lacking these fields are not evidence of zero timeouts. Rollout usage also sums phase timing dictionaries rather than treating them as zero; this is cumulative trial-phase time, not concurrent batch wall duration.

Continuous outer-agent research

agent start --optimizer PATH --objective TEXT starts a persistent research session from a certified baseline. PATH is a method bundle with a non-empty instructions.md, optional resume.md, and ordinary research tools. The host snapshots every file and validates Python syntax; this admits a method for use, not as a demonstrated improvement. Agent Driven has one research lifecycle. Original batch session manifests are unsupported; preserve their records and initialize a fresh research workspace. Fixed recipe-driven execution remains a separate workflow.

Run the controller through the agent run-controller handoff loop. Publishing keeps research active; finishing ends it. The Codex wrapper loads the exact adopted bundle, receipts its file hashes, and opens a fresh model context for each adoption. Attempts and costs remain cumulative.

Research actions are adopt_optimizer, publish_best, pause_research, resume_research, and finish_research. Use agent schema for required fields. To edit a method, copy its snapshot into runs/agent-driven/optimizer/drafts/, edit there, then adopt with draft_path, expected_digest, and reason. To roll back, provide an existing digest instead of draft_path. Adoption takes effect only after its completed action receipt. publish_best retains the active session; finish_research closes it. A paused session requires an explicit resume_research action before resuming its controller.

After finish_research, agent seal-research --confirm evaluates both the session's initial baseline and the selected final candidate on the frozen sealed split. If they are the same candidate it evaluates once; already complete anchors are reused. A failed baseline acceptance stops before the final candidate, and retry reuses completed results. These final acceptance costs are outside the research action budget. publish_best never triggers sealed acceptance.

An unfinished session owns the workspace even when paused or its process exits. Do not delete runs/agent-driven/ACTIVE to bypass the lock. For a paused session, submit a resume_research action and resume its controller, or submit finish_research to end it. For interrupted actions, inspect external effects and use agent resolve-interrupted; for interrupted controller attempts, reconcile usage with agent resolve-controller-interrupted. Then resume the persisted controller. Finishing clears ownership; ordinary driver runs can proceed again. A normal recipe-driven experiment that never starts an agent session is unaffected by this ownership rule.

Research notes are ordinary files in runs/agent-driven/notes/; no memory schema or save action is imposed. These files survive method changes and rollbacks. Trusted action/usage receipts remain separate. Parse rejections before execution can be returned to the controller for correction, with at most three consecutive rejected attempts. Unknown external effects still require reconciliation, and unknown controller usage blocks more paid work. Final sealed evaluation is permitted only after research finishes.

The local controller executes with its existing host permissions. Method hashes and mutable-surface checks do not isolate private evaluator data. Continuous execution alone is not a claim of leakage resistance, autonomous learning, or generalization; those require separate execution boundaries and independent evaluation.

Independent candidate delivery

evolve candidate-export WORKSPACE --ref gen/1 --output PACKAGE exports only the committed target/ tree and prints a receipt containing the manifest SHA-256. Uncommitted edits, untracked files, the Git database, evaluator files, and research notes outside target/ are not included. Files intentionally committed inside target/ remain part of the candidate, including target-owned memory. Symlinks and submodules are refused. Export does not execute candidate code.

The host can install a package without the research repository using evolve.candidate.package.materialize_candidate(package, expected_sha256, destination). Obtain the expected digest from the trusted export receipt, not from an untrusted package alongside its contents. Installation checks every file digest and rejects extra files and links before publishing the destination. This delivery boundary does not itself isolate candidate execution or certify benchmark performance.

evolve candidate-run PACKAGE --sha256 DIGEST --image IMAGE --command sh --arg /input/run.sh --output RUN_DIR verifies and materializes the package before executing it in Docker. The image must already exist locally and is resolved to its immutable ID. The process sees only /input (read-only candidate files), /output (untrusted writable results), the selected image, and a temporary filesystem. It receives no network, Docker socket, host credentials or host PID namespace. The default timeout is 60 seconds (--timeout overrides it).

The host retains execution.json, stdout, and stderr outside the writable output tree; Docker cleanup is attempted even on timeout or cancellation. Unconfirmed cleanup is an error. This is an offline execution interface, not a replacement for canonical benchmark evaluation: it supplies neither a model service nor private verifier access, and does not stamp a benchmark score.

Sandbox images must provide /bin/sh, tar, sleep, cat, and touch. Only /input is a host bind mount. /output is a 64 MiB container tmpfs (SandboxConfig.output_mb may reduce it), limited to 8192 inodes. A separate host supervisor exchanges bounded tar streams, validates paths and regular-file types, and mirrors accepted files; host-owned broker/RPC replies travel back through the same transport. Docker's copy API is not used because it does not reliably expose tmpfs contents. Transfers are snapshots, not transactional file systems; copying larger outputs adds latency.

File handoffs accept at most 32 MiB per file, 64 MiB per tree, 4096 entries and 32 path components. Executable bits survive method freezing and transport and are included in new optimizer identities; other permissions normalize to owner read/write. Legacy byte-only snapshots remain readable and reusable only without executable files. Oversized, linked or special-file output fails closed. Obsolete mirrored files and directories are removed so output churn cannot accumulate unbounded host files. Docker logs rotate at 1 MiB and returned logs are limited to the last 1000 lines and 1 MiB; logs are therefore not a complete transcript.

The independent supervisor enforces the deadline (including startup and transfer) and detects loss of its launcher, including SIGKILL. Liveness is checked between Docker commands, each bounded to at most 15 seconds. It removes the container before returning and writes a cleanup receipt in sandbox-lease-* beside the input tree. Cleanup can take up to 15 additional seconds. This protection requires a surviving supervisor and reachable Docker daemon; simultaneous host/supervisor failure or daemon unavailability still requires external reconciliation.

Resource defaults and hard handoff limits are declared once in src/evolve/runtime/policy.py. The host selects SandboxConfig memory, PID, wall-time and output limits; output may only be reduced below the policy ceiling. Candidates cannot override host policy through returned files. Boundary receipts record the effective configuration (including any remaining-time reduction) and the versioned resource policy. Defaults do not retroactively change already vendored workspaces.

Public artifact paths and aggregate feedback fields are declared in src/evolve/frozen/public_artifacts.py (protocol version 1). This is a host-owned allowlist, not a candidate-supplied configuration. New declarations require information-exposure review; undeclared metrics remain excluded. Existing projection behavior is unchanged.

Isolated continuous controller transport

evolve agent run-controller WORKSPACE --sandbox-image sha256:IMAGE_ID --controller python3 --controller-arg /input/optimizer/controller.py runs the controller inside the offline boundary above. Use --sandbox-timeout to set the per-attempt wall limit. The immutable local image ID and limits persist with the controller configuration and are reused on resume. Without a model handler, model tokens and charges are authoritatively zero on the host. With a handler, the host broker accounts for each request. Controller-owned usage files cannot alter either ledger.

Each invocation receives read-only /input/controller-input.json, session-facts.json, session.json, actions.json, the active optimizer/, a target-only champion/, and target copies for existing managed children. correction.json appears after a known no-effect parse rejection. The Git repository, evaluator directory and host action ledger are not mounted.

The method can freely read/write /output/notes, /output/drafts and /output/contexts. These regular-file trees are retained on the host between successful handoffs. Contexts can be organized by the activation ID in the controller input. Write /output/method-load.json matching the loaded optimizer identity and one /output/action.json with the existing typed action payload. Draft adoption uses the host-relative path runs/agent-driven/optimizer/drafts/NAME.

Optional /output/edits.json maps target/RELATIVE_PATH to {"data": "BASE64_BYTES", "executable": false}; data: null deletes a file. Edits require an existing managed child selected by a checkpoint, operator or commit request. The host validates paths and the declared mutable surface before importing these bytes. Only the typed request reaches the existing action queue; the container cannot write the queue or cost history itself. File edits remain journaled research changes. Before replacing host directories, the host persists runs/agent-driven/return-handoff.json with staging identities, backups and the action ID. Resume completes interrupted directory swaps and enqueues the same action once, then marks the handoff complete. Backups remain in the attempt folder. Changed/missing staging or unexpected destination contents stop recovery instead of overwriting unrelated edits. Do not manually edit these directories while a handoff is pending. This journal covers isolated controller imports; operator output imports have a separate lifecycle.

A crash can still leave a controller attempt without its usage terminal receipt. Recovering files does not invent a billing value: inspect the attempt, use agent resolve-controller-interrupted with observed usage when required, then agent resume-controller. Resume recovers the import before launching more work. The journal and rename directories are flushed to disk; disk corruption or loss of journal/staging storage remains outside this recovery guarantee.

This isolates the controller execution process. Candidate adapter isolation is configured separately below. Once the isolated controller manifest exists, operator invocations use its container image and data handoff boundary too. Legacy execution before that configuration remains outside this boundary. The legacy host controller path remains available without --sandbox-image and has no filesystem confidentiality boundary.

Host model handler

An isolated controller can use --model-handler EXECUTABLE with repeated --model-handler-arg ARG and a cumulative controller or total dollar limit. The handler is a trusted host program, not code from an optimizer or candidate. It receives two final arguments: an absolute request-body file and an absolute response-file destination, both inside the private host request ledger. It must constrain its provider/model, retain credentials on the host, and obtain usage from that provider. The framework never treats the request body as a command.

The handler writes JSON containing status (HTTP status), content_type, body (base64 response bytes) and usage (total_tokens, cost_usd). Missing, invalid, or null cost remains unknown and stops additional calls. Request and response envelopes are limited to 32 MiB. --max-model-requests bounds calls per controller attempt; --model-request-timeout bounds each handler. Remaining cumulative dollar budgets also constrain each broker invocation, including after method adoption. Limits are checked between calls, not hard in-flight cost caps.

Inside the container, atomically publish /output/broker/requests/32_LOWERCASE_HEX_DIGITS.json containing only {"body":"BASE64_REQUEST_BYTES"}. Read the corresponding file in /output/broker/responses/. Its public response omits usage. Request IDs are idempotent within the invocation; changing bytes under a used ID fails. Host request receipts are retained under the controller attempt's model-requests/, which is never mounted into the container. A started request without known usage requires reconciliation and is never automatically reissued.

With a handler configured, the launcher wraps the controller with a container-local HTTP bridge. OPENAI_BASE_URL and EVOLVE_MODEL_BASE_URL point to its loopback /v1 endpoint; OPENAI_API_KEY is a local placeholder, never the host key. Clients may POST to responses or chat/completions. Headers and URL paths are not forwarded to the host: the trusted handler receives only the body and must choose its own fixed provider endpoint and protocol. Client-specific endpoint configuration may still be necessary.

Responses, including SSE bodies, are buffered until the handler completes and then returned byte-for-byte. This does not provide incremental token streaming. A bridge timeout returns HTTP 504 without reissuing the request; the host broker retains responsibility for its final accounting. Configure client retries with care: a new HTTP request is a new billable request. A trusted provider handler is still required; the framework does not infer provider credentials or prices.

Isolated Harbor candidate adapter

Build containers/candidate-worker/Dockerfile using a context containing the repository's pyproject.toml and uv.lock, then resolve the resulting local image to its full immutable Docker image ID. The image installs the locked Harbor dependencies; the host supplies framework and committed candidate source as read-only input on each trial.

Select the frozen proxy in the recipe before workspace initialization:

evaluator:
  agent: evolve.integrations.harbor.isolated_candidate:IsolatedCandidateAgent
  agent_kwargs:
    candidate_import_path: target.agent:HarborAgent
    worker_image: sha256:FULL_IMAGE_ID
    worker_timeout_s: 3600

The proxy imports candidate code only inside the worker. It preserves adapter state across setup, run, and post-run log parsing. Its task-environment bridge accepts execution and byte-based file transfers only; it cannot select a host file to read or write. Post-run parsing receives agent logs, with no further task-environment requests serviced. Candidate exceptions retain their failure classification, partial context, and same-instance log parsing when necessary.

This path currently targets single-step trials. Explicit extra_env values are passed to the adapter; these may include scoped model credentials. Adapter reported usage is not independently verified by the research model broker. An abruptly killed worker cannot finish its parser; missing usage must remain unknown. Legacy adapter selection continues to execute on the host.

Operators in an isolated controller session

Operator invocations detect the persisted controller sandbox configuration and run inside the same image. They receive copies of candidate target/operator files, operator source, public archive data, surface rules and prior operator artifacts. They receive neither the original Git repository nor the evaluator directory. The image must contain Python and dependencies needed by those operators.

The host accepts only declared mutable-surface changes and the invoked stage's result files. Invalid paths and symlinks reject the return before candidate edits are applied; executable bits are preserved. Reflection can append validated operations to the existing insights file. Host action/evaluation receipts cannot be replaced through operator outputs.

This operator transport is currently offline. Model-based mutation, task rollout and operators requiring repository history need additional explicit services or public inputs; they do not silently fall back to host execution. Offline mutation usage is recorded as zero by the host. Operator data import is not transactional; an interrupted import requires inspection before retrying.

Operator feedback is an explicit development projection. Archive views retain candidate identities, aggregate scores, costs and host-certified parent choices; they omit task vectors, free-text diagnostics and sealed evaluation entries. Prior observation files are restricted to numeric summary metrics and the gate decision. Nested task jobs and verifier logs are not copied. Each operator attempt retains a host-side feedback-exposure receipt; these development observations are not independent holdout evidence. Richer training feedback requires a separately authorized projection rather than copying an entire job directory.

Codex controller with file transport

Place scripts/codex_agent_controller.py in the method bundle as controller.py, alongside instructions.md. Select a local immutable image containing Python and the intended Codex CLI version, and configure the controller arguments as:

/input/optimizer/controller.py --isolated-input /input --require-version VERSION

Pass model and reasoning choices explicitly with repeated --codex-arg options. The wrapper configures a Responses provider pointing at the local model bridge, disables provider request/stream retries, and uses a separate persisted Codex home for each method activation. Its instructions describe the action/edits file protocol and persistent notes. The outer Docker boundary supplies isolation; Codex can execute tools within that container without an interactive approval. Host billing still comes from the trusted model handler, not CLI token reports. The provider settings follow the official configuration reference.