`clawmates-runtime` shipped with `gcc` and `make` but no `cmake`, no `g++` and no `python3-dev`. Measured on clawhdf5, three probes: no cmake → "is `cmake` not installed?" exit 101 after 13s no python3-dev → "cannot find -lpython3.11" exit 101 at link with both → cargo test PASSES exit 0 after 69s This is not only the delivery gate. The AGENTS run in this image, so a coding phase was writing Rust it had no way to compile or test — which reframes the last run's 11 agent commits as unverifiable by construction. `images/agent-toolchain/Dockerfile` (the microVM path) has had `cmake build-essential` all along, and its own header warns about precisely this: "if `cargo` is present in one image and absent in another, the same mission passes or fails depending on which backend it landed on, and nothing says why." Both images now install the same set — it was missing `python3-dev` too. `images/runtime-toolchain.Dockerfile` is a thin local overlay so the laptop can run today without recompiling zeroclaw from the fork; it is meant to be deleted once a runtime image built from the corrected deploy/ Dockerfile is published. Also: a build failure is no longer reported as a red suite. Both are cargo exit 101, and `verify_tests` mapped every non-zero to `Failed(code)` — so a missing toolchain was recorded as the USER's tests failing. It now returns `CouldNotRun` with the reason when the output shows a compile or link failure. Deliberately narrow: a failing `assert!` still reads as red, because letting broken code past `on_green_tests` is the expensive direction to be wrong in. Both directions are pinned by tests built from today's two real samples. And the coding phase finally has a loop: `research_and_code.toml` declared `loop = "until_no_more_int_items"`, which `phase_config.rs` lists as DECLARED_BUT_UNREAD. Iteration is driven by `max_iterations` + `done_when`, and with `max_iterations = 1` and no `done_when` the phase ran ONCE and was never judged — reporting `completed` whatever it produced. Now 3 passes against a stated goal, wording per the measured rule (say what the tree must CONTAIN). Co-Authored-By: Claude Opus 5 <[email protected]>
144 lines
7.5 KiB
Docker
144 lines
7.5 KiB
Docker
# Slim Clawmates runtime = the zeroclaw daemon (gateway API), API-only.
|
|
# Simple full-source build (avoids the upstream Dockerfile's stale manifest-
|
|
# prefetch step). web/dist may be empty → dashboard is omitted, /api/* works.
|
|
# Requires BuildKit (cache mounts). Build context = the zeroclaw source tree.
|
|
# syntax=docker/dockerfile:1
|
|
|
|
# bookworm-pinned so the binary's glibc matches the bookworm runtime stage
|
|
FROM rust:1.96-slim-bookworm AS build
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
pkg-config build-essential cmake libssl-dev ca-certificates git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
WORKDIR /app
|
|
COPY . .
|
|
# Ensure the gateway's include_dir!("../../web/dist") target exists at compile time.
|
|
RUN mkdir -p web/dist
|
|
RUN --mount=type=cache,id=cm-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
|
|
--mount=type=cache,id=cm-cargo-git,target=/usr/local/cargo/git,sharing=locked \
|
|
--mount=type=cache,id=cm-target-bookworm,target=/app/target,sharing=locked \
|
|
cargo build --release --locked --bin zeroclaw \
|
|
&& cp target/release/zeroclaw /usr/local/bin/zeroclaw
|
|
|
|
FROM debian:bookworm-slim
|
|
# ca-certificates for TLS; Node 22 (Kimi Code needs >=22.19; Claude Code is fine
|
|
# on it too) to install BOTH agent CLIs that the subprocess providers spawn:
|
|
# `claude -p` (claude_cli, Claude subscription) and `kimi -p` (kimi_cli, Kimi
|
|
# membership) — subscription-backed, no per-minute API TPM ceiling.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gnupg git \
|
|
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
|
&& apt-get install -y --no-install-recommends nodejs \
|
|
&& npm install -g @anthropic-ai/claude-code @moonshot-ai/kimi-code \
|
|
&& npm cache clean --force \
|
|
&& rm -rf /var/lib/apt/lists/* /root/.npm
|
|
|
|
# Upstream Gitea SDLC tooling for team-scoped agents that operate on
|
|
# git.redclaw.dev. `tea` covers shell-level ops (git remote helpers,
|
|
# clone/push, PR checkout); `gitea-mcp` gives the LLM a structured
|
|
# MCP tool surface (create_pr, list_repo_issues, create_file, etc.).
|
|
# Both are official upstream binaries — no custom bundle to maintain.
|
|
ARG TEA_VERSION=0.14.2
|
|
ARG GITEA_MCP_VERSION=1.3.0
|
|
RUN set -eux; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in \
|
|
amd64) mcp_asset="Linux_x86_64" ;; \
|
|
arm64) mcp_asset="Linux_arm64" ;; \
|
|
*) echo "unsupported arch: $arch"; exit 1 ;; \
|
|
esac; \
|
|
curl -fsSL "https://dl.gitea.com/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-${arch}" \
|
|
-o /usr/local/bin/tea && chmod +x /usr/local/bin/tea; \
|
|
tmp="$(mktemp -d)" && \
|
|
curl -fsSL "https://gitea.com/gitea/gitea-mcp/releases/download/v${GITEA_MCP_VERSION}/gitea-mcp_${mcp_asset}.tar.gz" \
|
|
-o "$tmp/gitea-mcp.tgz" && \
|
|
tar -xzf "$tmp/gitea-mcp.tgz" -C "$tmp" && \
|
|
install -m 0755 "$tmp/gitea-mcp" /usr/local/bin/gitea-mcp && \
|
|
rm -rf "$tmp"; \
|
|
/usr/local/bin/tea --version | head -1; \
|
|
/usr/local/bin/gitea-mcp --version 2>&1 | head -1 || true
|
|
|
|
# ── Mission toolchain ────────────────────────────────────────────────
|
|
# Agents and the phase evaluator both run project checks inside this image:
|
|
# `templates/teams/rust_sdlc.toml` tells the coder to run `cargo test`, the
|
|
# `done_when` evaluator runs the project's own suite to verify a claim rather
|
|
# than believe it, and `security_scan.rs` shells out to four scanners.
|
|
#
|
|
# None of it was here. A Rust mission's `cargo build` failed, and every
|
|
# security scan produced four `<tool>:tool_error` task rows instead of
|
|
# findings — a scan that scanned nothing and reported cleanly.
|
|
#
|
|
# Measured cost on top of the 864 MB base: scanners +350 MB, Rust +1.23 GB,
|
|
# semgrep +680 MB. This image is NOT in `AGENT_IMAGES`, so it never ships to
|
|
# fleet nodes — only gw-04 holds it, against 112 GB free. The real cost is a
|
|
# slower `docker save | load` on each runtime rebuild, which is worth paying
|
|
# for missions that can actually compile and test what they write.
|
|
#
|
|
# Ordered cheapest-and-most-stable first so a version bump lower down doesn't
|
|
# invalidate the expensive layers above it.
|
|
ARG GITLEAKS_VERSION=8.30.1
|
|
ARG TRIVY_VERSION=0.72.0
|
|
RUN set -eux; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in \
|
|
amd64) gl_arch=x64; tv_arch=64bit ;; \
|
|
arm64) gl_arch=arm64; tv_arch=ARM64 ;; \
|
|
*) echo "unsupported arch: $arch"; exit 1 ;; \
|
|
esac; \
|
|
curl -fsSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_${gl_arch}.tar.gz" \
|
|
| tar -xz -C /usr/local/bin gitleaks; \
|
|
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-${tv_arch}.tar.gz" \
|
|
| tar -xz -C /usr/local/bin trivy; \
|
|
gitleaks version; trivy --version | head -1
|
|
|
|
# semgrep in its own venv so its pinned dependency tree can never collide with
|
|
# anything else installed here.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
python3 python3-pip python3-venv \
|
|
&& python3 -m venv /opt/semgrep \
|
|
&& /opt/semgrep/bin/pip install --no-cache-dir semgrep \
|
|
&& ln -s /opt/semgrep/bin/semgrep /usr/local/bin/semgrep \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& semgrep --version
|
|
|
|
# Rust last: the largest layer and the one most likely to be bumped, so it
|
|
# sits where a rebuild costs the least cache.
|
|
ENV RUSTUP_HOME=/usr/local/rustup \
|
|
CARGO_HOME=/usr/local/cargo \
|
|
PATH=/usr/local/cargo/bin:$PATH
|
|
# `cmake` and `build-essential` (for `g++`) are NOT optional here, even though
|
|
# the builder stage above already has them: a mission compiles the USER's repo
|
|
# in this stage, and a great many Rust crates drive a C/C++ build from their
|
|
# build script. `clawhdf5` is one — `libz-ng-sys` shells out to cmake, so
|
|
# `cargo test` died in 13 seconds with "is `cmake` not installed?" and exit 101.
|
|
#
|
|
# That exit is read as `TestOutcome::Failed(101)` — a RED SUITE — so the
|
|
# `on_green_tests` gate reported the repo's tests as failing when in truth they
|
|
# never compiled. A missing toolchain must not be indistinguishable from broken
|
|
# code.
|
|
#
|
|
# `python3-dev` is here for the same reason one layer down: `clawhdf5-py` is a
|
|
# pyo3 crate, so the link step wants `-lpython3.11` and fails with
|
|
# "cannot find -lpython3.11" without the dev package. `python3` alone ships no
|
|
# shared library to link against.
|
|
#
|
|
# `images/agent-toolchain/Dockerfile` (the microVM path) has installed
|
|
# `cmake build-essential` all along. Its own header warns about exactly this
|
|
# divergence: "if `cargo` is present in one image and absent in another, the
|
|
# same mission passes or fails depending on which backend it landed on, and
|
|
# nothing says why." This is that, one package down.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
gcc libc6-dev pkg-config libssl-dev make cmake build-essential python3-dev \
|
|
&& curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable \
|
|
&& cargo install cargo-audit --locked --no-default-features \
|
|
&& rm -rf /var/lib/apt/lists/* "$CARGO_HOME/registry" "$CARGO_HOME/git" \
|
|
&& chmod -R a+rX "$RUSTUP_HOME" "$CARGO_HOME" \
|
|
&& rustc --version && cargo audit --version
|
|
|
|
COPY --from=build /usr/local/bin/zeroclaw /usr/local/bin/zeroclaw
|
|
ENV HOME=/zeroclaw-data \
|
|
ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace \
|
|
ZEROCLAW_GATEWAY_PORT=42617
|
|
RUN mkdir -p /zeroclaw-data/workspace
|
|
EXPOSE 42617
|
|
ENTRYPOINT ["zeroclaw"]
|
|
CMD ["daemon"]
|