Skip to content

Build the agentic role runner, and put the implementer through it #215

Description

@thedancingdeveloper

Rewritten 2026-08-06 under the reframing in #195. Was "the agent loop is not reachable from the harness"; that is still true and is now the first half of a larger job.

First, and blocking

Nothing else in #195 can be built until this exists, and nothing built so far can be used without it.

$ grep -rn "minisweagent" src/agent_harness/*.py | grep -v adapters/
$

run chooses between SessionExecutor and Executor. The loop is neither. Every successful loop run to date went through a standalone script in a scratch directory, so the queue, gates, audit, attempt record, budgets and reviewer have never seen a loop-executed item.

What to build

One agentic role runner, not five bespoke paths. Given:

  • a role (routed by ModelClient, so chains, retry ladder, spend caps and recorded answers all apply unchanged),
  • a task,
  • an environment — the item's worktree, read-only or writable, screened by CommandGuard,
  • a termination condition and bounds,

it runs a bounded loop and returns a result. adapters/minisweagent.py already supplies the loop and both protocol implementations; this is the harness-side caller that does not exist.

Then use it for the implementer, and select it from run.

The seam

The loop replaces exactly one step and must not fork the pipeline:

claim -> worktree -> [ implement ] -> checks -> review -> commit -> PR

Direct mode's implement step is one call returning edit blocks, rendered to a diff, applied. The loop's is many turns in the worktree, leaving changes in the tree. Everything either side is identical.

So the loop's changes should arrive as git diff against the worktree's base and enter the existing _from_diff, keeping the patch record, checks, reviewer, commit and attempt log exactly as they are. A second copy of that sequence is a second place for the gates to drift, which _from_diff was factored to prevent.

Note #216: the worktree must be synced to the item's base before anything reads it. That bug — the diff computed against the previous item's branch — is fixed for direct mode, and the loop must not reintroduce it.

Two decisions I will not guess at

1. Who runs the checks? The loop is told the check command and will run it as feedback; the harness then runs it as the gate. Running both is honest and pays twice. Running only the harness's removes the feedback that makes a loop work. My view is both, because feedback and a gate are not the same thing — but it is a real cost decision and should be recorded.

2. How does a per-item budget bound a loop? budgets.py bounds an item's wall-clock and spend; step_limit bounds turns. #217 made the adapter able to map them, and was explicit that this is enforceable, not yet enforced, because nothing calls build(). The executor checks budgets at boundaries and a loop's boundary is the whole loop, so a long loop can exceed an item budget without anything noticing.

Acceptance

  • agent-harness run can select the loop, and an item is delivered through it with the queue, gates, audit and reviewer all involved.
  • The event stream for a loop-executed item is legible: which turns ran, what was refused, what it cost. ~30 calls attributed to one item, and pricing correct across them.
  • A per-item budget stops a loop that overruns it.
  • End-to-end tests in the mock pattern — scripted model, real git repository, real shell — covering delivery, refusal, budget exhaustion and a loop that never terminates.

Blind spots

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dispatchQueue, claims, worker supervision, admission controlrisk:highFailure here stalls the fleet or corrupts measurementtype:taskUnit of implementation work

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions