SCV planning
Date: 2026-08-22
Status: OpenCode worker, Codex propose-only driver, and operator-started read-only deployment lane implemented; durable write effects and autonomous promotion remain disabled
Outcome
Build an Elixir-native, durable SCV that continuously finds bounded codebase improvements, implements them in isolated repository workspaces, proves each candidate against an exact Git SHA, and submits admitted candidates to the Forge deployment pipeline.
SCV means Space Construction Vehicle. Use SCV consistently in code, documentation, configuration, and the interface. Do not introduce another name for this subsystem.
An SCV is a logical long-running service, not one immortal process or one unbounded model response. OTP processes may restart, provider calls may end, and workspaces may be discarded. PostgreSQL, Forge commits, immutable artifacts, and receipts preserve the SCV's identity and progress across those events.
The intended end state includes automatic deployment. The first implementation must not bypass the repository's current safety contracts:
- The Forge is canonical after the proof-gated cutover in ADR 0007. SCV candidates must use its authenticated push and promotion boundaries.
- Forge fleet deployment is active. The SCV deployment lane remains a bounded, operator-started, read-only work job and does not grant repository writes or promotion authority.
SELF-EDIT-001currently requires a human promotion. Enabling an SCV to promote a candidate requires an explicit invariant and architecture amendment, a typed service principal, and a policy-bound promotion receipt. Do not encode an SCV identity in the existing free-formpromoted_byfield and call that authorization.
The recommended first milestone is a continuously running, propose-only SCV
that uses OpenCode inside an isolated worker. Qualify the worker against the
OpenCode repository before using the same runtime to improve openagents.com.
The recommended first autonomous milestone is staging-only deployment of a
narrow, low-risk change class. Production autonomy is a later admission, not a
configuration toggle hidden inside the first release.
Model capability boundary
SCVs use openai/gpt-5.6-luna with low reasoning by default. You may select
none for latency-sensitive, measured workloads. Do not run implementation or
production-code tasks with GPT-5.4. GPT-5.6 is the minimum admitted model family
for code generation by an SCV.
Model capability does not grant infrastructure authority. A code-writing SCV must still use an isolated workspace, repository-scoped principal, durable effect records, required tests, and Forge promotion policy before its code can reach staging or production.
SCV runtime boundary
Define an SCV as the durable execution and supervision contract. Do not define an SCV as a container, OpenCode session, model, or tool catalog. The internal runtime deploys an SCV run. Each run selects one implementation driver and one execution environment.
| Boundary | Responsibility |
|---|---|
| SCV | Owns identity, objective, policy, capabilities, lifecycle, budgets, events, receipts, cancellation, artifacts, and Forge handoff |
| Driver | Adapts one coding implementation, such as OpenCode or a native Elixir tool loop, to the SCV contract |
| Environment | Supplies one digest-addressed runtime image or owned host with declared language and system capabilities |
| Runner | Starts and supervises the selected driver inside the environment |
| Tool catalog | Defines the typed effects available to a native driver; an external driver may retain its protocol only when the SCV maps it to the same policy and event boundary |
An SCV run normally binds one driver to one worker. A durable SCV campaign may coordinate several runs with different drivers or environments. Do not place several independent repository writers inside one worker and call the container an SCV.
Expose two tool surfaces:
- The internal runtime uses SCV control tools to start, inspect, cancel, and collect artifacts from SCV runs.
- A native SCV driver uses admitted coding tools for workspace inspection, edits, commands, tests, and Git operations. OpenCode retains its own protocol behind the same capability policy until the durable sidecar replaces its direct effects.
This split gives every implementation one operational contract without forcing OpenCode, a native Elixir driver, and future coding runtimes to share one model or tool-loop implementation.
Local implementation checkpoint
The repository now contains a complete direct-process SCV boundary and the first container environment. This implementation proves driver dispatch and worker execution without enabling a durable coordinator, repository write authority in staging, worker registration, Forge promotion, or deployment:
OpenAgents.SCV.Runbinds one objective to an admitted driver, environment, permission profile, capability set, and runner.OpenAgents.SCV.Driver.OpenCodeadapts OpenCode to the common SCV run and event contract.OpenAgents.SCV.Environmentdeclares theopencode-corecapabilities separately from the driver.OpenAgents.SCV.Runner.Localsupervises the driver as a direct process in the current environment. A container scheduler may place this runner inside a digest-addressed worker.OpenAgents.SCV.Workeraccepts the staging environment contract, admits only read-only OpenCode runs, streams JSON events, writes one terminal result, and exits with the run status.OpenAgents.SCV.Executor.OpenCodestarts one bounded OpenCode process with an isolated home, XDG roots, SQLite database, operator-owned configuration, and explicit permission profile.OpenAgents.SCV.OpenCodeEventsnormalizes content-free event counts, tool outcomes, token classes, and estimated cost.OpenAgents.SCV.OpenCodeReportcollects only redacted OpenCode text events into a versioned report capped at 32 KiB. It never includes tool output or diagnostic lines.OpenAgents.SCV.ResourceSamplerobserves the direct OpenCode process from the host and records RSS and CPU samples.- Codex-backed SCVs persist generation-fenced leases, normalized events, and a
bounded terminal report. The runtime reaps expired leases and projects active
durable runs through the public
/statusSCV stream, including when the SCV and web request land on different nodes. mix openagents.scv.opencodeexposes the adapter for local qualification.ops/scv/images/opencode-core/Dockerfiledefines the first complete multi-architecture environment with a pinned Debian runtime, Elixir release, Node.js, Bun, Python, Git, native build tools, and OpenCode.
The individual-operator Codex path adds a durable propose-only coordinator:
OpenAgents.SCV.CodexRunsclaims one connected account generation and dispatches one SCV under an OTP supervisor.OpenAgents.SCV.ExecutionandOpenAgents.SCV.ExecutionEventretain the exact-SHA lease, normalized event ledger, Codex session references, usage, resource summary, terminal report, and digest in PostgreSQL.OpenAgents.SCV.Workspacecreates a clean disposable checkout from the node-local Forge cache and destroys it after the run.OpenAgents.SCV.Driver.CodexAppServerfixes the execution togpt-5.6-luna,lowornonereasoning,approvalPolicy=never, and the repository-scopedscv-read-onlypermission profile.OpenAgents.SCV.Activityprojects Codex lifecycle and tool phases into the public/statusSCV stream without publishing objectives, paths, commands, output, or account identity.
This durability applies to the Codex propose-only driver. The existing Cloud Run OpenCode job still emits its bounded report through Cloud Logging; it does not ingest that result into the new PostgreSQL execution ledger.
The executor emits openagents.scv.event.v1 records while the run is active.
Callers can supply an event_sink function, and the executor also emits the
same records through the [:openagents, :scv, :event] telemetry event. The Mix
task prints lifecycle events, five-second resource heartbeats, and normalized
OpenCode events to standard error. --diagnostic-logs also prints redacted
OpenCode logs as OpenCode produces them. Final JSON remains on standard output,
so an operator or process can consume the receipt without waiting blindly for
the command to finish.
The terminal worker result now includes openagents.scv.report.v1. The executor
applies its run-specific provider-key redaction before the report parser sees a
line, preserves valid UTF-8 at the byte limit, and marks truncated reports. This
path makes a read-only audit result consumable through Cloud Logging. It does
not replace durable artifact storage: a write-capable SCV must persist and
acknowledge the report, event artifact, and their digests before it reports
success.
The adapter writes the bounded prompt to a mode 0600 scratch file and gives
that finite file to OpenCode as standard input. This keeps prompt content out of
the process argument list and delivers EOF after the prompt. OpenCode reads a
non-terminal standard input stream as additional prompt content and otherwise
waits indefinitely when an Erlang port keeps that stream open. Keep the finite
input wrapper as part of the adapter contract and retain its regression test.
OpenCode treats XDG_CONFIG_HOME as a parent directory and
OPENCODE_CONFIG_DIR as the OpenCode configuration directory itself. The
adapter uses <XDG_CONFIG_HOME>/opencode for the latter. Pointing both variables
at the parent creates two dependency locations and can trigger an unseeded
background install. A trusted local config_seed may copy only dependency and
lock files into the isolated directory; it never copies OpenCode configuration
or authentication state.
GPT-5.6 local qualification receipt
On 2026-08-20, installed OpenCode 1.18.5 ran a read-only SCV against OpenCode
commit b155b15694dbcc6768f11d2f25cc2bdd1f738ab4 with
openai/gpt-5.6-luna and low reasoning. The run read package.json and
returned the package name and a one-sentence repository description without
changing the checkout.
SCV run f4955a8b-d0a1-42ad-bc8b-acf4c6cb48b8 succeeded in 5,475
milliseconds. It emitted six normalized events, completed one read call,
used 6 input, 59 output, and 11 reasoning tokens, and reported no truncation.
The terminal receipt recorded the exact model and reasoning effort. This run
qualifies the local model-selection and event path. It does not authorize an SCV
to write to a shared repository or deploy code.
Historical GPT-5.4 read-only run
On 2026-08-20, the adapter ran installed OpenCode 1.18.5 against the inspected
OpenCode dev commit b155b15694dbcc6768f11d2f25cc2bdd1f738ab4 with model
openai/gpt-5.4-mini and read-only permissions. The fixed task read
package.json and README.md without changing the checkout.
The terminal receipt recorded:
succeededwith exit status0in 7,845 milliseconds;- eight structured events across two model steps;
- two completed
readtool calls and no tool errors; - 7,981 input, 144 output, 74 reasoning, and 3,584 cache-read tokens;
- an estimated cost of
$0.00723555; - 622,215,168 bytes of peak direct-process RSS and 141.9% maximum sampled CPU;
- 40,855 captured output bytes with no truncation.
The run streamed lifecycle, diagnostic, tool, and resource events before it
wrote the terminal summary. The summary and redacted event artifact use mode
0600; the executor deletes its scratch home after termination. The supplied
provider credential entered through silent terminal input and did not appear in
the command arguments, summary, or artifact.
A separate workspace_write proof ran only against a disposable Git fixture.
It changed message.txt from before to after, emitted one completed
apply_patch tool event, produced no malformed event lines, and left every
other file unchanged. This validates local edit mechanics but does not admit the
write profile for a durable or autonomous SCV.
This proof does not satisfy the final worker boundary. It runs the Elixir controller and OpenCode process on the trusted development host, measures only the direct OpenCode process, terminates only that direct process, and treats the whole OpenCode session as one uncertain external effect. Before an SCV receives write authority, move this adapter into the admitted worker, enforce process groups or cgroups, replace reusable provider credentials with a run-scoped inference grant, and persist each tool effect before execution.
Run the local adapter
Set OPENAI_API_KEY in the process environment without adding it to shell
history, then run:
OPENCODE_BIN=/absolute/path/to/opencode \
OPENCODE_CONFIG_SEED=/absolute/path/to/trusted/opencode-config \
mix openagents.scv.opencode \
--repo /absolute/path/to/target \
--model openai/gpt-5.6-luna \
--reasoning-effort low \
--timeout-seconds 180 \
--diagnostic-logs \
--prompt 'Inspect the requested files without changing them.' \
--json
Omit OPENCODE_CONFIG_SEED when the isolated OpenCode installation can resolve
its dependencies during an admitted setup phase. Do not use --write against a
valuable checkout. The current write profile enables only OpenCode's edit tool;
it does not yet provide the durable per-effect barrier required for candidate
construction.
Proven image build
Run ops/scv/images/build-opencode-core.sh to build the native architecture as
openagents/scv-opencode-core:local. The build pins its Debian and Elixir base
digests, Debian snapshot, Hex, Rebar3, Node.js, Bun, and OpenCode. It produces a
self-contained Elixir release and the OpenCode toolchain in one image.
Use ops/scv/images/build-opencode-core-cloud.sh to build a clean committed
source tree on native linux/amd64 Cloud Build infrastructure and publish it to
the immutable staging repository. Do not use an emulated cross-build as staging
evidence when the language runtime fails under the emulation layer.
On 2026-08-20, the complete ARM64 image ran as UID and GID 10001 through the
Elixir SCV process role. The SCV selected the opencode driver and
opencode-core environment, inspected the source baked into the image, and
made no changes. The worker emitted lifecycle records, two-second heartbeats,
normalized OpenCode events, two completed read calls, resource samples, and
one terminal worker result.
The terminal result recorded:
succeededin 9,387 milliseconds;- eight normalized OpenCode events and no tool errors;
- 9,060 metered tokens and an estimated cost of
$0.00657015; - 706,650,112 bytes of peak direct-process RSS and 188% maximum sampled CPU;
- 16,908 captured bytes with no truncation.
The image contains no Docker client or socket. The SCV process role starts no Phoenix endpoint, application Repo, Forge service, or deployment coordinator. It accepts only the staging environment and read-only permission profile. This proof does not admit repository writes or autonomous deployment. Add the durable worker protocol, process-tree or cgroup enforcement, run-scoped credential proxy, and effect-persistence sidecar before enabling writes.
Proven staging qualification
On 2026-08-20, Cloud Build built commit
09a775b83fa05b6a92854b0ad1f7b5c23b3aee88 on native linux/amd64
in build 9721075d-ed17-491c-af9a-85be8f46bf52. Artifact Registry stored the
image as
sha256:ee2a74660faa6137e4021a2870380f5977796d33ca9dcaabcb0b958f35e0e36b.
Cloud Run job execution openagents-scv-staging-wrppq ran that digest with one
task, zero retries, and the dedicated
openagents-scv-staging@openagentsgemini.iam.gserviceaccount.com identity.
The job configuration referenced only the staging provider secret. It did not
contain database, GitHub, Forge, release-cookie, deployment, or general cloud
credentials. The SCV emitted run_preparing, process-start, heartbeat,
normalized OpenCode, process-finish, run-finish, and terminal worker-result
records through Cloud Logging before it exited with status 0.
The terminal result recorded:
succeededin 11,008 milliseconds;- eight normalized OpenCode events, two completed
readcalls, and no tool errors; - 5,520 input, 173 output, 158 reasoning, and 3,072 cache-read tokens;
- an estimated cost of
$0.0058599; - 666,734,592 bytes of peak direct-process RSS, 114% maximum sampled CPU, and 44 resource samples with no sampling errors;
- 16,910 observed and captured bytes with no truncation;
- event artifact digest
sha256:506e4cbd02eb19abd5078d73832be45049b707e06f8c73ef1b4ef7dcc53c1e83.
This result qualifies the complete read-only SCV image in the existing shared Google Cloud project. It does not pass the isolated-staging gate described in this plan and does not authorize writes, Forge handoff, or autonomous deployment.
The current staging worker now uses openai/gpt-5.6-luna with low reasoning.
Cloud Build 655a2fe6-8173-4686-9533-f3a1733942b0 built revision
c7175ed8a8781ea1aab7d204623a28ac45a70bc1 as image digest
sha256:156ff9f51e955d03b4795af8e2bb190a6c4f9962cc7942a6dcc0586e9b48b0a9.
Parity execution openagents-scv-parity-audit-p5mkz proved the exact model and
reasoning effort in its live preparation event and terminal result. Its bounded
report arrived as three ordered structured JSON chunks without truncation. See
Qualify an SCV in staging for the
complete receipt.
Goals
An SCV should:
- Operate without a browser conversation or a person keeping a process alive.
- Select work from explicit evidence instead of producing undirected code churn.
- Use a versioned, digest-addressed SCV program and policy revision.
- Read and change the exact repository that Forge recognizes as source truth.
- Use an isolated, secret-free workspace with a complete compiler and test toolchain.
- Route work to capability-described workers so an SCV can use Elixir, Bun, Node.js, Python, Rust, browser, and platform-specific toolchains without adding those runtimes to the Phoenix release.
- Preserve every model request, tool decision, command result, commit, gate, promotion, deployment, verification, and rollback as bounded evidence.
- Collect host-observed resource use, benchmark samples, and OpenCode usage statistics with enough provenance to compare equivalent runs.
- Recover after node, process, provider, and executor failures without repeating an uncertain external effect.
- Enforce token, cost, time, CPU, memory, disk, command, diff, commit, and deployment budgets outside the model.
- Keep one linear improvement history so later work includes earlier admitted improvements.
- Treat no change, refusal, and rollback as valid outcomes.
Non-goals
The first SCV should not:
- Replace the existing user-scoped coding-job experience.
- Run as a conversational persona or compose user conversation memory into its instructions.
- Receive production credentials, user conversation content, profile memory, voice transcripts, or unrestricted database access.
- Edit its own authority policy, deployment allowlist, release gates, or evaluator and then approve that edit.
- Modify production data, perform destructive migrations, rotate secrets, change billing policy, or widen an authorization boundary.
- Run several repository-writing SCVs concurrently.
- Treat workers with different images, resource classes, operating systems, toolchain versions, or cache states as interchangeable benchmark hosts.
- Deploy a structural or unclassified candidate automatically in the first autonomous release.
- Treat a passing model-authored test as sufficient evidence of correctness.
Existing foundation
The repository already implements much of the mechanical foundation. Reuse the contracts, but do not force an SCV into a user-scoped abstraction whose identity or bounds are wrong.
| Existing capability | Reuse | Required SCV change |
|---|---|---|
OpenAgents.Providers.Provider, OpenAgents.Providers.Request, and OpenAgents.Providers.OpenAI |
Reuse the provider-neutral stream and event normalization | Add an SCV-specific client and program. Do not use the conversational context composer. Admit SCV-specific output and timeout bounds instead of relying on the text-turn defaults. |
OpenAgents.Tools.Registry, OpenAgents.Tools.Runner, and tool receipts |
Reuse schema validation, authority checks, cancellation, timeout handling, output bounds, and normalized outcomes | Add an scv execution surface and an SCV-only tool catalog. The model must never receive promotion, policy-edit, or deployment tools. |
OpenAgents.Work.JobServer and OpenAgents.Work |
Reuse the durable-step, generation-fence, forced-report, and recovery patterns | Do not add scv to work_jobs.kind. Work jobs are conversation- and owner-scoped, have a ten-minute limit, run the coding-lieutenant role program, and terminate after one report. |
OpenAgents.Work.Coding and repository tools |
Reuse exact-match edit semantics, safe path resolution, commit receipts, and branch confinement | Replace per-user approval receipts and the fixed openagents/job-<id> lifecycle with SCV service authority, durable run workspaces, richer Git inspection, and full test execution. |
OpenAgents.Inference |
Reuse metering concepts and server-held provider credentials | Add a service-principal ledger or generalize grants to identify an SCV. Do not invent a visitor, conversation, or computer to satisfy the current schema. |
OpenAgents.Forge.Pushes and the WAL |
Reuse the push acknowledgment barrier and immutable push receipts | Give an SCV executor a repository-scoped, branch-scoped credential. It must not receive the operator token or a credential that can update arbitrary refs. |
OpenAgents.Forge.Builder and the build worker |
Reuse isolated exact-SHA builds, structural classification, artifact verification, and bounded output | Keep the web release compiler-free. Run SCV commands in a separate worker identity and make candidate gate receipts durable outside one worker's .git directory. |
OpenAgents.Forge.Targets and deployment coordinators |
Reuse newest-target fencing, direct-load transactions, relup, rolling replacement, boot convergence, and receipts | Add a policy-authorized SCV promotion path that remains separate from a push. Preserve human promotion for every class outside the admitted SCV policy. |
OpenAgents.Incidents |
Reuse typed failures, bounded context, recurrence tracking, and nonrecursive repair principles | Admit sanitized incidents as possible work-item evidence. Never expose private incident context or allow a failed SCV to recursively create another SCV. |
| Exact-SHA release gate | Reuse mix precommit, focused tests, release smoke, direct-load, relup, and rolling proofs |
Define which gate is mandatory for each risk class. Store the exact gate definition digest so an SCV cannot weaken the gate in the same candidate. |
The existing coding-job integration test proves the sequence through a pushed branch and deliberately stops before promotion. An SCV should extend that receipt chain instead of replacing it with a less governed shortcut.
Separate bounded context
Create an OpenAgents.SCV bounded context. Keep generic SCV control-plane code
out of OpenAgents.Work and keep persona-specific code out of OpenAgents.SCV.
The context should own:
- durable SCV identity and policy revision;
- work discovery and admission;
- run leases and generation fencing;
- SCV program composition;
- provider continuations and tool-step receipts;
- executor requests and responses;
- repository workspace lifecycle;
- candidate and gate decisions;
- policy-authorized promotion requests;
- post-deployment observation and rollback decisions;
- budgets, circuit breakers, and operator controls.
Forge should continue to own Git, build, target, deployment, rollback, and boot convergence. An SCV proposes source and presents policy evidence. Forge decides whether an exact SHA can become a target and whether that target becomes live.
Architecture
sanitized evidence and operator work
|
v
SCV work-item admission
|
v
durable SCV coordinator and lease
| |
| provider events | typed executor requests
v v
server provider adapter capability scheduler
|
+-----------------+-----------------+
| | |
v v v
OpenCode driver OpenCode driver native driver
| | |
v v v
core environment browser environment Rust environment
| | |
+-----------------+-----------------+
|
|-- exact Forge checkout
|-- bounded file tools
|-- bounded command runner
|-- disposable database and services
`-- no production secrets
|
v
SCV candidate commit and push
|
v
immutable exact-SHA gate receipt
|
v
host policy and promotion receipt
|
v
Forge build and deployment pipeline
|
v
post-deployment verification window
| |
v v
admit result promote predecessor
Runtime placement
Keep the durable coordinator Elixir-native. Implement its lifecycle with OTP, Ecto, and supervised tasks. Do not make an external coding CLI the SCV's authority or durable state machine.
Run candidate code and build commands in a separate SCV worker container or owned worker VM. Run an Elixir/OTP worker release there with a separate runtime identity and mounts from the Phoenix release. Start only the worker supervision tree; do not start the Phoenix endpoint, application Repo, Forge control plane, or deployment coordinators. This preserves the Forge build-lane rule that the web release receives no compiler, Docker socket, or general command-execution authority.
The worker needs:
- read access to exact Forge objects;
- write access only to an SCV run ref;
- access to an atomic request and response channel;
- an ephemeral workspace, build cache, and disposable database;
- bounded CPU, memory, disk, process count, and wall-clock time;
- no production database URL, release cookie, cloud credential, Forge operator token, or user credential;
- no network by default, except the narrow internal endpoints required for Forge and coordinator communication.
Candidate code is untrusted during evaluation even though the worker runs in an owned environment. Tests and Mix tasks can execute arbitrary repository code. Do not mount any credential that candidate code could read or transmit.
OpenCode as the first driver
Use OpenCode for the first end-to-end SCV worker implementation. This validates
non-Elixir execution, long model-driven runs, structured events, permission
handling, a large polyglot repository, and measurable performance work before
the SCV targets openagents.com itself.
Keep these milestones separate:
- Environment qualification: Build the OpenCode worker image, compile and test the inspected OpenCode source, exercise one bounded OpenCode session, and collect resource and benchmark evidence without pushing a candidate.
- Self-targeting proof: Use the read-only admitted OpenCode driver to fix a seeded OpenCode defect in the separate target checkout. Stop at a propose-only run ref.
- Product pilot: Use the qualified OpenCode driver and environment to improve
openagents.com, pass its Elixir and Forge gates, and keep promotion human controlled.
Passing runtime qualification does not admit an OpenCode candidate, and passing
the self-targeting proof does not authorize an openagents.com deployment.
Mirror the inspected OpenCode commit into a dedicated Forge qualification
repository and keep its run refs internal. Do not push a self-targeting proof to
the upstream OpenCode repository or treat Forge as upstream authority.
Inspected baseline
This plan uses the OpenCode dev branch at commit
b155b15694dbcc6768f11d2f25cc2bdd1f738ab4 as its inspected baseline. The
repository is a Bun-first TypeScript monorepo, not a generic Node.js project.
Its relevant contracts include:
- Bun
1.3.14from the rootpackageManagerdeclaration; - Node.js 24 in continuous integration, with Node.js
24.15used for the Playwright path because the repository records a Chromium extraction issue with the next patch release; - Ubuntu 24.04 for the primary Linux test environment;
- Python 3 and
setuptoolsfor dependency compatibility; - native build tools,
pkg-config, Git, OpenSSH client,curl, certificates,jq,ripgrep,unzip,xz-utils, andzip; - package-scoped tests and type checks, because the root test configuration intentionally refuses test execution;
- Rust stable for native and desktop work, plus GTK, WebKit, and Tauri system packages for Linux desktop builds;
- Playwright Chromium and its system dependencies for application end-to-end and performance work;
- Windows and macOS runners for complete platform and release coverage.
OpenCode already publishes Linux container layers for a base toolchain, Bun plus Node.js, Rust, Tauri Linux, and publishing. Reuse their pinned versions as input evidence, but build and sign SCV-owned images. Do not trust a mutable tag or let repository code select the worker image.
Trust boundary
OpenCode is a driver inside an SCV worker. It is not an environment, SCV coordinator, lease authority, policy engine, receipt store, or promotion authority. The Elixir control plane owns those responsibilities even when OpenCode manages the model and tool loop for one run.
When an SCV works on OpenCode, keep two separate copies:
- Install the admitted OpenCode runtime under a read-only path such as
/opt/scv/opencode/bin/opencode. Bind its version to the worker image digest. - Check out the target OpenCode SHA under the disposable writable workspace. Treat every file and executable produced there as candidate code.
The target checkout must not replace the admitted runtime during a run. A gate may build and execute the candidate OpenCode binary as an untrusted test artifact, but that binary cannot control the run, approve permissions, write receipts, or evaluate its own gate.
First worker image
Build scv-opencode-core before creating generic language images. The image is
the first executable SCV milestone and should contain:
| Layer | Pinned contents |
|---|---|
| Operating system | Digest-pinned Debian Trixie from one dated snapshot for the build and runtime stages; use a separate Ubuntu qualification environment when exact OpenCode CI parity matters |
| JavaScript runtimes | Bun 1.3.14 baseline build, Node.js 24.15, and Corepack |
| Native support | Python 3, setuptools, build-essential, pkg-config, libgcc, and libstdc++ |
| Repository tools | Git, OpenSSH client without credentials, curl, certificates, jq, ripgrep, unzip, xz-utils, and zip |
| SCV runtime | A self-contained Elixir/OTP worker release with ERTS, a pinned read-only OpenCode binary, and cgroup and process-tree measurement support |
| Runtime identity | An unprivileged UID, empty home and XDG roots per execution, a read-only root filesystem, and a bounded writable workspace and cache |
Do not include Chromium, Rust, a Docker daemon or socket, a cloud CLI, credential helpers, an SSH agent, or production credentials in this first image. Build dependencies while the image build has admitted network access. Run candidate commands without general network access.
Address the image by its manifest digest. Produce an SBOM and record the source SHA, Dockerfile digest, base image digest, OpenCode version, package-manager lock digest, toolchain versions, and build receipt. Forge should admit that manifest before any worker registers with it.
Use a multi-stage build. Compile the minimal SCV worker release and the admitted OpenCode binary in build stages, then copy their immutable artifacts into the final toolchain image. Do not copy source credentials, package-manager tokens, Hex state, SSH state, or build-stage homes into the final image. The worker release should spawn OpenCode and language tools as contained OS processes and remain the parent authority for deadlines, cancellation, output bounds, and receipts.
Worker image family
Use additional images only when the work item requires their capabilities:
| Worker image | Adds | Intended work |
|---|---|---|
scv-opencode-core |
Bun, Node.js, Python, native build tools, and the admitted OpenCode runtime | OpenCode CLI and server changes, unit tests, type checks, source benchmarks, and most TypeScript work |
scv-opencode-browser |
Playwright Chromium, browser system libraries, and production application assets | Application end-to-end tests, trace capture, and UI performance benchmarks |
scv-opencode-rust |
A pinned stable minimal Rust toolchain | Rust crates, native helpers, and cross-language changes |
scv-opencode-tauri-linux |
GTK, WebKit, librsvg, Tauri prerequisites, and packaging utilities | Linux desktop compilation and packaging |
| Platform workers | Native Windows or macOS environment with the same SCV protocol | Platform behavior and release evidence that Linux cannot prove |
Derive later scv-node, scv-python, and scv-rust images from the same worker
contract. A new language requires an image and capability manifest, not a new
privilege in the Phoenix release.
Use this initial artifact layout when implementation starts:
ops/scv/images/opencode-core/Dockerfile
ops/scv/images/opencode-browser/Dockerfile
ops/scv/images/opencode-rust/Dockerfile
ops/scv/images/opencode-tauri-linux/Dockerfile
ops/scv/images/versions.env
ops/scv/worker/entrypoint.sh
Keep versions and expected digests in an operator-owned manifest. The entrypoint may start only the compiled worker release. It must not interpret repository input or construct a shell command.
OpenCode execution adapter
Implement OpenAgents.SCV.Executor.OpenCode behind the generic executor
protocol. Start with one admitted OpenCode process per run:
- Create isolated
HOME,XDG_CONFIG_HOME,XDG_DATA_HOME,XDG_STATE_HOME, andXDG_CACHE_HOMEdirectories. - Generate operator-owned OpenCode configuration through
OPENCODE_CONFIG_CONTENTand record its redacted digest. - Start
opencode run --format jsonwith the admitted model, directory, and bounded prompt. - Parse JSON events as nested observational records under one outer SCV execution step.
- Mark the outer step uncertain and discard its workspace if OpenCode exits without a terminal event. Do not infer which internal tool effects finished.
- Cancel the entire process tree when the lease, generation, deadline, budget, or operator state changes.
- Reconcile the OpenCode session ID and local database at termination, retain admitted artifacts, and destroy the run home.
Do not use --auto, --yolo, or --dangerously-skip-permissions. Those modes
bypass the permission boundary that an SCV needs to test. A later adapter may
run opencode serve inside each worker for durable multi-turn sessions. Bind it
to loopback, require a random OPENCODE_SERVER_PASSWORD, keep the password in
the sidecar, and address sessions by their recorded OpenCode session IDs.
The native JSON stream provides useful visibility, but it cannot prove that the SCV persisted each tool request before OpenCode executed it. Treat the first compatibility run as one coarse, disposable effect. Before crash-resumable candidate construction, add an SCV-specific OpenCode tool transport that:
- Disables OpenCode's direct edit and command execution for SCV sessions.
- Sends each typed tool request, session ID, run generation, and idempotency key to the sidecar.
- Persists the requested
scv_stepbefore the sidecar acknowledges it. - Executes the request in the credential-free candidate compartment after policy and generation checks pass.
- Returns a signed, digest-addressed result that OpenCode can use as its tool output.
- Resolves retries from the committed step instead of repeating the effect.
Implement this transport as a narrow OpenCode integration or admitted patch, not by parsing terminal output and reconstructing tool calls afterward. Keep OpenCode's SDK, server event stream, session API, and permission API available for session control, but do not confuse those APIs with the durable effect barrier.
OpenCode project configuration, plugins, Model Context Protocol servers,
skills, instructions, and language-server downloads are executable or
instruction-bearing repository inputs. The first adapter should disable
automatic project configuration and downloads with
OPENCODE_DISABLE_PROJECT_CONFIG=1 and
OPENCODE_DISABLE_LSP_DOWNLOAD=1. Resolve AGENTS.md and other required
repository instructions separately, admit their exact digests, and pass their
bounded content as evidence. Enable a project feature only after the host
policy classifies and receipts it.
Configure OpenCode permissions explicitly. Start from deny and admit only the
read, list, search, workspace edit, and structured command operations that the
current phase needs. Pass the operator-owned rule set through
OPENCODE_PERMISSION and record its digest. OpenCode's permission result is one
input to enforcement; the outer worker namespace, filesystem mounts, command
policy, cgroup, and network policy remain authoritative.
Provider credentials
Give OpenCode a short-lived, run-scoped inference grant for an OpenAI-compatible internal endpoint. Bind it to the SCV ID, run ID, admitted model set, request count, token budget, cost budget, expiry, and worker generation. Never place a reusable provider key in the image or workspace.
OpenCode normally launches tool processes beneath itself. Assume those child processes can inspect the OpenCode environment until isolation proves otherwise. The first experiment may use only a disposable, tightly budgeted grant with no authority outside inference. It does not satisfy the final credential-isolation requirement.
Before an SCV receives autonomous write or deployment authority, separate the OpenCode model process from candidate command execution. Route tool requests through the sidecar into a credential-free execution compartment. Keep the inference grant in the model compartment or authenticate to a local inference proxy through an out-of-band worker identity. Prove that candidate code cannot read, reuse, or transmit the grant.
First OpenCode validation run
Use a fixed workload against the inspected baseline before admitting arbitrary OpenCode work:
- Build and admit
scv-opencode-coreby digest. - Import the exact OpenCode baseline into the dedicated Forge qualification repository and verify its object and WAL receipts.
- Check out the exact OpenCode baseline in a clean disposable workspace.
- Verify the Bun, Node.js, Python, Git, and admitted OpenCode versions.
- Run
bun install --frozen-lockfileand record cold-cache and warm-cache receipts. - Run
bun typecheckandbun test --timeout 30000 --only-failuresfrompackages/opencode. - Run
bun run test:httpapifrompackages/opencodeon the core worker. - Run
bun run bench:testfrompackages/opencode, first with one measured run and then with explicitBENCH_WARMUPSandBENCH_RUNSvalues. - Run
bun run profile:testfrompackages/opencodewith explicitTEST_PROFILE_GLOB,TEST_PROFILE_LIMIT,TEST_PROFILE_TIMEOUT, andTEST_PROFILE_TOPvalues. - Start one bounded
opencode run --format jsonsession, exercise admitted read and command operations, and prove every permission denial and event reaches the SCV receipt chain. - Cancel a second run at each external boundary and prove generation fencing, process-tree termination, and workspace cleanup.
Run the browser performance suite only on scv-opencode-browser. Run it
serially against a production build, preserve the emitted BENCHMARK and
BENCHMARK_PAGE JSON records, and retain optional Chrome trace artifacts by
digest. Invoke bun run test:bench from packages/app. Use
bun run test:e2e:local from the same package for the normal end-to-end gate.
Do not make Rust, Tauri, Windows, or macOS gates mandatory for a change that
does not reach those surfaces.
The first improvement candidate should fix a seeded, reproducible OpenCode defect or performance regression with an existing or independently authored test. Keep the candidate propose-only. This proves the worker and evidence system before any Forge deployment path opens.
Worker pool and capability routing
Run multiple workers under one Elixir scheduler. Each worker registers an operator-admitted capability manifest containing:
- worker image and SBOM digests;
- operating system, architecture, and resource class;
- CPU count or admitted CPU class, memory and disk limits, and process limit;
- runtime, compiler, package-manager, browser, and OpenCode versions;
- supported command and network profiles;
- benchmark isolation and tracing capabilities;
- current state:
starting,ready,busy,draining,unhealthy, oroffline.
The scheduler matches a work item's required capabilities to an exact manifest. It must not infer compatibility from a worker name. A worker claims one execution lease with the current run generation, heartbeats while active, and rejects stale or duplicate requests. Draining prevents new claims without interrupting an admitted execution.
Keep one repository writer per integration history. Multiple workers may run read-only investigation, exact-SHA tests, benchmarks, or independent evaluation in parallel against immutable checkouts. Candidate-affecting results must bind to one exact SHA. A parallel result from an older SHA becomes evidence for a later decision; it cannot silently update the active candidate.
Separate the logical SCV run from its worker executions. One run may dispatch a core type check, browser benchmark, and Rust gate to different workers while the coordinator retains the run lease and joins their immutable receipts. Cancel or supersede each execution independently when its result is no longer needed.
Worker protocol
Use a versioned, language-neutral protocol so Linux containers, owned virtual machines, and native Windows or macOS workers implement the same boundary. Do not require a remote worker to join the BEAM cluster.
The protocol needs these operations:
- register an admitted image and capability manifest;
- heartbeat, renew an execution claim, and report health;
- claim the next compatible execution with its run generation;
- acknowledge a requested step only after the control plane persists it;
- stream bounded progress and host-observed measurements;
- publish a terminal receipt and artifact digests;
- cancel one execution or all executions for a run generation;
- drain, retire, and reject an image.
Authenticate workers with an SCV-specific machine identity over mutually authenticated transport. Bind every message to the SCV ID, run ID, execution ID, generation, protocol revision, and idempotency key. Sign or MAC terminal receipts and verify them before the coordinator changes durable state.
Let workers pull compatible work instead of accepting arbitrary commands on a
general remote-execution port. The control plane stores the request before it
becomes claimable. Large source bundles, logs, traces, and profiles move through
digest-addressed artifact storage; protocol messages carry bounded metadata and
artifact refs. Use Req for the Elixir HTTP client if the first protocol uses
HTTPS. Keep transport selection behind the protocol behavior so an admitted
queue can replace HTTPS without changing run semantics.
Resource, benchmark, and statistics evidence
Measure candidate processes from outside their namespace. OpenCode output and repository benchmark scripts provide domain metrics, but only the worker host can provide authoritative resource use.
Execution measurements
Record these values for each structured command and aggregate them for the run:
- monotonic start and finish times, wall duration, exit status, signal, retry, and cancellation reason;
- user and system CPU time, allocated CPU class, throttled CPU time, and throttling count;
- current and peak memory, swap use, page faults, and out-of-memory events;
- filesystem bytes read and written, workspace and cache size, disk peak, and inode use;
- process and thread peak, descendant count, and leaked-process findings;
- network bytes, destination classes, and denied connection count;
- standard output and error bytes, retained bytes, truncation, and artifact digests;
- cold, warm, or disabled cache state and relevant cache digests.
Collect cgroup or container-runtime counters before cleanup. Sample long commands at a bounded interval and store a compact time series outside the workspace. Candidate code cannot write or amend these measurements.
OpenCode usage measurements
Normalize OpenCode JSON events into SCV metrics for:
- session, provider, and model IDs;
- model requests, retries, time to first event, and total completion time;
- input, output, reasoning, cache-read, and cache-write tokens;
- estimated and provider-reconciled cost;
- tool calls by type, duration, result, output size, and permission decision;
- changed files, added and removed lines, commands, checkpoints, compactions, and terminal reason.
OpenCode's stats command aggregates sessions, messages, token classes, cost,
tool use, model use, date range, cost per day, tokens per session, and median
tokens per session from its local SQLite data. Import that result only as
reconciliation evidence. SCV step receipts and the inference ledger remain the
usage authority because a candidate can influence the local OpenCode store.
Benchmark receipts
Store every benchmark as a definition plus immutable samples. A definition includes the repository SHA, worker image digest, resource class, operating system and architecture, toolchain versions, command profile, dataset or fixture digest, cache policy, warmup count, measured run count, timeout, and environment digest.
A sample stores its raw metric records and artifacts plus normalized values. Summaries may report minimum, maximum, mean, median, and percentiles only when the sample count supports them. Keep failed and cancelled samples; removing them biases the result.
For OpenCode, ingest:
METRIC test_suite_seconds,test_suite_best_seconds, andtest_suite_worst_secondsfrombench:test;- per-file timings,
slowest_test_file_seconds, andprofiled_test_filesfromprofile:test; - application
BENCHMARKandBENCHMARK_PAGEJSON lines; - Chrome Performance traces and any admitted CPU or visual profiles;
- SCV host measurements for the same processes.
Compare a candidate with its recorded base only when the worker image, resource
class, operating system, architecture, toolchains, benchmark definition, cache
policy, and isolation level match. Otherwise mark the comparison
not_comparable. Use medians from repeated runs for performance decisions.
Keep correctness gates separate from performance evidence, and do not create
machine-dependent pass thresholds for OpenCode's browser benchmarks.
Reserve the benchmark worker exclusively for a comparison window. Run base and candidate samples on the same worker when possible, alternate their order, and record thermal, throttling, memory-pressure, and background-load invalidation signals. Do not publish an improvement when environmental noise exceeds the definition's admitted envelope.
Durable execution model
An SCV remains logically active while its work occurs in bounded runs. The coordinator repeats this sequence:
- Wake on a durable work item, admitted signal, or bounded poll interval.
- Acquire the repository's single-writer lease and increment its generation.
- Select one work item against the current policy, budget, and integration head.
- Start a bounded run with a fresh model context and isolated workspace.
- Persist every requested tool step before execution.
- Commit an honest terminal run result, including
no_change,refused, orbudget_exhausted. - If the run produced a candidate, advance it through gates and deployment as a separate durable state machine.
- Observe the outcome, update the work item, release the lease, and return to idle.
This model supports continuous operation without infinite prompts, unbounded mailboxes, permanent workspaces, or one process whose death loses the plan.
Context and checkpointing
Start each run with bounded, source-linked context:
- the exact base SHA and SCV integration ref;
- the admitted work-item objective and evidence refs;
- the SCV program and policy digests;
- relevant repository paths, symbols, and recent commits;
- focused test failures or sanitized operational facts;
- prior attempts for the same work-item fingerprint;
- current token, command, diff, time, and deployment budgets.
Store compact checkpoints after investigation, plan selection, each mutation, each test group, commit, gate, and deployment. A checkpoint is structured state, not a transcript dump. It should identify facts, evidence refs, decisions, changed paths, remaining work, and unresolved risks.
If context grows beyond its admitted limit, start a new provider response from the latest durable checkpoint. Do not trust a model-authored summary without the source refs needed to verify it.
SCV program
Define a versioned OpenAgents.SCV.Program artifact with a calculated digest
and an admitted digest, following the repository's existing persona and role
artifact discipline without making an SCV a persona.
The SCV program should require this method:
- State the observed problem and its evidence.
- Read the relevant implementation, tests, invariants, and documentation.
- Define the smallest verifiable outcome.
- Add or identify a failing test before changing behavior.
- Make a focused change.
- Run the narrowest useful check, then the required candidate gate.
- Inspect the final diff for unrelated or policy-protected changes.
- Commit once the candidate is coherent.
- Report uncertainty, omitted work, and exact receipts.
Repository files, issues, test output, comments, commit messages, dependency metadata, and incident descriptions remain untrusted input. They cannot change the SCV program, tool authority, budget, protected paths, risk class, gate, or deployment policy.
Work discovery and selection
An SCV should optimize against observable product and engineering outcomes. It should not make changes to remain busy.
Initial work sources
Admit these sources first:
- operator-created SCV work items;
- reproducible failing tests from the owned gate;
- compile warnings and static contract failures;
- typed, recurring incidents with sanitized evidence;
- documented TODO items that name an expected result and owner-approved scope;
- focused coverage gaps for high-risk code when the work item names the missing behavior;
- measurable performance regressions with a stable benchmark.
Delay broad dependency updates, external vulnerability feeds, speculative refactoring, and free-form issue ingestion until their authority and network contracts are explicit.
Admission score
Score a work item with host-owned data:
- user or operator impact;
- reproducibility and evidence quality;
- confidence that the repository contains the fix;
- expected diff and deployment risk;
- estimated test and inference cost;
- recurrence and age;
- collision with active human work;
- cooldown after a prior failed attempt.
The model may recommend a score, but host code calculates the admitted score and selects the next item. Deduplicate work by a stable fingerprint over the repository, evidence class, affected surface, and normalized problem code.
Valid idle behavior
When no item clears the admission threshold, an SCV remains idle. Idle is a healthy state. Avoid goals such as "improve the codebase" without a measurable problem because they reward churn, test rewriting, and style-only diffs.
Workspace and Git model
Exact base
Create each workspace from an exact commit in the WAL-backed Forge repository. Do not copy the running image tree into a writable directory and do not clone from GitHub.
Use a linear SCV integration ref, initially
refs/heads/scv/integration. Each run:
- Reads the ref and its durable WAL position.
- Records the exact base SHA in the run.
- Checks out that SHA detached in a fresh workspace.
- Creates
refs/heads/scv/runs/<run-id>for its candidate. - Pushes with an expected-old-SHA compare-and-swap condition.
Only one repository-writing SCV runs at first, but the compare-and-swap remains required. It catches operator changes, restore races, and future concurrency.
Keeping improvements
Do not base each run on the default branch or current image independently. A candidate deployed from a run branch can be absent from the next default-branch clone. The SCV integration ref must advance only after the candidate reaches its admitted terminal state:
- For a source-only candidate, advance after its source gate passes.
- For a runtime candidate, advance after Forge marks it
liveand the observation window passes. - For a reverted or failed candidate, leave the integration ref on its predecessor.
Advance the integration ref through the normal authenticated Forge push path so the WAL remains ref authority. Use compare-and-swap against the recorded base. Do not update a bare repository ref directly from application code.
The Forge is canonical. Keep an SCV in propose-only mode until a separately
admitted write policy exists. Decide whether scv/integration becomes the
default branch or merges into it through another policy-controlled
fast-forward. Do not operate a second writable history through the GitHub
mirror.
Workspace lifecycle
Keep a run workspace until its candidate reaches a terminal result and retain only bounded diagnostic artifacts afterward. Remove it after success, refusal, failure, cancellation, or rollback. Never reuse a dirty workspace for another run.
Tool and command design
Reuse repository read, grep, list, exact-edit, write, and commit semantics where they fit. Add SCV-specific tools for:
git status, diff, log, show, merge-base, and blame projections;- file creation, deletion, and rename with path and byte bounds;
- focused test discovery and execution;
- Mix help and admitted Mix tasks;
- OpenCode runs through the admitted OpenCode adapter;
- repository-defined Bun, Node.js, Python, Rust, browser, and Mix commands through capability-specific profiles;
- formatting and final diff inspection;
- checkpoint and candidate submission.
Do not expose a raw shell-string tool. The executor should receive an executable name, argument list, working directory, environment profile, time limit, and output limit as structured data. Invoke it without a shell.
A small command allowlist is safer but may be too restrictive for useful coding work. Use policy profiles instead:
- A read profile admits bounded Git and source-inspection commands.
- A focused-test profile admits exact repository-owned test entry points and repository tasks after validating their options.
- A candidate-gate profile admits only the immutable gate definition.
- A networked profile remains disabled in the first release.
Validate environment variables against an allowlist and build a fresh environment. Never inherit the coordinator's complete environment. Redact output before persistence and retain full output only in an operator-only, short-lived store with a digest in the receipt.
Promotion, deployment, rollback, policy changes, budget increases, and integration-ref advancement are host actions. Do not advertise them as model tools.
Durable records
Prefer separate tables over extending user-facing work rows.
scvs
Store one logical SCV identity per repository:
- repository;
- status:
disabled,idle,running,paused, orcircuit_open; - admitted program and policy revisions and digests;
- integration ref and admitted head SHA;
- owner node, lease generation, and lease expiry;
- current run and candidate IDs;
- budget window counters;
- last healthy and last terminal timestamps.
scv_work_items
Store durable candidate work:
- source and source reference;
- stable deduplication fingerprint;
- bounded title, objective, and sanitized evidence refs;
- admitted risk ceiling and repository scope;
- priority inputs and calculated score;
- status:
discovered,admitted,running,completed,deferred,refused, orfailed; - attempt count, cooldown, and terminal reason.
scv_runs
Store one bounded execution episode:
- SCV, work item, base SHA, integration WAL position, and generation;
- program, policy, tool-catalog, evaluator, and gate digests;
- model and provider adapter IDs;
- requested worker capabilities, benchmark definitions, and comparison base;
- phase and terminal status;
- token, cost, tool, command, time, CPU, memory, disk, and diff usage;
- structured checkpoint and bounded report;
- candidate ID and error code;
- start and completion timestamps.
scv_steps
Store every provider and tool boundary in order:
- sequence, provider response and call IDs, and prior-response ID;
- tool version, artifact digest, arguments digest, and output digest;
- requested, claimed, terminal, and uncertain timestamps;
- executor identity, status, error code, and receipt refs;
- run generation and idempotency key.
Store large input and output only in a bounded, access-controlled artifact store when diagnosis requires it. Database rows should contain redacted excerpts and digests.
scv_worker_images
Store admitted immutable worker manifests:
- image, SBOM, Dockerfile, base image, source, and build receipt digests;
- operating system, architecture, toolchains, OpenCode runtime, and supported capability names;
- default resource bounds and permitted command and network profiles;
- admission, retirement, and vulnerability-review state;
- creation and immutable admission timestamps.
scv_workers
Store the current worker registration and lease surface:
- stable worker ID, admitted image ID, resource class, and capabilities digest;
- state, health, drain request, and last heartbeat;
- current execution, run generation, claim expiry, and coordinator owner;
- boot identity and monotonic registration generation;
- bounded health details without hostnames, credentials, or internal addresses in operator projections.
scv_executions
Store one dispatch to one worker:
- run, step, worker image, required capabilities, and exact repository SHA;
- execution generation, idempotency key, claim, start, heartbeat, cancellation, and terminal timestamps;
- command profile and redacted environment digest;
- exit, signal, timeout, cancellation, and uncertainty result;
- resource summary, output, event stream, and artifact refs.
scv_benchmark_definitions
Store the admitted comparison contract:
- stable name and revision;
- repository paths, structured commands, metric parsers, fixture digests, and required capabilities;
- warmup, repetition, serial-execution, cache, timeout, and tracing rules;
- normalized metric names, units, direction, and decision policy;
- operator admission and immutable definition digest.
scv_benchmark_runs and scv_benchmark_samples
Store the benchmark execution and its samples:
- definition, base or candidate SHA, worker image, resource class, toolchain, cache, and environment digests;
- sample ordinal, warmup flag, status, raw metric refs, normalized metrics, and host resource summary;
- aggregate statistics, comparison result, confidence policy, and
not_comparablereasons; - retained trace, profile, and diagnostic artifact refs.
scv_resource_samples
Store compact host-observed measurements for long executions:
- execution ID, monotonic offset, and sample interval;
- CPU, throttling, memory, swap, filesystem, process, and network counters;
- cgroup or runtime source and collection error;
- immutable sample and summary digests.
scv_candidates
Store the immutable candidate decision:
- run, base SHA, candidate SHA, run ref, and changed paths;
- diff digest, line and byte counts, and semantic risk findings;
- focused-test and gate receipt refs;
- policy decision and policy digest;
- source integration, Forge target, build, deploy, and predecessor refs;
- observation window, measurements, terminal result, and rollback target;
- immutable timestamps for each transition.
Use database constraints and triggers for forward-only terminal states and immutable receipt fields. Treat PubSub and UI projections as hints.
Run state machine
Use a state machine that distinguishes source construction from deployment:
queued
-> claiming
-> investigating
-> editing
-> focused_testing
-> candidate_committed
-> candidate_pushed
-> gating
-> policy_review
-> source_admitted
-> promoting
-> building
-> deploying
-> observing
-> completed
Every nonterminal state may move to failed, refused, cancelled,
budget_exhausted, or superseded where appropriate. A deployed candidate may
move from observing to reverted. no_change is a terminal result from
investigation or focused testing.
Do not store one generic running status and infer the operation from logs.
Recovery and operator controls need the exact durable phase.
Change policy
An SCV's most important code is the host-owned change classifier. It should combine changed paths, diff structure, AST-level findings where practical, Forge's build classification, and explicit protected-surface rules. A path allowlist alone cannot recognize an authorization change hidden in a general module.
Recommended risk classes
| Class | Examples | Initial action |
|---|---|---|
| Source-only | Documentation, comments, tests that add coverage, and development-only diagnostics | Require human review first. After a separate source-only policy admission, gate and advance the SCV integration ref without creating a fleet target when runtime output is unchanged. |
| Low-risk runtime | Focused bug fix in an explicitly admitted module, no interface or state-shape change, direct-load classification, and strong regression test | Human promotion first; staging autodeploy after policy proof. |
| Moderate runtime | New route, changed API shape, process-state behavior, broad refactor, or cross-context behavior | Require human review and the complete exact-SHA release gate. |
| Structural | Dependencies, assets, runtime configuration, migrations, releases, module deletion, native code, relup, or rolling replacement | Require human review. Admit staging automation only in a later policy revision with independent evidence. |
| Protected | SCV policy, Forge control, authentication, authorization, secrets, billing, data rights, invariant weakening, or release-gate weakening | Never auto-approve. Require an external operator path and independent evaluation. |
Protected surfaces
The initial policy should refuse automatic promotion when a candidate changes:
AGENTS.md,INVARIANTS.md, the SCV program, SCV policy, or SCV evaluator;.githooks/,ops/ci/, release-gate code, coverage floors, or test filters;OpenAgents.Forge, deployment providers, boot convergence, or release code;- authentication, authorization, token, vault, secret, route-authority, or operator modules;
- migrations, schemas with durable-state meaning, or database triggers;
- provider credential handling, inference pricing, budgets, or metering;
- memory consent, data rights, publication visibility, or private-data bounds;
- dependencies, lockfiles, Dockerfiles, Terraform, runtime configuration, or production infrastructure;
- existing tests whose removal or weakening reduces a protected assertion.
An SCV may propose changes in these areas on its run branch, but the candidate must stop at human review. The automatic lane must evaluate the complete diff, including generated files and renames.
Policy independence
Evaluate a candidate with the policy revision that existed at the run's base SHA and a host-installed minimum policy. If a candidate changes policy code, tests, or configuration, those changes cannot affect its own decision.
Use a two-key rule for later policy expansion: an operator admits the new policy digest, and an independent evaluator proves its regression corpus. An SCV can author a policy change, but it cannot supply either approval key.
Candidate gates
Run checks in increasing order of cost and stop on the first failure:
- Confirm workspace cleanliness and exact base ancestry.
- Validate changed paths, diff bounds, generated artifacts, and protected surfaces.
- Run formatting and focused regression tests.
- Run the repository's admitted compile or type-check profile with warnings as errors where the toolchain supports it.
- Run the repository's final precommit profile. Use
mix precommitforopenagents.comand the exact package-scoped Bun gates for OpenCode. - Commit and push the exact candidate SHA.
- Run the required exact-SHA release gate in a fresh checkout.
- Verify that the gate definition digest matches the base policy.
- Have Forge build and classify the exact pushed SHA independently.
- Compare Forge's manifest and structural findings with the SCV policy decision.
The final gate must run after commit because the repository's release receipts bind an exact SHA. Run it in a fresh checkout so ignored files, a dirty worktree, or the SCV's build cache cannot change the result.
For a low-risk automatic candidate, require all of these facts:
- the candidate descends from the admitted integration head;
- focused tests prove the reported defect or improvement;
- no protected surface changed;
- the repository's admitted final gate passes without retries or modified thresholds;
- the exact-SHA gate passes in the trusted evaluator;
- Forge independently classifies the complete candidate as
direct_candidate; - every changed runtime module matches the narrower SCV allowlist and Forge's operator-owned allowlist;
- the deployment budget and cooldown admit another target;
- no active incident, deploy, rollback, or human freeze blocks promotion.
Automatic promotion
Keep push and promotion separate. A pushed SCV candidate should wake an SCV
policy evaluator, not OpenAgents.Forge.Targets directly.
Add a typed promotion principal such as:
principal_type: scv
principal_id: <stable-scv-id>
policy_digest: <admitted-policy-digest>
candidate_id: <immutable-candidate-id>
gate_receipt_ref: <exact-sha-gate-receipt>
decision_digest: <complete-policy-input-digest>
The promotion API should accept either an authenticated operator receipt or an admitted SCV receipt. It should verify the principal and evidence server-side, then insert the same append-only Forge target used by a human promotion.
This amendment preserves the useful boundary that a push never promotes itself. The model cannot promote. The repository tools cannot promote. The SCV coordinator can request promotion only after host code has produced the admitted receipt.
Record the promotion authority class explicitly in the target schema. Do not
overload a display string such as operator:scv because it would make audits
and authorization ambiguous.
Deployment and verification
Deployment sequence
After an admitted SCV promotion:
- Wait for Forge to build the exact SHA and persist its receipt.
- Require the build classification to match the SCV policy decision.
- Let Forge select and execute the admitted deployment strategy.
- Wait for the target and terminal deploy receipt from durable state, not only PubSub.
- Start a post-deployment observation window after the target reaches
live. - Run candidate-specific probes plus common health and readiness checks.
- Compare bounded operational measurements with the recorded predecessor baseline.
- Mark the candidate complete and advance the integration ref only after the observation window passes.
An SCV must never call BEAM loading functions or cloud deployment APIs itself. Forge owns those effects and their rollback contracts.
Observation contract
Define the expected signals before promotion. Use candidate-specific signals where possible:
- the new regression test remains green against the packaged or live target;
/healthand deployment readiness remain healthy;- fleet revision and artifact identities remain consistent;
- affected error codes do not regress;
- latency, memory, mailbox, and restart measurements remain within an admitted envelope;
- no new anomalous incident correlates with the candidate;
- the candidate's intended product outcome is observable when a safe synthetic probe exists.
Avoid a single global "error rate" gate that may miss a focused regression or react to unrelated traffic. Store baseline interval, candidate interval, sample size, missing-data result, and comparison policy in the candidate. Missing required data should refuse admission rather than count as success.
Rollback
Forge already reverts a failed deployment transaction before it marks a target
live. Post-live regression needs a second path: promote the exact predecessor
as a new target with an scv_automatic_rollback receipt, run the normal Forge
pipeline, and verify convergence.
Open the SCV circuit when any automatic candidate:
- requires post-live rollback;
- cannot verify rollback;
- leaves fleet identity divergent;
- creates an anomalous incident in a protected plane;
- exceeds its observation budget without enough evidence.
After the circuit opens, an SCV may continue read-only diagnosis if policy allows it, but it cannot push, integrate, promote, or deploy until an operator records a resume receipt.
Control-loop stability
Continuous improvement can become an unstable feedback loop. Add these controls from the first autonomous release:
- One active repository-writing run and one active candidate per repository.
- A cooldown between live candidates.
- A daily deployment budget and a separate rollback budget.
- A stable work-item fingerprint and retry backoff.
- A limit on changed files, lines, bytes, commits, and modules.
- A maximum number of attempts before operator review.
- A ban on immediately undoing and redoing the same change without new evidence.
- A predecessor comparison that detects oscillation between two SHAs.
- A change-frequency cap per subsystem.
- A freeze during incidents, migrations, operator maintenance, or fleet degradation.
- An operator pause that takes effect before the next external effect and a kill action that cancels current provider and executor work.
Do not use deployment count or lines changed as an SCV success metric. Prefer resolved reproducible failures, prevented incidents, retained regression tests, measured performance improvement, rollback-free observation windows, and operator acceptance.
Budgets
Use nested budgets:
- Step budget: input and output bytes, provider tokens, command output, command duration, and retries.
- Run budget: model calls, tool calls, continuations, wall time, CPU, memory, disk, changed files, diff size, and commits.
- Candidate budget: gate time, build attempts, deployment attempts, observation duration, and rollback attempts.
- Window budget: daily tokens, estimated cost, runs, pushes, promotions, deployments, and rollbacks.
Store the budget snapshot on the run before execution. A later configuration
increase must not widen an active run. When a run reaches a bound, refuse new
effects, request a tool-free bounded report when possible, and commit an honest
budget_exhausted result.
Start with conservative staging values and tune them from receipts. A useful initial posture is one 45-minute run at a time, one candidate in flight, a 15-minute post-live observation window, at most four automatic staging deployments per 24 hours, and an immediate circuit open after one rollback. Keep these settings operator-owned and runtime-validated instead of hard-coding them in a prompt.
Recovery and idempotency
Use PostgreSQL as the run fence, following the existing work recovery contract:
- Claim a run under a row lock, record
owner_node, incrementgeneration, and set a bounded lease. - Require the current generation on every checkpoint and terminal update.
- Reclaim only after the prior lease expires or its node is proven absent.
- Keep completed steps immutable.
- Resume only from committed outcomes.
Classify effects by recovery behavior:
| Effect | Recovery rule |
|---|---|
| Repository reads and deterministic analysis | Safe to repeat against the recorded SHA |
| Provider planning call | Safe to replace with a new call from a committed checkpoint; do not claim the interrupted response completed |
| File edit in an isolated workspace | Re-read and verify the expected digest before repeating |
| Test or compile command | Safe to repeat in a clean candidate workspace |
| Commit | Resolve by the run's tree digest and recorded ref before creating another commit |
| Push | Resolve the run ref and WAL receipt before retrying with compare-and-swap |
| Integration-ref advance | Resolve the WAL position and expected predecessor; never repeat blindly |
| Promotion | Deduplicate by candidate ID and promotion-decision digest |
| Deployment | Forge target and deployment IDs are authority; an SCV only observes or requests rollback |
If the executor dies during a command whose external effects cannot be
resolved, mark the step uncertain, fail the run closed, and require a new
workspace. Do not infer success from partial output.
Security boundaries
Service identity
Give an SCV separate, narrow identities for:
- provider use and usage accounting;
- coordinator-to-executor requests;
- Forge fetch;
- run-ref push;
- promotion receipt signing or verification;
- read-only operational measurements.
Do not reuse a browser session, user API token, computer pairing token, Forge operator token, release cookie, or cloud deployment identity.
Repository content and prompt injection
Treat all repository and work-item text as data. Host code must enforce:
- tool names and versions;
- exact repository and workspace roots;
- command profiles and arguments;
- environment variables and network access;
- path and output bounds;
- policy and gate digests;
- promotion and rollback admission.
A comment that says to ignore policy, expose credentials, weaken tests, or deploy directly is an input-quality incident, not an instruction.
Candidate execution
Assume candidate tests can read every mounted file and connect to every allowed network destination. Use a disposable database with synthetic data, an empty home directory, no forwarded SSH socket, no cloud metadata access, and no inherited credential helpers. Pin dependencies before disabling external network access for candidate execution.
Data minimization
An SCV needs code and content-free operational facts, not user data. Incident inputs should carry typed codes, affected component, recurrence, timestamps, and sanitized stack or test refs. Never attach prompts, messages, memory, transcripts, OAuth data, or arbitrary production rows.
Observability and operator controls
Add an operator-only SCV surface with stable IDs and bounded projections. Show:
- SCV status, policy and program revision, lease generation, and integration head;
- active and recent work items, runs, candidates, and budgets;
- current phase, elapsed time, and cancellation state;
- worker pool health, admitted image digests, capabilities, resource classes, active executions, queue pressure, and drain state;
- changed paths and diff summary after a candidate exists;
- focused tests, exact-SHA gate, Forge build, target, deployment, and observation receipts;
- resource summaries, comparable benchmark results, OpenCode usage totals, and retained trace or profile refs;
- terminal result, rollback state, and circuit reason;
- Pause, Resume, Cancel run, Reject candidate, Require human review, and Open diff controls.
Do not expose raw prompts, private incident content, credentials, internal node names, full build logs, or unrestricted command output. Public changelog entries may name an SCV as the source role only after the existing repository visibility policy admits the candidate.
Emit content-free telemetry for:
- work discovery and admission;
- run, provider, tool, and command duration;
- token and cost use;
- worker claims, queue time, utilization, health, resource use, and out-of-memory results;
- benchmark samples, comparison eligibility, regressions, and improvements;
- candidate refusal reasons;
- gate and build outcomes;
- promotion, deploy, observation, and rollback results;
- lease recovery, stale generation refusal, and circuit changes.
Runtime configuration
Add typed, fail-closed settings such as:
OPENAGENTS_FEATURE_SCV
OPENAGENTS_SCV_MODE=observe|propose|staging_auto|production_auto
OPENAGENTS_SCV_REPOSITORIES=anomalyco/opencode
OPENAGENTS_SCV_PROGRAM_REVISION=<revision>
OPENAGENTS_SCV_POLICY_REVISION=<revision>
OPENAGENTS_SCV_PROCESS_ROLE=coordinator|worker
OPENAGENTS_SCV_EXECUTOR=<adapter>
OPENAGENTS_SCV_EXECUTOR_QUEUE_DIR=<absolute-path>
OPENAGENTS_SCV_WORKSPACE_DIR=<absolute-path>
OPENAGENTS_SCV_WORKER_IMAGES=<admitted-manifest-digests>
OPENAGENTS_SCV_OPENCODE_IMAGE=<admitted-manifest-digest>
OPENAGENTS_SCV_MAX_WORKERS=<bounded-integer>
OPENAGENTS_SCV_MAX_ACTIVE_RUNS=1
OPENAGENTS_SCV_MAX_ACTIVE_REPOSITORY_WRITERS=1
OPENAGENTS_SCV_WORKER_CPU=<bounded-resource-class>
OPENAGENTS_SCV_WORKER_MEMORY_BYTES=<bounded-integer>
OPENAGENTS_SCV_WORKER_DISK_BYTES=<bounded-integer>
OPENAGENTS_SCV_RESOURCE_SAMPLE_MS=<bounded-integer>
OPENAGENTS_SCV_RUN_TIMEOUT_MS=<bounded-integer>
OPENAGENTS_SCV_BENCHMARK_RETENTION_MS=<bounded-integer>
OPENAGENTS_SCV_ARTIFACT_RETENTION_MS=<bounded-integer>
OPENAGENTS_SCV_DAILY_TOKEN_BUDGET=<bounded-integer>
OPENAGENTS_SCV_DAILY_COST_MICROUSD=<bounded-integer>
OPENAGENTS_SCV_DAILY_DEPLOYMENTS=<bounded-integer>
OPENAGENTS_SCV_DEPLOY_COOLDOWN_MS=<bounded-integer>
OPENAGENTS_SCV_OBSERVATION_MS=<bounded-integer>
The runtime boundary should reject:
- any enabled mode without admitted program and policy digests;
- a worker whose image, SBOM, capability, or command-profile digest is not admitted;
- an OpenCode adapter without an admitted OpenCode image and explicit permissions;
- a worker process role that also starts the Phoenix endpoint, application Repo, Forge control plane, or deployment coordinators;
- an executor path under
/tmpin staging or production; - an automatic mode before Forge deployment, boot convergence, durable artifacts, and isolated staging are enabled;
production_autowithout a separate production-autonomy admission and policy receipt;- multiple active runs in the first policy revision;
- multiple repository writers for one integration history;
- a benchmark comparison across incompatible provenance;
- a deployment budget without an observation window and rollback authority;
- an SCV repository that is absent from the configured Forge repositories.
Readiness should report only enabled mode, admission status, circuit state, and whether dependencies validate. It must not print paths, URLs, credentials, work-item content, prompts, or internal identities.
Suggested module layout
Keep one module per file.
lib/openagents/scv.ex
lib/openagents/scv/driver.ex
lib/openagents/scv/driver/open_code.ex
lib/openagents/scv/environment.ex
lib/openagents/scv/instance.ex
lib/openagents/scv/work_item.ex
lib/openagents/scv/run.ex
lib/openagents/scv/step.ex
lib/openagents/scv/candidate.ex
lib/openagents/scv/worker_image.ex
lib/openagents/scv/worker.ex
lib/openagents/scv/worker_supervisor.ex
lib/openagents/scv/worker_client.ex
lib/openagents/scv/worker_runner.ex
lib/openagents/scv/runner.ex
lib/openagents/scv/runner/local.ex
lib/openagents/scv/execution.ex
lib/openagents/scv/benchmark_definition.ex
lib/openagents/scv/benchmark_run.ex
lib/openagents/scv/benchmark_sample.ex
lib/openagents/scv/resource_sample.ex
lib/openagents/scv/program.ex
lib/openagents/scv/policy.ex
lib/openagents/scv/change_classifier.ex
lib/openagents/scv/budget.ex
lib/openagents/scv/coordinator.ex
lib/openagents/scv/recovery.ex
lib/openagents/scv/context.ex
lib/openagents/scv/provider_loop.ex
lib/openagents/scv/tool_catalog.ex
lib/openagents/scv/workspace.ex
lib/openagents/scv/executor.ex
lib/openagents/scv/executor/open_code.ex
lib/openagents/scv/executor/sidecar.ex
lib/openagents/scv/executor_protocol.ex
lib/openagents/scv/worker_scheduler.ex
lib/openagents/scv/resource_collector.ex
lib/openagents/scv/benchmark_parser.ex
lib/openagents/scv/gate.ex
lib/openagents/scv/promoter.ex
lib/openagents/scv/observer.ex
lib/openagents/scv/circuit.ex
Use test adapters under test/support. Keep Forge changes in
lib/openagents/forge/ when they generalize promotion principals or receipts;
do not make Forge import the SCV context.
Implementation phases
Phase 0: Amend contracts
- Add an ADR for policy-authorized SCV promotion.
- Amend
SELF-EDIT-001,docs/architecture.md, and the route-authority ledger to distinguish human and SCV promotion receipts. - Define the first SCV program, policy, protected surfaces, risk classes, and evaluator corpus.
- Define service identities and secret inventory entries.
- Keep the feature disabled.
Exit criteria: Documentation and tests agree on who can admit an SCV candidate, which changes can qualify, and how the operator stops the system.
Phase 1: Observe and queue
- Add SCV, work-item, run, step, candidate, worker, execution, resource, and benchmark schemas with database guards.
- Add the coordinator, lease, budgets, recovery, pause, and circuit state.
- Ingest operator work items and sanitized owned-gate failures.
- Run selection and planning without repository writes.
- Add the capability scheduler, worker registration, operator surface, and content-free telemetry without starting candidate commands.
Exit criteria: An SCV runs continuously in observe mode, survives process
and node loss, deduplicates work, spends within budget, and performs no external
effect.
Phase 2: Build candidates
- Build and admit
scv-opencode-coreby digest before any generic language worker. - Add the isolated executor protocol, OpenCode adapter, capability scheduler, worker leases, and resource collector.
- Add exact Forge checkout, workspace confinement, the durable OpenCode tool transport, SCV tools, OpenCode event receipts, command profiles, and candidate-code sandboxing.
- Register multiple
scv-opencode-coreworkers, but retain one repository writer for the OpenCode integration history. - Run the fixed OpenCode validation workload, ingest its native benchmarks, and prove credential, permission, cancellation, and generation boundaries.
- Add focused tests, commit resolution, run-ref push, WAL receipt linking, and cleanup.
- Keep every candidate propose-only.
Exit criteria: An SCV can use OpenCode internally to reproduce, test, patch, commit, and push a bounded OpenCode candidate. Multiple workers can execute exact-SHA read and gate work without creating multiple writers. Resource and benchmark receipts remain complete across crash tests, and no uncertain push or command becomes success.
Phase 3: Qualify and review OpenCode
- Add the independent change classifier and protected-surface enforcement.
- Add immutable focused-test, repository precommit, exact-SHA release-gate, benchmark, and evaluator receipts.
- Run the OpenCode runtime qualification and self-targeting proof against the inspected baseline.
- Add human review from the SCV candidate surface without a deployment action.
- Reconstruct the complete chain from work evidence through OpenCode session, worker executions, resource samples, candidate push, exact-SHA gate, and benchmark comparison.
Exit criteria: A human can review a propose-only OpenCode candidate with all evidence visible. The admitted OpenCode runtime remains separate from the candidate checkout, and every refused class stops before source admission.
Phase 4: Pilot openagents.com
- Build and admit an
scv-openagentscapability image with the repository's pinned Elixir, Erlang, Mix, asset, database, and test toolchains. Keep the admitted OpenCode runtime as the model and tool-loop process. - Run the fixed fixture defect through focused tests,
mix precommit, the exact-SHA release gate, and independent policy evaluation. - Add human promotion from the SCV candidate surface.
- Reconstruct the complete chain from work evidence to the Forge deploy and observation receipts.
Exit criteria: A human can review and promote a low-risk
openagents.com candidate with all evidence visible, and every refused class
stops before promotion.
Phase 5: Automate isolated staging
Start only after ADR 0007's Forge-canonical cutover and the isolated staging deployment gates pass.
- Add typed SCV promotion principals and decision receipts.
- Admit only the low-risk direct-load class.
- Add post-live observation, automatic predecessor promotion, cooldown, daily deployment budgets, and the circuit breaker.
- Run failure injection for provider, worker, WAL, database, build, fleet, health, observation, and rollback failures.
- Hold the staging SCV through a defined soak with no unexplained state.
Exit criteria: Staging proves repeated exact-SHA improvements and deliberate failure cases without mixed revisions, lost commits, unreceipted effects, or continued deployment after rollback.
Phase 6: Expand admitted scope
Expand one independent policy revision at a time. Possible later admissions include broader direct-load modules, source-only integration, relup candidates, and rolling replacement in isolated staging. Require a new regression corpus, operator approval, and soak for each expansion.
Production autonomy requires a separate plan and approval after production deployment itself becomes authorized. Do not infer production approval from a successful staging SCV.
Test plan
Lifecycle and recovery
- Prove single-writer lease acquisition, renewal, expiry, and stale-generation refusal.
- Kill the coordinator during every phase and recover from durable state.
- Prove completed steps and terminal runs cannot change.
- Prove duplicate work evidence, provider events, executor responses, pushes, and promotion requests do not repeat effects.
- Prove uncertain commands fail closed.
Workspace and executor
- Refuse absolute paths, traversal, symlink escape, invalid refs, stale bases, oversized files, and output overflow.
- Prove every run starts from the recorded exact SHA in a clean workspace.
- Prove candidate code receives no coordinator, Forge operator, database, cloud, or user credential.
- Prove network and environment profiles fail closed.
- Prove cancellation terminates descendant processes and cleans the workspace.
- Prove the target checkout cannot change the admitted OpenCode runtime.
- Prove OpenCode home and XDG data never cross run or worker boundaries.
- Prove candidate commands cannot read or reuse the inference grant before enabling autonomous authority.
Workers and scheduling
- Refuse unadmitted image, SBOM, capability, resource, and command-profile digests.
- Match every execution to its required operating system, architecture, toolchain, browser, and tracing capabilities.
- Prove worker heartbeat expiry and drain behavior cannot duplicate an execution.
- Run read-only exact-SHA executions on multiple workers while one writer owns the integration history.
- Supersede late results from an older candidate without rewriting their immutable receipts.
- Prove a platform-specific gate never runs on an incompatible worker.
Model and tools
- Validate SCV program and tool-catalog digests at boot.
- Reject parallel tool calls when the host supports only serial execution.
- Persist a tool request before execution and continue only from its committed outcome.
- Force a bounded report on token, continuation, command, and wall-clock limits.
- Run adversarial repository text that asks an SCV to expose secrets, weaken gates, expand authority, or deploy directly.
- Refuse OpenCode automatic-permission flags, project plugins, unadmitted Model Context Protocol servers, and automatic language-server downloads.
- Reconcile OpenCode session statistics against SCV steps and the inference ledger without trusting the local SQLite store as authority.
Resources and benchmarks
- Compare cgroup or runtime CPU, memory, disk, process, and network summaries with known fixture workloads.
- Prove candidate code cannot amend host-observed resource samples.
- Parse OpenCode
METRIC,BENCHMARK, andBENCHMARK_PAGErecords and retain raw artifacts by digest. - Refuse comparisons when image, resource class, platform, toolchain, benchmark definition, cache policy, or isolation differs.
- Retain failed, cancelled, timed-out, and out-of-memory samples.
- Prove benchmark processes run serially when the definition requires it.
Change policy
- Cover every source-only, low-risk, moderate, structural, and protected class.
- Refuse test deletion, assertion weakening, skipped tests, coverage-floor reduction, gate edits, renamed protected files, and generated protected output.
- Evaluate with the base policy when the candidate changes policy code.
- Require both the narrower SCV allowlist and Forge allowlist.
- Prove an unknown or conflicting classification fails closed.
Git and receipts
- Prove compare-and-swap run-ref and integration-ref updates.
- Prove a run includes prior admitted SCV changes.
- Prove a failed or reverted candidate does not advance the integration ref.
- Resolve a crash after push from the WAL receipt without pushing twice.
- Reconstruct work item, run, steps, commit, push, gate, target, build, deploy, observation, and rollback by immutable refs.
Deployment
- Prove a push alone never promotes.
- Refuse an SCV promotion without an admitted policy, exact-SHA gate, candidate decision, and budget.
- Prove an operator freeze wins every promotion race.
- Prove superseded-target handling and one candidate in flight.
- Inject canary, fleet, readiness, observation, and rollback failures.
- Open the circuit after one rollback and require an operator resume receipt.
End-to-end proof
Create a fixture defect with a stable failing test. An SCV should:
- Admit the work item.
- Read the relevant code and invariant.
- Add or select the regression test.
- Make the smallest patch.
- Pass focused checks and the repository's admitted final gate.
- Commit and push an exact SHA to its run ref.
- Pass the independent exact-SHA gate and change policy.
- Produce a policy-bound promotion receipt.
- Reach
livethrough Forge's transactional lane. - Pass the observation window and advance the integration ref.
Repeat with an injected post-live regression. The second proof must promote the predecessor, verify restoration, leave the integration ref unchanged, and open the circuit.
Recommended first release
Ship the first SCV with this deliberately narrow posture:
- one repository: OpenCode, initially anchored to the inspected
devbaseline; - one logical SCV, one active run, and one repository writer;
- multiple registered
scv-opencode-coreworkers for read-only investigation, exact-SHA gates, and benchmark execution; scv-opencode-coreas the first admitted image, with browser, Rust, Tauri, Windows, and macOS images added only when a required gate needs them;observeandproposemodes only;- operator-created work items plus reproducible owned-gate failures;
- a dedicated SCV program, tool catalog, service principal, and budget ledger;
- an OpenCode executor behind a sidecar with no production secrets or general candidate-command network access;
- explicit OpenCode permissions, isolated XDG state, and no automatic permissions or unadmitted project extensions;
- run branches and complete receipt chains;
- source, diff, package-scoped type-check, focused-test, HTTP API, resource, and benchmark receipts;
- no automatic promotion, integration-ref advance, or deployment.
This release validates the lifecycle, polyglot worker, OpenCode runtime,
sandbox, Git, resource, benchmark, and evidence contracts without granting
deployment authority. After it succeeds, target openagents.com with an Elixir
capability image and the same worker protocol. The next release can add
human-reviewed SCV candidates. Staging autodeploy should follow only after the
Forge-canonical and isolated-fleet prerequisites pass.
Acceptance criteria for staging autonomy
Do not describe an SCV as autonomous until all of these conditions hold:
- An SCV resumes across coordinator and worker loss without duplicate effects.
- Every candidate descends from and conditionally advances one linear integration history.
- Candidate code runs without production or operator credentials.
- Candidate code cannot read an inference grant or replace the admitted OpenCode runtime.
- Every execution binds an admitted worker image, capability manifest, resource class, and host-observed resource receipt.
- Performance decisions use comparable benchmark definitions and preserve raw samples, including failures.
- Host policy, not model output, determines risk, gates, budgets, and promotion.
- Protected changes cannot approve themselves.
- A push cannot directly create a target.
- Every automatic target carries an admitted SCV principal, policy digest, candidate ID, exact-SHA gate receipt, and decision digest.
- Forge independently builds, classifies, deploys, and receipts the SHA.
- Post-live verification uses predefined evidence and treats missing evidence as failure.
- Automatic rollback promotes and verifies the exact predecessor.
- One rollback opens the circuit and stops further external effects.
- An operator can pause, cancel, reject, and resume an SCV without editing code or restarting the fleet.
- The complete staging failure matrix and soak pass against exact retained receipts.
An SCV that satisfies these criteria can make rapid iterations without making the model, workspace, or running process the source of truth. Git commits, policy decisions, gates, Forge targets, deployment receipts, and verified runtime state remain the authority at every step.