feat(harness): make aforge the default provider across Python, Go, and TypeScript - #905
Merged
Conversation
Contributor
Performance
⚠ Regression detected:
|
Contributor
📊 Coverage gateThresholds from
✅ Gate passedNo surface regressed past the allowed threshold and the aggregate stayed above the floor. |
Contributor
📐 Patch coverage gateThreshold: 80% on lines this PR touches vs
✅ Patch gate passedEvery surface whose lines were touched by this PR has patch coverage at or above the threshold. |
This was referenced Aug 10, 2026
AbirAbbas
added a commit
to Agent-Field/contract-af
that referenced
this pull request
Aug 17, 2026
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>
AbirAbbas
force-pushed
the
agent/aforge-do-harness
branch
from
August 17, 2026 15:29
bfd3442 to
d4be7bf
Compare
Contributor
Author
|
recheck |
`app.harness("...")` with nothing configured now runs AForge, AgentField's
native coding harness, instead of raising "No harness provider specified".
Provider precedence is explicit value > AGENTFIELD_HARNESS_PROVIDER >
"aforge", implemented once in harness/_defaults.py and applied both where
HarnessConfig materialises its default and where the runner resolves options
(so a runner built without any config follows the same chain).
HarnessConfig.model stops defaulting to "sonnet" — that was Claude-specific
and wrong for every other provider. The default is now empty, meaning "use
the provider's own default", and the claude-code provider carries "sonnet"
internally so explicit claude-code users see no change.
Also drops the private-repo build instruction from the aforge install hint;
the binary ships alongside `af` and `af aforge ensure` (re)installs it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Runner.Run no longer errors on an empty Options.Provider. BuildProvider and
Run both route the name through ResolveProviderName, which applies the same
precedence as the Python SDK: explicit value > AGENTFIELD_HARNESS_PROVIDER >
DefaultProvider ("aforge"). The resolved name is written back onto the
options so error messages and provider construction see the real provider.
Model stays empty by default and means "the provider's own default"; the
Harness doc example no longer suggests a Claude-specific "sonnet".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewrites the harness-providers lead so the zero-setup path is the headline: AForge is the default, `af aforge ensure` installs it alongside `af`, and picking Claude Code / Codex / Gemini CLI / OpenCode is an override of one option rather than a prerequisite. Documents the provider precedence chain (explicit > AGENTFIELD_HARNESS_PROVIDER > aforge) and that an unset model means the provider's own default. Drops the "go build -o aforge ./cmd/aforge" instruction — that repo is not public and is no longer how anyone gets the binary. Adds the grok row (Python SDK only) so the install table matches the supported provider set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The design doc still asserted that HarnessConfig.provider has no implicit default and that a call without one raises. Both are false now: provider resolves through explicit > AGENTFIELD_HARNESS_PROVIDER > "aforge", and model defaults to the provider's own rather than "sonnet". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HarnessRunner.run no longer throws when nothing sets a provider. Both the
runner and buildProvider route the name through resolveProviderName, which
applies the same precedence as the Python and Go SDKs: explicit value >
AGENTFIELD_HARNESS_PROVIDER > DEFAULT_HARNESS_PROVIDER ("aforge"). The
resolved name is written back onto the options so providers and error
messages see the real provider.
HarnessConfig.provider becomes optional to match, so `{}` is a complete
config.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Go and TypeScript adapters told anyone hitting a missing binary to "Build it from https://github.com/Agent-Field/aforge-v2" — a repo they cannot open. AForge ships with `af`, so the message now names `af aforge ensure` and the AFORGE_BIN escape hatch, matching the Python install hint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AbirAbbas
force-pushed
the
agent/aforge-do-harness
branch
from
August 17, 2026 18:04
d4be7bf to
dbb19b2
Compare
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
Makes AForge AgentField's default coding harness across all three SDKs, and adds the
AForge provider itself (Python, Go, TypeScript) with the
aforge exec --jsoncontract.A harness call with nothing configured now works:
Previously each of those raised "No harness provider specified". Picking Claude Code,
Codex, Gemini CLI, or OpenCode is now an override rather than a prerequisite — same
loop code, different worker.
Why
The harness was the one AgentField primitive that could not be used out of the box: every
call needed a provider name plus a third-party CLI installed and authenticated first.
AForge ships alongside
af, so the default path costs the user one env var(
OPENROUTER_API_KEY) and nothing else. Orchestrating someone else's harness stays afirst-class, documented story — it just stops being the price of admission.
The
modeldefault moved for the same reason. It was"sonnet", a Claude-specific idthat was silently wrong for every other provider. It is now empty, meaning "the provider's
own default";
claude-codecarriessonnetinternally so explicit claude-code users seeno change.
Provider precedence
.harness(provider=…),HarnessConfig(provider=…),Options.Provider,{ provider }AGENTFIELD_HARNESS_PROVIDERenv varaforgeImplemented once per SDK and applied at both the config-materialisation and the
runner-resolution points, so a runner built with no config at all follows the same chain.
Validation contract
Behaviours this PR must exhibit, in caller-observable terms:
aforge— it does not raise.AGENTFIELD_HARNESS_PROVIDER=<name>set and no explicit provider, that provider runs.through to the next source.
HarnessConfigbuilt with no arguments is a complete, valid config in all three SDKs.claude-codewith no model still sendssonnet, so no existing claude-code caller changes behaviour.aforge exec --json -w <root> --timeout <outer-5s> --context-fill 60 --completion-reserve 65536, delivers the task on stdin, and never putsthe prompt on argv.
AGENTFIELD_AFORGE_COMMAND=doselects the routed workflow instead.AFORGE_BINorPATHand exec it directly —no download, no unpacking, no build step.
Every item above has a test. Existing tests were changed only where the documented contract
changed (the three "provider is required" assertions and the two
model == "sonnet"assertions); each was rewritten to assert the new behaviour rather than deleted.
Verification
Gates run locally, matching
.github/workflows/sdk-python.yml,sdk-go.yml,sdk-typescript.ymland the sdk surfaces ofcoverage.yml:sdk-python.ymllintruff check .with the pinnedruff==0.15.22sdk-python.ymltests./scripts/run_pytest.sh(full suite)sdk-go.ymltidygo mod tidy+git diff --exit-code go.mod go.sumsdk-go.ymlbuildgo build ./...coverage.yml(sdk-go)go test ./... -count=1ok(agent, ai, client, did, harness, inputs, triggers, types)sdk-typescript.ymlinstallnpm cion Node 20sdk-typescript.ymllintnpm run lint(tsc --noEmit)npm run build(tsup + dts)sdk-typescript.ymltestsnpm testgofmt -l ./harness ./agentmaintoo)readme-links.ymlpython3 scripts/check-utm-links.pyManual confirmation of items 8 and 9 of the contract: the
--timeout/--context-fill/--completion-reserveflags are present in all three adapters(
sdk/python/agentfield/harness/providers/aforge.py,sdk/go/harness/aforge.go,sdk/typescript/src/harness/providers/aforge.ts), and none of the three contains anyarchive/decompression path — each resolves
AFORGE_BINorPATHand execs.CI
Green on this head: Python SDK CI (3.10 / 3.11 / 3.12 + both websockets matrices), Go SDK CI,
TypeScript SDK CI, Coverage Summary (all five surfaces + the gate), Functional Tests
(local + postgres), Performance Check, README Links, CodeQL for actions/go/python.
CodeQL / Analyze (javascript-typescript)is red for an unrelated reason: it died inSet up jobduring a GitHub Actions incident that also took out five other jobs on this PR(
codeloadreturning 429/503 while downloadingactions/setup-go, plus GraphQL 503s). Everyother affected job passed on re-run; that one is a Default-Setup code-scanning run, which the
API refuses to retry, so it will clear on the next push to this branch.
Still draft — un-draft conditions
harness binary alongside af". Until it does,
aforgeis not on a user'sPATHand thenew default would fail for anyone who has not built it themselves. That PR also adds
af aforge ensure, which this PR's docs and the Python install hint now point at.aforgerow toaf harness doctor— this PR deliberately doesnot touch
control-plane/internal/cli/harness_doctor.goto keep that change conflict-free.Relationship to #913 (Pi and OMP harnesses)
#913 currently lands "OMP as the default". That conflicts with the product decision here:
aforge is the default, and pi/omp are optional providers alongside claude-code, codex,
gemini, and opencode. #913 should drop its default-provider change and keep the two new
providers; the precedence chain in this PR (explicit >
AGENTFIELD_HARNESS_PROVIDER>aforge) is where a user selects them.Whichever of the two rebases second owns these 24 overlapping files:
The three hot spots are the provider-name registry (
SUPPORTED_PROVIDERS/ResolveProviderName/ the factory switch), the runner's provider-resolution line, and theprovider table in
docs/harness-providers.md. Everything else is additive on both sides.Follow-ups
grok, and eventuallypi/omp, rows to the Go and TypeScript factories — todaythey are Python-only, which the docs now state explicitly.
af harness doctorequivalents for the Go and TypeScript SDKs (Harness provider availability: install extras +harness doctorpreflight + standardized missing-binary errors #685) still do not exist;the preflight remains Python + CLI only.
skills/agentfield/SKILL.md(and its skillkit copy) still lists the harness providers asclaude-code|codex|gemini|opencodeand gatesapp.harness()behind "is a CLI installed".Left untouched here to keep the Add Pi and OMP harnesses with OMP as the default #913 conflict surface small — it wants a pass of its own
once the default lands.
feat: make AForge exec the default harness sec-af#129, feat: make AForge exec the default harness pr-af#72, feat: make AForge exec the default harness cloudsecurity-af#7,
feat: make AForge exec the default harness contract-af#3.
🤖 Generated with Claude Code