Skip to content

feat: make AForge exec the default harness - #3

Merged
AbirAbbas merged 8 commits into
mainfrom
agent/aforge-exec-default
Aug 18, 2026
Merged

feat: make AForge exec the default harness#3
AbirAbbas merged 8 commits into
mainfrom
agent/aforge-exec-default

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Status update — agentfield v0.1.130 is released; this PR is ready

Everything below the line was written against the 0.1.129 pin and is kept for history. What is true on the current head:

  • Pins bumped: agentfield>=0.1.130 in pyproject.toml and the Dockerfile; .env.example no longer calls AGENTFIELD_AFORGE_COMMAND reserved (0.1.130 reads it: exec default, do opt-in; --timeout/--turns are passed).
  • Live verification (real OpenRouter key, default provider): image built with default args from agentfield.ai + PyPI (aforge v0.1.0, agentfield 0.1.130); node registered on a fresh agentfield/control-plane:latest; contract-af.anatomy_phase (the repo's only direct app.harness() path) on a 7-clause NDA returned 16 sections / 5 defined terms / 4 cross-refs and flagged the one-sided indemnification as critical — 3 runs, ~30 s each, cents; live argv aforge exec --json -w … --timeout 1795 --turns 50 --context-fill 60 --completion-reserve 65536 --model … --plan-model …. Code default with CONTRACT_AF_PROVIDER/HARNESS_PROVIDER unset = aforge; overrides flip it. The new agentfield-package.yaml parses under af show-requirements v0.1.130. Tests: same failure set as main (+4 new passing), incl. the pre-existing tests/test_review_layer.py import error.
  • Un-draft preconditions met (site live, 0.1.130 on PyPI).

What / why

Makes AForge exec the default Contract-AF coding harness, and makes the container able to
get 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:

ARG AFORGE_BASE_URL=https://agentfield.ai/downloads/aforge
ARG AFORGE_VERSION=v0.1.0

The aforge stage downloads ${AFORGE_BASE_URL}/${AFORGE_VERSION}/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 chmod +x. Both ARGs are
overridable, 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 reach openrouter.ai over HTTPS.

It also drops the agentfield git-SHA pin in favour of the PyPI release that actually ships the
Python AForge provider.

Changes

  • DockerfileARG AFORGE_IMAGE / FROM ${AFORGE_IMAGE} AS aforge / COPY --from=aforge /aforge
    replaced 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 + Dockerfileagentfield @ git+…@bfd34426…#subdirectory=sdk/pythonagentfield>=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 the
    user-environment contract (OPENROUTER_API_KEY required; server/model/harness overrides optional).
    dependencies.system names the AForge CLI so af install prints the manual step up front instead
    of failing on the first harness call — af install builds a Python venv, it does not fetch harness CLIs.
  • README.md / .env.example — corrected. The old text said AGENTFIELD_AFORGE_COMMAND=do
    selects AForge orchestration; that is not true on any released SDK (see below).
  • tests/test_config.py — two more behaviours pinned: aforge_bin override precedence and
    provider-key forwarding.

SDK pin: what works today, what is inert

agentfield==0.1.129 (PyPI, current latest) ships agentfield/harness/providers/aforge.py and
HarnessConfig.aforge_bin, so the default path in this PR is fully live:

  • HARNESS_PROVIDER=aforgebuild_provider returns AforgeProvider, which runs
    aforge exec --json -w <project_dir>.
  • CONTRACT_AF_AFORGE_BIN / AFORGE_BIN → read by contract_af.config and passed as
    HarnessConfig.aforge_bin, which 0.1.129 honours. These work today.

Two caveats, both to revisit when Agent-Field/agentfield#905 is released:

  1. AGENTFIELD_AFORGE_COMMAND is a no-op. It is forwarded into the harness subprocess
    environment, 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 a
    switch yet. It is kept (in Dockerfile, docker-compose.yml, .env.example) so the behaviour
    is already declarative once #905 lands.
  2. 0.1.129's provider passes no --timeout, so AForge's own 15-minute wall applies. The SDK's
    outer subprocess timeout is AGENTFIELD_HARNESS_TIMEOUT_SECONDS (default 1800s), which is
    longer — 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/go pseudo-version to carry.

Validation contract

Observable behaviours this PR must exhibit:

  1. With no harness env set, Contract-AF resolves provider aforge and binary aforge.
  2. HARNESS_PROVIDER=opencode rolls back to OpenCode, which stays installed in the image.
  3. CONTRACT_AF_AFORGE_BIN beats AFORGE_BIN beats the default aforge.
  4. The harness subprocess environment carries OPENROUTER_API_KEY.
  5. A default docker build produces an image containing an executable /usr/local/bin/aforge
    whose sha256 equals the published checksum for its architecture.
  6. A tampered or missing asset fails the build rather than shipping an unverified binary.
  7. Inside the image, aforge exec --json returns a JSON envelope on stdout and exits 0.
  8. Inside the image, the runtime environment resolves to a real AforgeProvider bound 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 check on the files this PR touches — clean.
  • docker build — contract item 5; the build's sha256sum -c - step prints aforge: OK.
  • Negative test: a build against a directory whose checksums.txt was altered fails at the
    verify step (aforge: FAILED), contract item 6.
  • docker run --rm --entrypoint aforge <img> --help and a real one-shot
    aforge exec --json -w /tmp/w --timeout 120 --budget 20000 --turns 3 inside the container —
    contract item 7.
  • In-container provider resolution check via agentfield.harness.providers._factory.build_provider
    using the image's own baked environment — contract item 8.

Known pre-existing failures (not introduced here, not fixed here)

Reproduced on origin/main with the same interpreter and the same agentfield version:

  • tests/test_review_layer.py fails to collect: it imports CLAUSE_ALIASES and
    EXPECTED_CLAUSES from contract_af.agents.gap_analyst, which no longer defines either —
    gap_analyst now derives expected clauses from a prompt. The module needs rewriting against the
    current behaviour, not a re-export.
  • tests/test_clause_analyst.py::test_depth_escalation_any_critical
  • tests/test_clause_analyst.py::TestGetSectionTexts::test_extracts_known_section
  • tests/test_intake.py::test_uses_first_3000_chars
  • ruff check . reports 46 findings repo-wide (benchmarks, src/, tests/) — all on main.

Follow-ups

  • Bump the two agentfield>=0.1.129 lines listed above once agentfield#905 is released, and turn
    AGENTFIELD_AFORGE_COMMAND into a documented switch at the same time.
  • Repair the four pre-existing test failures and the test_review_layer.py collection error, and
    decide whether ruff check . should be a gate (this repo has no CI at all).
  • Publish a checksum/signature for the manifest-declared AForge system dependency path used by
    af install, so a local (non-Docker) install gets the same verification the image does.

Still a draft

This stays in draft until both:

  1. https://agentfield.ai/downloads/aforge/v0.1.0/… is live — the default
    docker build (no --build-arg) must succeed against the public URL. The host
    already serves the older build-<sha> coordinate; v0.1.0 is still a 404 there.
  2. feat(harness): make aforge the default provider across Python, Go, and TypeScript agentfield#905 is released to PyPI and the two pin lines above are bumped to it,
    so AGENTFIELD_AFORGE_COMMAND stops being inert.

license/cla is 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

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

AbirAbbas and others added 7 commits August 17, 2026 11:22
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>
@AbirAbbas
AbirAbbas marked this pull request as ready for review August 18, 2026 02:28
@AbirAbbas
AbirAbbas merged commit a93e44e into main Aug 18, 2026
1 check was pending
@AbirAbbas
AbirAbbas deleted the agent/aforge-exec-default branch August 18, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants