diff --git a/docs/adr/0002-lean-inner-image-per-project-extension.md b/docs/adr/0002-lean-inner-image-per-project-extension.md index 156d562..b56e1ad 100644 --- a/docs/adr/0002-lean-inner-image-per-project-extension.md +++ b/docs/adr/0002-lean-inner-image-per-project-extension.md @@ -9,3 +9,29 @@ We deliberately do **not** reuse agentic.dev's outer Dockerfile for inner sandbo - Trivial JS/TS projects need no custom inner Dockerfile — the sandcastle default suffices. - Toolchain-heavy projects own a `.sandcastle/Dockerfile`; the per-language `devcontainer-*` images become reusable bases for agent environments, keeping human-dev and agent toolchains in parity. - Inner images stay lean — no nested docker, no orchestration tooling. + +## Addendum (2026-07-06): `devcontainer-*` derivation guidance is stale + +The original guidance above — derive a toolchain-heavy project's inner Dockerfile +`FROM` the matching sibling `devcontainer-*` image in this workspace +(rust/python/dotnet/vue) — was never executed. No inner Dockerfile in this repo +derives from one, and none of those sibling repos has ever grown a +`.sandcastle/` directory. On reflection it's also the wrong default: those +images are heavy VS-Code-devcontainer bases (e.g. +`mcr.microsoft.com/vscode/devcontainers/dotnet:8.0`), built for human editors +with debuggers/extensions, not minimal agent sandboxes — and requiring a +sibling repo to exist and stay in sync is an unnecessary coupling. + +**Superseded by:** derive per-language `.sandcastle/Dockerfile.` variants +from the lean official upstream SDK image for that language instead (e.g. +`mcr.microsoft.com/dotnet/sdk:8.0`, `rust:1-slim`, `python:3.12-slim`), +matching the existing `Dockerfile`/`Dockerfile.opencode` pattern: install +`git` + `curl`, create/rename the non-root `agent` user with matching UID/GID +build-args, `curl -fsSL https://claude.ai/install.sh | bash` (the native +installer — confirmed Node-independent, so it works unmodified on a non-Node +base image), no nested Docker or orchestration tooling. + +See `docs/adr/0024-sandcastle-is-not-a-language-constraint.md` for why this +stays a Dockerfile-only concern rather than a sandcastle engine swap. No +per-language Dockerfile has been built yet under either the original or +corrected guidance — that remains separate follow-up work. diff --git a/docs/adr/0024-sandcastle-is-not-a-language-constraint.md b/docs/adr/0024-sandcastle-is-not-a-language-constraint.md new file mode 100644 index 0000000..2a30468 --- /dev/null +++ b/docs/adr/0024-sandcastle-is-not-a-language-constraint.md @@ -0,0 +1,74 @@ +# Sandcastle is not a language constraint — no engine change for multi-language support + +agentic-dev's `/afk`/`/hitl` orchestration works well for TypeScript/JS target +projects today. Extending it to other target languages (dotnet, rust) raised +the question of whether the underlying agent-sandbox engine — `@ai-hero/sandcastle`, +the npm package `.sandcastle/main.ts` is built on — should be replaced or +complemented with something that abstracts more naturally across languages, +rather than hand-writing a Dockerfile per language. + +## Evidence: sandcastle has no language opinion + +`.sandcastle/sandbox-runner.ts` (the sandcastle adapter) calls +`createSandbox({ branch, sandbox: docker({ imageName, ... }), cwd, ... })` +(`sandbox-runner.ts:253`) and `sandbox.run({ agent, maxIterations, +completionSignal, ... })` (`sandbox-runner.ts:286,298`). `.sandcastle/reviewer-adapter.ts` +calls sandcastle's top-level `run({ agent, sandbox: noSandbox(), ... })` +(`reviewer-adapter.ts:222,231`) with `Output.object({ tag: "output", schema })` +for structured extraction (`reviewer-adapter.ts:258,266,274,283`). Every +argument across both call sites is a Docker image name, a git branch/worktree +path, an agent-provider config, or CLI-shaped run options — nothing in +sandcastle's API takes or infers a target-project language. + +Sandcastle's own published README (`@ai-hero/sandcastle@0.12.0`, matching this +repo's pinned `.sandcastle/package.json` dependency) describes itself as +"provider-agnostic" (Docker/Podman/Vercel/custom sandboxes) and lists the +scaffolded `Dockerfile` as "Sandbox environment (customize as needed)" in its +project-layout diagram. There is no mention of language, runtime, or toolchain +anywhere in the README — the Dockerfile is explicitly the customization point. + +This repo's own `.sandcastle/Dockerfile` already documents the same intent in +its own comment: *"Add a project FROM/derived image if a project needs more +toolchain (ADR-0002)"* (`Dockerfile:7-8`). Only `Dockerfile` (Node) and +`Dockerfile.opencode` exist today — no `Dockerfile.rust`/`.dotnet`/`.python` +variant has ever been added. `docs/adr/0002-lean-inner-image-per-project-extension.md` +already named this exact shape (per-language Dockerfiles) as the intended +extension point; it was simply never executed. + +## Alternatives considered and rejected + +- **OpenHands** (Python) ships its own opinionated GitHub-issue-resolver and + Docker-sandboxing loop. Adopting it as the engine would mean throwing away + this repo's already-built retry semantics (#76), review gate (ADR-0020), + crash recovery (ADR-0021), and label-driven state machine, and rewriting the + outer orchestrator in Python to get back roughly what already exists. +- **OpenAgentsControl** (built on opencode, advertises multi-language support) + is a context-adaptation layer, not a sandbox engine — it doesn't manage + Docker containers or git worktrees at all, so it doesn't do sandcastle's + actual job here regardless of its language claims. + +## Decision + +Keep sandcastle as the sandbox engine. The multi-language gap is not an engine +problem — it's that per-language `.sandcastle/Dockerfile.` variants, +already anticipated by ADR-0002, were never built. Solve it there, not by +swapping the engine. + +## Consequences + +- No code changes result from this ADR — it's a decision record, not an + implementation. +- ADR-0002's guidance on *how* to derive a per-language Dockerfile (`FROM` a + sibling `devcontainer-*` image in this workspace) is separately stale and + corrected by a dated addendum on that ADR (see its 2026-07-06 addendum). +- Building actual per-language Dockerfile variants (dotnet, rust, etc.) + remains unbuilt and is tracked as separate follow-up work, not part of this + decision. + +## Relations + +- ADR-0002: names the per-language Dockerfile extension point this ADR + confirms is the right layer for multi-language support; its derivation + guidance is corrected by a same-day addendum. +- ADR-0019: the `createSandbox`/`sandbox.run()` lifecycle cited as evidence + here is the same high-level API adopted in that ADR.