feat: make AForge exec the default harness - #3
Merged
Conversation
|
|
The aforge stage pulled `ghcr.io/agent-field/aforge-v2:chat-v2-exec`. That
image is not publicly pullable, so `docker compose up --build` — the quick
start this repo advertises — could not work outside the org.
Replace it with an HTTPS fetch of the published release asset. The stage
downloads `aforge-linux-${TARGETARCH}.gz`, gunzips it, and verifies the
decompressed binary against `checksums.txt` from the same directory (the
release checksums hash the uncompressed binaries) before making it
executable. A bad or missing asset fails the build instead of shipping an
unverified binary.
AFORGE_BASE_URL and AFORGE_VERSION stay overridable so CI or a mirror can
serve the same layout elsewhere. The stage reuses python:3.11-slim rather
than adding a second base image to the build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the git-SHA pin. agentfield 0.1.129 is on PyPI and already ships the Python aforge provider (harness/providers/aforge.py, which runs `aforge exec --json -w <root>`) and HarnessConfig.aforge_bin, so the default path this repo now takes is served by a real release. It also means the image build no longer needs a git checkout to install the SDK. Two things stay inert until Agent-Field/agentfield#905 is released: AGENTFIELD_AFORGE_COMMAND is forwarded to the harness subprocess but read by nobody, and the 0.1.129 provider passes no --timeout, so aforge's own 15-minute wall applies. Bump both pins to the #905 release when it exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contract-AF was the only Agent-Field product node without one, so `af install https://github.com/Agent-Field/contract-af` had nothing to read. Follows the pr-af / SWE-AF shape: config_version v1, entrypoint, node id and default port, and the user-environment contract (OPENROUTER_API_KEY required; control-plane, model and harness overrides optional). dependencies.system names the aforge CLI because `af install` builds a Python venv and does not fetch harness binaries — the installer prints it as a manual step, which is better than failing on the first harness call. The Docker image, which does fetch it, is unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The README claimed `AGENTFIELD_AFORGE_COMMAND=do` selects AForge orchestration. No released agentfield SDK reads that variable — 0.1.129's provider always runs `aforge exec` — so the documented switch did nothing. Say what is actually true today: aforge exec is the default, OpenCode is the configuration-only rollback, and CONTRACT_AF_AFORGE_BIN overrides which binary runs (that one is honoured, via HarnessConfig.aforge_bin). Keep AGENTFIELD_AFORGE_COMMAND set but label it reserved until agentfield#905. Also record where the container gets aforge from, since that is now a fetch-and-verify rather than an image pull. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two behaviours the config exposes but nothing covered: that CONTRACT_AF_AFORGE_BIN beats AFORGE_BIN beats the default, and that the harness subprocess environment carries the OpenRouter key aforge needs to reach the API. Both are what a user hits first when pointing the harness at a non-PATH binary or debugging an auth failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
aforge-v2 cut its first semver release (v0.1.0), so the AFORGE_VERSION default moves off the build-<sha> coordinate onto the tag. Bumping the string is what busts the fetch layer's cache, so this is what actually pulls the released binary instead of restoring the stale one. The Dockerfile ARG is the only place this repo writes the coordinate — the compose file and README defer to it. The AgentField SDK pin is deliberately left alone; it bumps on its own release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0.1.130 is the release that ships the aforge exec provider path, so the runtime and build pins move up together. AGENTFIELD_AFORGE_COMMAND is a real switch in this release (it selects `aforge exec` vs `aforge do`), so the .env.example note that called it reserved is no longer accurate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / why
Makes AForge
execthe default Contract-AF coding harness, and makes the container able toget AForge without a private registry.
The original draft pulled the binary out of
ghcr.io/agent-field/aforge-v2:chat-v2-exec.That image is not publicly pullable (the source repo is private), so
docker compose up --build— the repo's advertised quick start — could not work for anyone outside the org. This replaces
it with a plain HTTPS fetch of the published release asset plus checksum verification:
The
aforgestage downloads${AFORGE_BASE_URL}/${AFORGE_VERSION}/aforge-linux-${TARGETARCH}.gz,gunzips it, and verifies the decompressed binary against
checksums.txtfrom the samedirectory (the release checksums hash the uncompressed binaries) before
chmod +x. Both ARGs areoverridable, so CI or a local mirror can serve the same layout from somewhere else. The runtime
stage already installs
ca-certificates, which AForge needs to reachopenrouter.aiover HTTPS.It also drops the
agentfieldgit-SHA pin in favour of the PyPI release that actually ships thePython AForge provider.
Changes
Dockerfile—ARG AFORGE_IMAGE/FROM ${AFORGE_IMAGE} AS aforge/COPY --from=aforge /aforgereplaced by a checksum-verified fetch stage;
COPY --from=aforge /out/aforge /usr/local/bin/aforge.The fetch stage reuses
python:3.11-slim(debian bookworm) so the build does not pull a second base image.pyproject.toml+Dockerfile—agentfield @ git+…@bfd34426…#subdirectory=sdk/python→agentfield>=0.1.129.agentfield-package.yaml(new) — Contract-AF was the only AF product without an install manifest.Mirrors the pr-af / SWE-AF shape:
config_version: v1, entrypoint, node id/port, and theuser-environment contract (
OPENROUTER_API_KEYrequired; server/model/harness overrides optional).dependencies.systemnames the AForge CLI soaf installprints the manual step up front insteadof failing on the first harness call —
af installbuilds a Python venv, it does not fetch harness CLIs.README.md/.env.example— corrected. The old text saidAGENTFIELD_AFORGE_COMMAND=doselects AForge orchestration; that is not true on any released SDK (see below).
tests/test_config.py— two more behaviours pinned:aforge_binoverride precedence andprovider-key forwarding.
SDK pin: what works today, what is inert
agentfield==0.1.129(PyPI, current latest) shipsagentfield/harness/providers/aforge.pyandHarnessConfig.aforge_bin, so the default path in this PR is fully live:HARNESS_PROVIDER=aforge→build_providerreturnsAforgeProvider, which runsaforge exec --json -w <project_dir>.CONTRACT_AF_AFORGE_BIN/AFORGE_BIN→ read bycontract_af.configand passed asHarnessConfig.aforge_bin, which 0.1.129 honours. These work today.Two caveats, both to revisit when Agent-Field/agentfield#905 is released:
AGENTFIELD_AFORGE_COMMANDis a no-op. It is forwarded into the harness subprocessenvironment, but neither 0.1.129 nor the AForge binary reads it — the provider always runs
aforge exec. That happens to be the mode this PR wants, so it is harmless, but it is not aswitch yet. It is kept (in
Dockerfile,docker-compose.yml,.env.example) so the behaviouris already declarative once #905 lands.
--timeout, so AForge's own 15-minute wall applies. The SDK'souter subprocess timeout is
AGENTFIELD_HARNESS_TIMEOUT_SECONDS(default 1800s), which islonger — AForge self-terminates first, so there is no orphan process, but a long harness call
cannot currently be given a bigger budget from Contract-AF.
Exact lines to bump when #905 ships a release:
pyproject.toml:11—"agentfield>=0.1.129",Dockerfile:54—"agentfield>=0.1.129" \There is no Go node in this repo, so there is no
sdk/gopseudo-version to carry.Validation contract
Observable behaviours this PR must exhibit:
aforgeand binaryaforge.HARNESS_PROVIDER=opencoderolls back to OpenCode, which stays installed in the image.CONTRACT_AF_AFORGE_BINbeatsAFORGE_BINbeats the defaultaforge.OPENROUTER_API_KEY.docker buildproduces an image containing an executable/usr/local/bin/aforgewhose sha256 equals the published checksum for its architecture.
aforge exec --jsonreturns a JSON envelope on stdout and exits 0.AforgeProviderbound to/usr/local/bin/aforge.How verified
This repo has no CI, so the gate is local. Everything below was run against a mirror serving the
exact published layout (
/downloads/aforge/<version>/…), pointed at with--build-arg AFORGE_BASE_URL=…; the default value is the public URL.pytest -q— contract items 1-4. See "Known pre-existing failures" below for the baseline.ruff checkon the files this PR touches — clean.docker build— contract item 5; the build'ssha256sum -c -step printsaforge: OK.checksums.txtwas altered fails at theverify step (
aforge: FAILED), contract item 6.docker run --rm --entrypoint aforge <img> --helpand a real one-shotaforge exec --json -w /tmp/w --timeout 120 --budget 20000 --turns 3inside the container —contract item 7.
agentfield.harness.providers._factory.build_providerusing the image's own baked environment — contract item 8.
Known pre-existing failures (not introduced here, not fixed here)
Reproduced on
origin/mainwith the same interpreter and the sameagentfieldversion:tests/test_review_layer.pyfails to collect: it importsCLAUSE_ALIASESandEXPECTED_CLAUSESfromcontract_af.agents.gap_analyst, which no longer defines either —gap_analystnow derives expected clauses from a prompt. The module needs rewriting against thecurrent behaviour, not a re-export.
tests/test_clause_analyst.py::test_depth_escalation_any_criticaltests/test_clause_analyst.py::TestGetSectionTexts::test_extracts_known_sectiontests/test_intake.py::test_uses_first_3000_charsruff check .reports 46 findings repo-wide (benchmarks,src/,tests/) — all onmain.Follow-ups
agentfield>=0.1.129lines listed above once agentfield#905 is released, and turnAGENTFIELD_AFORGE_COMMANDinto a documented switch at the same time.test_review_layer.pycollection error, anddecide whether
ruff check .should be a gate (this repo has no CI at all).af install, so a local (non-Docker) install gets the same verification the image does.Still a draft
This stays in draft until both:
https://agentfield.ai/downloads/aforge/v0.1.0/…is live — the defaultdocker build(no--build-arg) must succeed against the public URL. The hostalready serves the older
build-<sha>coordinate; v0.1.0 is still a 404 there.so
AGENTFIELD_AFORGE_COMMANDstops being inert.license/clais pending because the CLA has not been signed for this repo by the commit author;that needs the author to sign at cla-assistant, it cannot be resolved from here.
🤖 Generated with Claude Code