[ENV-B1] Implement supervised native Python Docker isolated-workspace backend
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 22/100
調査の方向性
Read EPIC #266 and accepted ADRs 0002, 0004, 0005, 0006, and 0008, then inspect sandbox.py, executor*.py, providers.py, and agent_worktree.py. Start with the hard dependencies #268–#270 and the Python executor's existing provider launch paths. Done means the fake CLI and gated real Docker checks cover lifecycle, isolation, commit export, failure handling, and cleanup, with documentation and capability status updated.
索引モデルが issue の本文から書いたものです。
説明
Artifact: implementation-ready engineering issue. Parent EPIC: #266. Workstream: B. Size: XL. Baseline: main 711ff09 (2026-10-21). No implementation is authorized by this planning action.
Objective / scoped deliverable
Implement Docker runtime using Python's existing executor, typed argv, validated image reference, explicit container create/start/exec/stop/remove lifecycle and stream supervision. Stage minimal Git input, run existing Claude/OpenCode/Pi providers against isolated cwd, export verified commits and clear temp materials; do not create PRs, merge or create host branches. Support diagnostic image/runtime checks; no Node bridge, no Sandcastle dependency. Scope first feature to REMOTE only and refuse unsupported launch classes instead of silent fallback.
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: #268 (ENV-A2)
- Hard: #269 (ENV-A3)
- Hard: #270 (ENV-A4)
- Hard: #290 (ENV-B0 provider-specific credential projection; Claude Code CLI, OpenCode 2.x and Pi)
- 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_docker.py (new), sandbox.py, executor*.py, providers.py, agent_worktree.py, tests
Acceptance criteria
- Fake Docker CLI test covers all lifecycle steps, streaming, cancellation, timeout, nonzero and partial-start cleanup
- Run/issue scoped container names and labels prevent cross-worker cleanup; name collision and permission-denied fail safely
- Real Docker smoke: isolated repo gets precise base, agent-produced commit exported and imported with same SHA
- All REMOTE provider launch classes either honor backend policy or refuse with a precise diagnosis
- No Docker daemon/socket access in agent and no leaking secret in command argv, inspect, logs or state
- 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.
Provider scope clarification (2026-10-22)
- Docker MVP runs Claude Code CLI, OpenCode 2.x CLI, Pi RPC using #290's credential plan; Codex and Codex OAuth are out of scope.
- The Docker integration must not create container-wide model token metadata or silently fall back to operator HOME or unsandboxed credentials.
Docker mount and execution contract (2026-10-23)
- Hard: #292 (ENV-A5 host snapshot / container-private workspace).
- Docker run maps only controller-approved read-only
/source, private per-issue/run writable/workspaceand #290's narrow model-auth projection; it never bind-mounts host HOME, a live writable checkout, common Git admin or gh credentials. - Claude Code/OpenCode 2.x/Pi and all repo-originating build/test commands operate from
/workspace; never run them on host as a fallback. Stage an exact base tree and minimal private Git objects using #269/#292. - Host Controller retains all GitHub
ghAPI operations and hardened hostgitimport/push; container may commit privately but cannot publish. Proof includes no write to host snapshot/main checkout.
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.
Native dependencies (verified read-back 2026-10-23): blocked_by #268, #269, #270, #290, #292. #292 no longer needs this issue to finish its own acceptance: its Docker-side criteria moved here (below), so the old cycle is gone.
Image contract (previously unowned; owned here)
- Where the agent CLIs come from (owner decision 2026-10-23, #267 decision 7).
- AutoForge installs Claude Code, OpenCode 2.x and Pi into the image at pinned versions, together with any MCP servers and Skills the operator configures.
- That configuration comes from the operator only, under strict keys, never from the repository or an agent.
- Versions are detected inside the container. A host installation is never bind-mounted, and no repository-controlled path is mounted.
- Installing an MCP server or Skill does not enable it. Today's policy keeps them off for every launch, and enabling them in a Docker launch waits for #267's policy amendment.
- Who builds it, and how it is pinned: AutoForge builds it (owner decision 2026-10-23, #267 decision 7).
- The build runs inside a build container, never on the host.
- It starts from an operator-configured base image pinned by digest (
name@sha256:…). A tag-only base is shown as unpinned and is refused undersandbox.mode: requiredunless #267 decides otherwise. - The built image is identified by its digest and cached under a content key: the base digest, the CLI versions, and the MCP/Skills configuration. An unchanged configuration reuses it, and a change rebuilds it (least resources).
- The effective digest is recorded per launch for diagnostics (#273).
- Project toolchains. Compilers and test runners the repository needs come from the image. A missing toolchain fails closed inside the container. It never falls back to the host (#292 criterion 3).
- User. Agents and commands run as a non-root UID/GID inside the container, using #292's ownership data. That Claude Code refuses or changes behaviour when run as root is an assumption. It must be verified against the installed CLI and recorded, not taken from memory.
- Upgrades and cache. Replacing the image is an operator action. A run records the digest it used, and a later step under another digest is reported, with handling per #273's pin rule. Caches stay private to one run, unless #278 later decides otherwise.
- No configuration hole. There is no raw
docker runflag, mount,--privileged,--cap-add,--pid host,--network hostor device surface, whether from the operator configuration, the repository, or an agent. The plan is built from typed fields only. - No credential in the image. No layer, build argument or label carries a credential (#290).
EXPORT_COMMAND under the Docker backend (previously unowned; owned here)
REMOTE validation (src/autoforge/engine.py:5841) and pre-merge verification commands (engine.py:9592) run the candidate's own code under LaunchClass.EXPORT_COMMAND. Under the Docker backend each one either:
- runs in an isolated container over the verified, materialized tree, using
materialize()and its proof as today (ADR 0006 D6.3/D6.4), with the network from #270's table; or - is refused before any work starts, with a precise diagnostic.
It never runs on the host. Host gh and the controller's hardened git stay on the host and are not commands of this class.
Runtime identity detection (moved here from #275)
- Do not trust the executable's basename. On the reviewing host,
/usr/bin/dockeris Fedora'spodman-dockershell wrapper (exec /usr/bin/podman "$@"), anddocker versionreportsClient: Podman Engine, version 5.8.7, withdocker inforeporting rootlesstrue. - The probe reports the engine (Docker Engine or Podman), its version, rootless or rootful, and the API version, from the runtime's own structured output.
- Under
sandbox.mode: required, an unverified or incompatible runtime is refused before anything of the step is saved or launched. Podman behind adockername is diagnosed as Podman. It is never treated as Docker Engine. Rootless Podman becomes a supported runtime through #275 (moved into the MVP 2026-10-23), which builds on this backend. - Shared-filesystem probe (#267 decision 12). The probe also proves whether the runtime shares the controller host's filesystem: a nonce file in the controller-owned staging root, bound read-only and read back from inside the container. The result decides whether
/sourceis a read-only bind or whether only the workspace seed is copied in. An operator may force copy-in, never a bind. Doctor reports the result (#273).
Wiring split with #269 and #274
- #269 defines the import semantics, the manifest, the bounds and the candidate-source abstraction, and tests them with fakes.
- This issue wires them: the container's export of a bounded pack and manifest to the controller-owned export location, #292's staged
/sourceand/workspaceseed, and the abstraction's imported-artifact source. - #274 proves the whole path on real Docker.
Acceptance criteria received from #292 (ownership transfer, 2026-10-23)
Quoted verbatim from #292. They are unchanged in substance.
- (#292 criterion 1)
/sourceis mounted read-only from the controller-prepared host snapshot;/workspaceis private and writable; a deliberate write to/sourcefails and does not mutate host bytes. (Real-Docker proof also in #274.) - (#292 criterion 2, second half) Preparing two independent sandboxes from the same base SHA yields … independent writable workspaces …
- (#292 criterion 5) Rootless/userns and non-root launch permissions checked; no raw user-provided host mount paths or repo-controlled Docker run flags accepted. Unsupported Docker features fail closed in
sandbox.mode: required. (Selection and diagnostics shared with #273.) - (#292 criterion 9, real half) Fake and real-Docker tests verify read-only source and writable container workspace; record skips as untested, never as success.
- (moved from #292's implied Docker-runtime scope) Real UID/GID mapping and SELinux mount-label compatibility for
/sourceand/workspaceon real Docker. Rootless Podman's mount compatibility is proven in #275 (moved into the MVP 2026-10-23). The relabel policy is decided with #270 and never relabels a path outside the controller-owned staging root.
Not selectable from this issue alone
When this issue merges, the backend still cannot be selected for real runs. That happens only after #272 (lifecycle journal, cleanup and reconciliation) and #273 (selection, pin and doctor), which are natively blocked by this issue. A Docker backend whose process-launch tests pass is not thereby an enforced backend.
Additional acceptance criteria (planning reconciliation)
- AutoForge builds the image in a build container from a digest-pinned base, caches it by content key, and installs the pinned CLIs plus the operator-configured MCP servers and Skills; installed integrations stay disabled until #267's policy amendment allows them
- Image contract implemented: in-container version detection for each provider; a digest recorded per launch; a non-root UID/GID; no raw flag or mount surface (tests try each forbidden flag)
- Claude Code's behaviour as root versus non-root verified against the installed CLI and recorded in the PR
-
EXPORT_COMMANDruns only in a container over the verified materialized tree, or is refused before work starts; a test proves no candidate command is spawned on the host under the Docker backend - Runtime identity probe distinguishes Docker Engine from Podman behind a
dockername, and rootless from rootful; underrequired, an unverified runtime is refused before anything is saved or launched - The shared-filesystem probe decides bind vs copy-in for
/source(#267 decision 12); a runtime whose daemon resolves paths elsewhere (DOCKER_HOST, anotherdocker context, a VM) is never given a bind, and forced copy-in works on a bind-capable runtime
- 主要言語
- Python
- スター
- 0
- フォーク
- 0
- 平均マージ
- 5時間 42分
- マージ済み PR(30日)
- 64
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
iceboundrock/AutoForge のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
iceboundrock/AutoForge#304 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 1/100
iceboundrock/AutoForge#303 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 54/100
iceboundrock/AutoForge#302 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
iceboundrock/AutoForge#301 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
iceboundrock/AutoForge#295 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
iceboundrock/AutoForge の issue をすべて見る
似ている issue
-
Project submission:オープン
難易度 1/5 1時間未満 初心者へのやさしさ 72/100
slavakurilyak/awesome-ai-agents#845 ·
メンテナーはふだん 1 日以内に返信
-
tests: pin that the atomic writers in utils/paths.py stage the temporary file in the target's directory対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンdifficulty:2 enhancement good first issue
難易度 1/5 1〜3時間 初心者へのやさしさ 92/100
MakazhanAlpamys/Soup#2178 ·
メンテナーはふだん 1 日以内に返信
-
[BUG] dcg.waggr permutation p-values are folded twice, so near-zero scores get the smallest p-valuesオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
scverse/rapids-singlecell#854 ·
メンテナーはふだん 1 日以内に返信
-
Slang input: int8_t4_packed and uint8_t4_packed are copied into CrossGL対応中かも @ninglinLiu が今日担当しました。 オープンarea:frontend backend:slang bug direction:input
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
random_elements(unique=True) on an OrderedDict: ZeroDivisionError once only zero weights remain, AssertionError with use_weighting=False対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
メンテナーはふだん 13 日以内に返信