[ENV-A2] Introduce pluggable sandbox backend seam without changing Bubblewrap behavior
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- python
- Domain
- backend, infrastructure, testing
Research direction
Start with the accepted ADRs 0002, 0004, 0005, 0006 and 0008, then read #267 and the existing sandbox and executor boundaries in src/autoforge/sandbox.py, sandbox_bwrap.py, executor.py, executor_duplex.py, executor_supervised.py, and doctor_sandbox.py. Use the tests and offline fake executable/provider fixtures to trace backend selection, launch, streams, cancellation, and denial behavior. Done means all acceptance criteria hold, including unchanged bwrap behavior, no unsafe fallback, and parity across single-issue and scheduler launches; attach focused negative and crash-window tests or an auditable design-only test plan.
Written by the indexing model from the issue text.
Description
Artifact: implementation-ready engineering issue. Parent EPIC: #266. Workstream: A. Size: L. Baseline: main 711ff09 (2026-10-21). No implementation is authorized by this planning action.
Objective / scoped deliverable
Extract narrowly typed backend selection/probe/launch lifecycle at the existing executor boundary while preserving bwrap as the default implementation. Keep the process supervisor responsible for termination, output, streams, timeouts and containment; no second agent orchestration stack. Distinguish unsupported, denied policy, setup failure, command failure and uncertain external lifecycle. No direct shell command strings or provider-dependent state transitions.
Existing foundations and boundaries
Read EPIC #266 and relevant accepted ADRs 0002, 0004, 0005, 0006, 0008. The deterministic controller retains every workflow transition, Git/GitHub write, read-back, ReviewCertificate and merge authorization. Do not embed Sandcastle's run(), replace the Python agent providers or introduce an arbitrary privileged mount. Preserve the Linux bwrap baseline. Coordinate session/interaction overlaps with #147/#126. If state schema must change, obtain protocol-number coordination on #23 before the migration.
Dependencies
- Hard: #267 (ENV-A1)
- This issue must not be considered complete by filing a follow-up for a core acceptance criterion; split decisions before implementation only if the EPIC ledger is updated.
Relevant implementation surfaces (starting points, not an exhaustive mandate)
src/autoforge/sandbox.py, sandbox_bwrap.py, executor.py, executor_duplex.py, executor_supervised.py, doctor_sandbox.py, tests
Acceptance criteria
- Existing bwrap fixtures and real-backend semantics remain unchanged including required/preferred/disabled
- Fake sandbox backend proves invocation, exact argv/stdin/env handling, streaming, cancellation and denial path
- Prelaunch capability refusal leaves state and attempt counters untouched where current contract requires
- Backend lifecycle errors never become success or permit automatic unsandboxed fallback
- Parities across single-issue and scheduler worker launch, with no change to controller authority
- Existing regression suite passes; focused negative and crash-window tests are attached to the PR, or a design-only issue includes an auditable test plan.
- Update documentation and backend capability/rollout status when user-facing behavior changes; do not represent a skipped live test as a pass.
Exclusions / anti-goals
Do not weaken sandbox.mode: required, allow agent-held GitHub write credentials, expose host .git common-dir/state to isolated agents, silently apply raw REMOTE diffs, or invent a second lifecycle controller. EPIC #255 Public Alpha is not blocked by this task.
Verification guidance
Use offline fake executable/provider fixtures first, then explicitly gated disposable-repository real-backend tests when applicable. Record exact candidate/base SHAs and externally observed failures, ensure secure redaction, and demonstrate idempotent cleanup and resume.
Planning reconciliation (2026-10-23)
Artifact: planning update from the 2026-10-23 reconciliation of EPIC #266 with its architecture review (#266 comment). Baseline re-read: main 388ecb3. Planning only: nothing here implements a feature or checks off a criterion. The earlier sections stay authoritative where this one does not change them.
Why the seam needs a full lifecycle
On 388ecb3 the sandbox seam is hard-wired to bwrap. Each of these call sites must go through the new backend seam:
src/autoforge/executor.py:97importsBwrapLaunch;_spawn_sandboxed(executor.py:1322-1356) and_prepare_sandbox(executor.py:1359-1377) callsandbox_bwrap.prepare;src/autoforge/executor_duplex.py:105,503,583usesBwrapLaunchfor duplex launches;src/autoforge/engine.py:1260-1269probes throughsandbox_bwrap.probe;src/autoforge/doctor_sandbox.py:42usessandbox_bwrap.ProbeResult.
A bwrap launch is one process whose death ends the sandbox. A container is not. So the seam must model a lifecycle, not only "prepare a command".
Backend-neutral lifecycle (the seam's contract)
- Capability probe. Prove capability with a real minimal launch and report the backend's identity (engine, version, rootless or rootful for containers), with no side effects outside a temporary area. Dry run derives a pure, inspectable launch plan from the existing
LaunchDescription(src/autoforge/sandbox.py:222) and the launch class (sandbox_policy.py:99-105) and stops before step 2. - Prepare resources. Allocate the isolated environment under run-scoped names: a container and its volume, or nothing for bwrap.
- Stage source and credentials. Present #292's staged source snapshot and workspace seed, and #290's credential projection, by role.
- Start and supervise execution. Start the command. Apply timeouts, stop, cancellation and liveness through a channel that is not the agent's stdin.
- Capture streams and agent result. Use the executor's existing stdin, stdout and stderr handling, including the duplex streams, then collect the exit status and the backend's teardown facts.
- Export candidate/artifacts. Hand the produced candidate artifact to the controller-owned export location. For bwrap, the candidate is still the worktree
HEAD(ADR 0005 D6.3), and nothing is exported. - Verify and import through Controller. Pass the artifact to #269's strict, bounded import and candidate-source abstraction. For bwrap this is today's strict import of the private object directory (ADR 0005 D6.5). The backend never imports or publishes anything itself.
- Stop and remove resources. Run idempotently on every exit path. Report uncertain cleanup as uncertain, never as done.
- Reconcile after interruption. Give #272's journal and reconciliation the hooks it needs to find and remove this run's leftovers by ownership label after a crash. For bwrap this is a no-op.
Steps 1 to 3 can refuse the launch before anything is saved, charged or spawned, following today's setup-failure rule (ADR 0005 D10.8). A failure at a later step is reported as a typed failure and never becomes success or an unsandboxed fallback.
Requirements added by this reconciliation
- Preserve executor streaming, timeouts, cancellation, Pi RPC duplex stdin and OpenCode 2.x stdin exactly as today. A liveness mechanism for a container backend must not read from, write to, or close the agent's stdin.
- Backend identity becomes part of
SandboxFacts(sandbox.py:382) and of every diagnostic that says a launch was enforced, following ADR 0005 D1.2 as amended by #267. For the container backend, the identity includes the detected runtime: Docker Engine or Podman (#267 decision 8, 2026-10-23). Today'sTEARDOWN_UNCOUNTEDfact (sandbox.py:374) stays bwrap's. A container backend reports its own teardown fact and never borrows "removed by namespace teardown". - The seam stays reusable by a later SDK helper launch (#147's providers run through the same executor). This issue does not implement or change #147.
- This issue lands with bwrap and a fake backend only. It adds no Docker code (#271) and makes no backend selectable (#273).
Additional acceptance criteria (planning reconciliation)
- Every listed bwrap call site goes through the seam; a test fails if engine, executor or doctor imports
sandbox_bwrapdirectly for a launch or probe - The fake backend exercises all nine lifecycle steps (probe, prepare, stage, start and supervise, capture, export, verify and import, stop and remove, reconcile), including a refusal at each step and a cleanup that reports "uncertain"
- Pi RPC duplex and OpenCode 2.x stdin tests pass unchanged through the seam; a test proves the liveness hook never touches the agent's stdin
-
SandboxFactscarries backend identity; existing bwrap facts and doctor rows are unchanged in content
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- Avg merge
- 5h 57m
- Merged PRs (30d)
- 71
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from iceboundrock/AutoForge
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
iceboundrock/AutoForge#304 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
iceboundrock/AutoForge#333 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
iceboundrock/AutoForge#332 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 1/100
iceboundrock/AutoForge#331 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 45/100
iceboundrock/AutoForge#329 · 1 comment ·
Maintainers usually reply within 1 day
All issues in iceboundrock/AutoForge
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
scikit-learn-contrib/category_encoders#553 ·
Maintainers usually reply within 1 day
-
tests/strategies_test.py:174 goes uncovered on about 1 run in 100, failing the coverage ratchetOpenflaky-watch
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
btclib-org/btclib#2839 ·
Maintainers usually reply within 1 day
-
BarrelList constructor drops false-valued iterable contentsPossibly taken @Gambit-Checkmate claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 19 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
vllm-project/vllm#63863 ·
Maintainers usually reply within 1 day