Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0a6e5c0
delegate: a program's ceilings and its report ending are its own, not…
ZeroPoint95 Oct 5, 2026
7730417
security-audit: sec-af, the security auditor, built into codeaf
ZeroPoint95 Oct 5, 2026
662bc88
security-audit: the fixes its first real runs asked for
ZeroPoint95 Oct 5, 2026
057016a
security-audit: titled by what it audits, quieter page, change entry
ZeroPoint95 Oct 5, 2026
6338d7e
sec: security-audit is called sec
ZeroPoint95 Oct 5, 2026
f5568bc
sec: one vocabulary on its page, and a report in the account's words
ZeroPoint95 Oct 5, 2026
d35442a
sec: the guide constant and change entry the rename left unstaged
ZeroPoint95 Oct 5, 2026
ceb0825
sec: its files and SARIF say sec, and its hunt says which hunter foun…
ZeroPoint95 Oct 5, 2026
5e100b2
a program's ending reaches the person even when the reply to it fails
ZeroPoint95 Oct 5, 2026
1e0e372
sec: a run nobody limited gets four hours, not two
ZeroPoint95 Oct 5, 2026
04dd61f
sec: a proposed run reads the words it was asked with, and its row po…
ZeroPoint95 Oct 5, 2026
5bfaa6c
notices: the modules sec brought in
ZeroPoint95 Oct 5, 2026
732545c
config: CODEAF_RECORDS is launch plumbing
ZeroPoint95 Oct 5, 2026
62f603a
changelog: sec's entry carries its pull request's number
ZeroPoint95 Oct 5, 2026
eef127a
sec: the review's fixes — the entry's number, ABSORB's path, a person…
ZeroPoint95 Oct 5, 2026
11d0b3b
agentsession: sec's agent loop moves out of secaf, and names each pro…
ZeroPoint95 Oct 6, 2026
19c219f
sec and agentsession: an ending says sec's name once, and one empty r…
ZeroPoint95 Oct 6, 2026
df4e989
sec: the manual's ceiling endings are the two codeaf prints
ZeroPoint95 Oct 6, 2026
1453508
agentsession and sec: each agent's turns and time are its own
ZeroPoint95 Oct 6, 2026
ab7b005
agentsession: a long line says how to reach its rest, and grep shows …
ZeroPoint95 Oct 6, 2026
bedb052
agentsession: machinery only — every figure is the program's, and an …
ZeroPoint95 Oct 6, 2026
270e8f1
pr: copy pr-af's Go port into internal/praf at its tag codeaf-absorb
ZeroPoint95 Oct 6, 2026
edb9ced
pr: pr-af's code review, carried as /pr and codeaf pr
ZeroPoint95 Oct 6, 2026
4d29975
pr: run on internal/agentsession, told its work is a code review
ZeroPoint95 Oct 6, 2026
1324333
pr: one failed session is one failed reviewer, and endings say their …
ZeroPoint95 Oct 6, 2026
96e8274
pr: a review's sessions take twenty turns, and a killed run's checkou…
ZeroPoint95 Oct 6, 2026
fb31a29
pr: a JSON context file is written one value to a line
ZeroPoint95 Oct 6, 2026
0632d7e
pr: each of a review's agents has its own turns and time
ZeroPoint95 Oct 6, 2026
938b289
pr: a review's failed-call note counts what it counts
ZeroPoint95 Oct 6, 2026
8e867bb
pr: each agent's limits are pr's own, and the manual says so
ZeroPoint95 Oct 6, 2026
75a5fb0
pr: every figure its agent sessions run on is pr's own
ZeroPoint95 Oct 6, 2026
4220ecb
pr: its limit flags' help fits eighty columns
ZeroPoint95 Oct 6, 2026
72b14de
pr: its change entry carries its pull request's number
ZeroPoint95 Oct 6, 2026
81c1113
pr: its change entry's pr field is 1784 too
ZeroPoint95 Oct 6, 2026
5349ac0
pr: a bare post asks for its report, and what the page says is what i…
ZeroPoint95 Oct 6, 2026
46b7dd3
Merge branch 'dev' into zeropoint95/sec
ZeroPoint95 Oct 6, 2026
b59e07c
review: pr is renamed review — /review, codeaf review, via: "review"
ZeroPoint95 Oct 6, 2026
9acd405
agentsession, ABSORB: /pr is called /review
ZeroPoint95 Oct 6, 2026
4144f70
Merge zeropoint95/sec (which merged dev) into zeropoint95/pr
ZeroPoint95 Oct 6, 2026
47b556c
review: heard by its command and its work, not by the word review
ZeroPoint95 Oct 6, 2026
6aec350
Merge remote-tracking branch 'origin/dev' into zeropoint95/sec
ZeroPoint95 Oct 7, 2026
d662c37
Merge dev into zeropoint95/pr
ZeroPoint95 Oct 7, 2026
f8c5fb8
prefix: sec's guide costs both prefixes 396 bytes on today's dev
ZeroPoint95 Oct 7, 2026
4221d0a
Merge zeropoint95/sec (dev baa2d7f0f, sec's re-measured prefix) into …
ZeroPoint95 Oct 7, 2026
ab539e9
Merge dev (4fd9cfaa0) into zeropoint95/pr
ZeroPoint95 Oct 7, 2026
a89aa4e
Merge remote-tracking branch 'origin/dev' into zeropoint95/sec
ZeroPoint95 Oct 7, 2026
62765e2
Merge zeropoint95/sec (a89aa4e14, its merge of dev 4fd9cfaa0) into ze…
ZeroPoint95 Oct 7, 2026
16e937b
Merge dev (741f1b9ee, #1781 squash-merged) into zeropoint95/pr
ZeroPoint95 Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions PERF.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,21 @@ rest is the type and line tables that code carries. The budget rises by exactly
that cost, to 71,363,000 — this change's bill and nothing else. The tree without
it was 1,798,688 under the sixth reset's 68,850,000, and still is.

It was reset an eighth time on 2026-10-06, when review — pr-af, the pull-request
reviewer, copied in once at its tag `codeaf-absorb` — became the third program
built into codeaf (`internal/praf`). Measured the same way, on darwin/amd64 with
its own furrow staged, the same tree with and without the program in the build's
list, on Go 1.27.0:

| platform | without | with | what review cost |
| --- | --- | --- | --- |
| darwin/amd64, furrow staged | 69,630,224 | 70,663,008 | 1,032,784 |

It brings no module codeaf did not already link — its agent sessions are sec's
and its schema readers the ones sec brought — so the cost is pr-af's own code
and prompts and the tables that code carries. The budget rises by exactly that
cost, to 72,395,784.

## Adaptive run shutdown grace

`Agent.Close` cancels adaptive runs and their name calls, then gives all accepted
Expand Down Expand Up @@ -1509,6 +1524,12 @@ both prefixes 396 bytes, so the full cap is **57,614** bytes and the lean cap
**49,986**, both dated in `prefixWaivers`. (Against the dev of 2026-10-05 it cost
the lean prefix only 229, before the page carried a program's guide there too.)

**review, the third program (pr-af's code review), adds its guide (2026-10-06).** review's
guide is 190 bytes and its item in the hand-off paragraph's list 203, the same in
both arms, and the caps rise by exactly that over sec's: measured on dev
`baa2d7f0f` with sec (2026-10-07), the full cap is **57,817** bytes and the lean
cap **50,189**, both dated in `prefixWaivers`.

## Following through on a completion claim

A turn may decline handoff once per request when its own continuation says no
Expand Down
2 changes: 1 addition & 1 deletion SIZE-BUDGET
Original file line number Diff line number Diff line change
@@ -1 +1 @@
71363000
72395784
23 changes: 23 additions & 0 deletions docs/changes/unreleased/1784-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
kind: added
title: review, pr-af's code review built into codeaf, reviews a GitHub pull request and posts on your yes
pr: 1784
surface: [chat, engine, docs, build]
invalidates:
- "sec and senior-dev were the only programs codeaf carries. There are three: review (`/review`, `codeaf review`, `via: \"review\"`) is the third, and it changes no files."
- "pr-af was a separate AgentField node with its own keys and a coding-agent binary per reviewer. It is copied into codeaf at pr-af's tag codeaf-absorb (b70667e) as internal/praf, runs only through codeaf on the shared agent sessions (internal/agentsession), and is frozen in its own repository."
- "A program codeaf carried never posted anything outside the machine. review posts a review it wrote to its pull request, but only as its own run (`/review post <report>`), which the chat proposes after the person says yes."
- "Typing a program's name as a word asked for it. A program whose name is an everyday word (`Delegate.Asked`) is heard by its command and its work instead: `/review` and \"take a look at PR 123\" ask for review, and \"review this function\" does not."
- "The fixed and lean prefix caps were 57,614 and 49,986 bytes and SIZE-BUDGET was 71,363,000. They are 57,817, 50,189 and 72,395,784, raised by exactly what review measured (PERF.md)."
---

`/review <pull request> [focus]` (or a bare `/review` for the current branch's open pull
request) checks the pull request out in a folder of its own, plans the review,
sends a reviewer at each part of the change, checks and challenges what they found
against the code, and hands back every finding, blocking first, with where it is
and a suggested fix. Its review goes to the task's record folder (`review-report.md`,
`review-report.json`) and its account to the conversation, which offers to post it or
to hand the blocking findings to senior-dev. Private repositories use `GH_TOKEN`,
`GITHUB_TOKEN` or `gh auth token`. Every model call goes through the run's model
API, priced and held to its ceiling: $5 and two hours unless the conversation has
less.
78 changes: 78 additions & 0 deletions docs/design/pr-review/ABSORB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Absorbing pr-af into codeaf

The pull-request reviewer was copied once from the pr-af repository and is frozen
there. This page records what was copied, what was left behind, and what was
changed on the way in, so a reader diffing `internal/praf` against pr-af can tell
a deliberate change from drift. sec's `docs/design/security-audit/ABSORB.md` is
the worked example this one follows.

## Source

- Repository: pr-af, Go module `github.com/Agent-Field/pr-af/go`
- Tag `codeaf-absorb`, commit `b70667e`
- Copied on 2026-10-06
- `go/internal/*` became `internal/praf/*`; `go/README.md` is
`PR-AF-GO-README.md` here, unchanged.

## Not copied

- `cmd/` (the HTTP node binary), `Dockerfile`, `docker-entrypoint.sh`,
`agentfield-package.yaml`, `Makefile`, `scripts/`, `test/`.
- `internal/node`: the AgentField node — agent construction from the
environment, control-plane registration, the webhook, the `review` endpoint's
status mapping. The one piece the program needs, the table of 16 router
reasoners, is `internal/praf/handlers.go`.
- `internal/hitl`: the approval loop that paused a review on the control plane
for a person's go-ahead before posting. Inside codeaf no review posts from
the pipeline; posting is its own run, on the person's yes.
- `internal/delegate`, `internal/inproc`, `carried/` and `codeaf/`: pr-af's own
first drafts of a codeaf integration (a host mirror, its own read-only agent
loop, a drop-in adapter). The program here uses codeaf's real
`internal/delegate` types, and its agent sessions are the loop sec runs on
too (`internal/agentsession`), told their work is "a code review".

## Changed

- Imports were rewritten to `github.com/Agent-Field/codeaf/internal/praf/...`.
- The AgentField SDK's `agent` and `harness` packages are no longer imported;
only `sdk/go/ai` remains, at codeaf's pinned version, and no `ai` symbol needed
an adaptation. `internal/praf/appx` declares the review's own seam (Harness,
AI, Note) with `HarnessOptions{Cwd, Label}` and a `HarnessResult` carrying the
fields the review reads. Each reasoner's agent session carries a label
(`reviewer`, `evidence`, `challenge`, …) that names its thread.
- `orch.Orchestrator.Run` runs the pipeline once. Its HITL revision loop, the
`Pause` verb, the hax client seams and `config.HITLConfig` are gone; the
review's hints still reach every reviewer as guidance. `orch.PostReview` and
`orch.PostReviewEvent` post a saved review with the "own pull request" 422
fallback the pipeline's own post used, and `Orchestrator.PRData` hands back the
pull request a review fetched.
- No `PR_AF_*` environment is read. `config/ai.go` (provider selection, the
external CLI binaries, `ProviderEnv`, every `PR_AF_*` model and retry
variable) and `config/harnessbin.go` are deleted. The evidence pack stays on
and the post-worthiness gate off, their shipped defaults. `ResolveBudgetCaps`
takes the caller's caps or its defaults.
- `orch.ResolveRepo` takes an `orch.Access` — the folder to clone into and the
GitHub token — instead of reading `PR_AF_WORKDIR` and `GH_TOKEN`; the five git
timeouts are the shipped defaults; a review with nothing to clone is refused
instead of falling back to `PR_AF_REPO_PATH` or the working directory.
- **The token never reaches the disk.** pr-af wrote `GH_TOKEN` into the clone's
remote URL, which git keeps in `.git/config`; here it is an Authorization
header in one git process's environment (`GIT_CONFIG_COUNT`).
- `github.NewClient` takes the token it is handed and reads no environment.
GitHub App sign-in (`GITHUB_APP_ID`, its private key, `golang-jwt`) is not
carried. `OpenPullRequest` is new: the open pull request from a branch, for a
bare `/review`.
- `LFS` smudging is always skipped (`GIT_LFS_SKIP_SMUDGE=1`): the review reads
code.
- The pipeline's progress prints go to stderr; stdout is the program's record
stream.
- A JSON context file under `.pr-af-context/` is written indented, one value
to a line, where pr-af wrote `json.dumps`' single line. codeaf's sessions
read a file a line at a time and cut a line at 2,000 characters, so a
single-line context of tens of thousands of characters showed its first
2,000. The prompts, and the inline contexts in them, are unchanged.
- Each of a review's agents has turn and time limits of its own
(internal/praf/limits.go): 10 to 30 turns and 5 to 15 minutes, where
pr-af's harness allowed every agent 50 turns. `--max-turns` and
`--session-wall` replace every agent's own for one run. The limits are
review's alone; the shared agent loop holds none of its own.
208 changes: 208 additions & 0 deletions docs/design/pr-review/PR-AF-GO-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# PR-AF — Go node

A Go implementation of the PR-AF agentic code-review node. It registers the same reasoner
surface under the same names as the Python node, exposes a byte-compatible
HTTP API, and reports every pipeline phase as its own tracked execution, so the
control-plane DAG UI renders the same multi-node orchestration graph as the
Python node (see [Pipeline DAG on the control plane](#pipeline-dag-on-the-control-plane)).
The Python package under `src/pr_af/` is untouched; this implementation lives entirely
under `go/`.

One binary:

| Binary | Node ID | Default port | Role |
|----------|------------|--------------|----------------------------------------|
| `pr-af` | `pr-af` | `8007` | Full review pipeline (intake → review) |

Module path: `github.com/Agent-Field/pr-af/go`.

## Install

Installing the bare repository URL follows the root manifest's redirect to this
package and registers the Go node as `pr-af` on `:8007`:

```bash
af install https://github.com/Agent-Field/pr-af
af run pr-af
af call pr-af.review --in '{"pr_url":"https://github.com/owner/repo/pull/123"}'
```

To install the Python node deliberately, clone the repository and run `af
install ./pr-af`; local-path installs do not follow the redirect. `NODE_ID` /
`PORT` still override the Go defaults if you want a different id/port.

## Pipeline DAG on the control plane

The Python node's review is not one execution — every phase
(`intake_phase`, `anatomy_phase`, the three `meta_*` selectors, each parallel
`review_dimension`, `adversary_phase`, `evidence_verifier`, the obligation and
compound reasoners, `coverage_gate`) runs as a **tracked child execution** of
the parent `review`, and the control-plane UI renders the run as that DAG.
Python gets this from its `@router.reasoner()` wrapper, which routes direct
in-process calls through workflow instrumentation.

The Go port reproduces the same graph through the SDK's `Agent.CallLocal`:
the orchestrator's phase seams (`orch.callLocalSeams`) invoke each phase under
its registered reasoner name instead of calling the function directly. Each
call builds a child execution context from the incoming request's context and
emits `running` / `succeeded` / `failed` workflow events to the control plane,
which mirrors them into the executions table — one DAG node per phase, parented
under the `review` execution, same shape as Python.

Mechanics worth knowing:

- **Same code path either way.** A CallLocal-routed phase goes through the
registered handler (`afx.Bind` into the typed input → the same `reasoners.*`
function with the same deps). `afx.ToMap` keeps nested values typed so
custom marshalers (notably `OrderedPatches`' insertion-ordered
`diff_patches`) survive the round trip byte-for-byte.
- **Best-effort reporting.** Workflow events are fire-and-forget: an
unreachable control plane logs a warning and the review proceeds — the DAG
is observability, never a failure mode.
- **Stubs opt out.** `orch.Deps.Local` is nil in unit tests and stub
harnesses, which keeps the plain direct-call seams; production wiring
(`node.BuildAgent`) always points it at the live agent.

## Depending on the AgentField Go SDK

Unlike the SWE-AF Go port, this module depends on the AgentField Go SDK
(`github.com/Agent-Field/agentfield/sdk/go`) via a **real, committed `require`**
resolved from `proxy.golang.org` — there is **no `replace` directive** and no
sibling checkout to lay out. `go build ./...` works out of the box against the
pinned SDK pseudo-version in `go.mod`.

- **CI / Docker.** `go mod download` pulls the SDK (and every other dependency)
straight from the module proxy. No `GOWORK=off`, no sparse clone.
- **Dev — optional Go workspace.** A gitignored `go.work` (spanning this module
and a local `agentfield/sdk/go` checkout) is the way to develop against
unreleased SDK changes; with it present, `go build ./...` picks up local SDK
edits live. It is never committed.

Bumping the SDK is a deliberate, reviewable change: bump the `require`
pseudo-version in `go.mod`, and move the Docker builder image tag / CI
`setup-go` version together if the SDK's own `go` directive ever advances past
`go 1.21`.

## Build & run locally

From `go/`:

```bash
make build # go build ./...
make vet # go vet ./...
make test # go test ./...
make check # vet + test
make fmt # gofmt -w .
make run # run the node (pr-af, :8007)
```

`make run` needs a control plane reachable at `AGENTFIELD_SERVER` (default
`http://localhost:8080`). The node reads all configuration from the environment
at startup.

## Docker

The image is a multi-stage build and is **simpler than SWE-AF's** — because the
SDK is a real proxy-resolved `require`, the builder just runs `go mod download`
(no SDK clone stage). The runtime stage is a slim Debian mirroring the Python
image: the pinned `opencode` CLI (`1.17.15`), a non-root `praf` user, and the
**same** `docker-entrypoint.sh` (a byte copy of the Python one) that generates
`opencode.json` from `PR_AF_MODEL` at container start.

The build context is the **repo root** so the `go/` module is in context:

```bash
# from the repo root
docker build -f go/Dockerfile -t pr-af:latest .
```

### Compose: opt-in add-on to the Python stack

`docker-compose.go.yml` (at the repo root) is an **add-on**, not a standalone
stack. It defines only the Go node and joins the Python stack's compose network
as an external reference, sharing the control plane (`agentfield`) and the
`workspaces` volume the Python stack brings up. The Python `docker-compose.yml`
is left untouched. Start the Python stack first, then layer the Go node:

```bash
docker compose up -d # Python stack (control plane + pr-af :8004)
docker compose -f docker-compose.go.yml up -d # adds pr-af-go :8007
```

Adds:

| Service | Port | Node id | Notes |
|------------|--------|------------|--------------------------|
| `pr-af-go` | `8007` | `pr-af-go` | full review pipeline |

The control plane (`:8080`) and the `workspaces` volume come from the Python
stack — the Go add-on joins them via the external `pr-af_default` network and
`pr-af_pr-af-workspaces` volume. This assumes the Python stack was brought up
with the default project name `pr-af` (the Python compose has no explicit
`name:`, so its project name is the checkout directory's basename); see the
compose file header for the `COMPOSE_PROJECT_NAME` override. Health:
`curl -f http://localhost:8007/health`.

## Environment variables

The node is configured entirely through the environment.

| Variable | Purpose |
|-----------------------------|----------------------------------------------------------------|
| `OPENROUTER_API_KEY` | LLM provider key (OpenRouter) — required |
| `GH_TOKEN` | GitHub token (`repo` scope) for reading PRs and posting reviews |
| `AGENTFIELD_SERVER` | Control-plane URL (default `http://localhost:8080`) |
| `AGENTFIELD_API_KEY` | Control-plane API key (if the CP has auth enabled) |
| `NODE_ID` | Node ID (default `pr-af`) |
| `PORT` | Listen port (default `8007`) |
| `PR_AF_PROVIDER` | Harness provider (default `aforge`; use `opencode` to roll back) |
| `AGENTFIELD_AFORGE_COMMAND` | AForge headless command — `exec` (default) or `do` |
| `PR_AF_MODEL` | Harness model (default `deepseek/deepseek-v4-flash-0731`) |
| `PR_AF_LABEL` | Pull-request label that triggers a webhook review (default `pr-af`) |
| `PR_AF_MAX_CONCURRENT_REVIEWERS` | Optional webhook review concurrency cap (minimum `1`) |
| `PR_AF_MAX_REVIEW_DEPTH` | Optional webhook sub-review depth cap (minimum `0`) |
| `PR_AF_MAX_COVERAGE_ITERATIONS` | Optional webhook coverage iteration cap (minimum `1`) |
| `PR_AF_HARNESS_BIN` | Optional harness executable override for every provider (point it at an `aforge` binary outside `PATH`); unset uses provider defaults |
| `PR_AF_MAX_COST_USD` | Per-run cost ceiling in USD (default `2.0`) |
| `PR_AF_MAX_DURATION_SECONDS`| Per-run wall-clock ceiling in seconds (default `3600`) |
| `AGENTFIELD_HARNESS_IDLE_SECONDS` | Harness no-output watchdog window in seconds (default `360`) — harness CLIs in JSON mode emit events only at completion boundaries, so long single completions look silent |
| `HAX_API_KEY` | Optional — enables the HITL review-approval gate when set |

The image ships the released AForge CLI (fetched and checksum-verified at build
time from `https://agentfield.ai/downloads/aforge`, pinned by the
`AFORGE_VERSION` build arg) and runs `exec` by default. OpenCode remains
installed and can be selected with `PR_AF_PROVIDER=opencode` without rebuilding.

Note: the code default model is `minimax/minimax-m2.5`, while the Docker image /
compose / manifest set `PR_AF_MODEL=deepseek/deepseek-v4-flash-0731`. The env
var always wins; both defaults are intentional (they mirror the Python node).

## Deployment: `af install`

Because the root package redirects git installs here, install the Go node with
the repository's bare URL:

```bash
af install https://github.com/Agent-Field/pr-af
```

This resolves to `go/agentfield-package.yaml` (node id `pr-af`, default port
`8007`) and builds `./cmd/pr-af`. A prior Python `pr-af` installation is
replaced in place, retaining its node id, triggers, and node-scoped secrets.

## `/pr-af` in codeaf

PR-AF is also a program codeaf carries, the way it carries `/senior-dev`:
compiled into codeaf, run as codeaf's own child process, and reaching models
only through the API codeaf serves each run. pr-af's half lives here:

| | |
|---|---|
| [`carried`](carried/carried.go) | The public package codeaf imports: name, summary, guide, stages and their words, the `review` command and its body, crew flags, and the task page's reader, in types that mirror codeaf's `internal/delegate`. [`carried/guide`](carried/guide/guide.go) holds the guide in a package with no imports, for codeaf's prefix budget. |
| [`internal/delegate`](internal/delegate) | The body: brief parsing (`owner/repo#N`, a URL, or `#N` against the folder's GitHub origin), the served node's pipeline with the router reasoners reported as stages and steps, the review rendered as Markdown for the terminal record's deliverable, and the ceilings kept with the pipeline's budget gate at 80%. Always a dry run. |
| [`internal/inproc`](internal/inproc) | The reviewers' coding agent when carried: a read-only tool loop (read, list, glob, grep, read-only git) inside the process, on the run's model API, one thread per reviewer, so every call is metered and no grandchild needs a key. |
| [`codeaf/`](codeaf/README.md) | codeaf's half: the adapter package, the guide, the manual page and tests, and `apply.sh`, which carries pr-af in a codeaf checkout. |

The served node is unchanged by any of this. One change applies to it: with
the `aforge` provider and no `aforge` binary on `PATH`, the harness runs
`codeaf` (the binary's new name).
Loading
Loading