FLT-2 · run_agent result contract (canonical schema)¶
Status: v1 — frozen 2026-07-14. Two implementations follow this document and must stay byte-compatible on the wire:
| Implementation | Language | Where |
|---|---|---|
| fleet-harness wrappers | Python | src/fleet_harness/contract.py (this repo) |
fleet-dispatch run-agent |
C# | fleet-dispatch repo (separate session, in flight) |
Changing a field means changing both implementations and bumping the
version note here in the same change. The Python wrappers are shaped so
they can later delegate to fleet-dispatch run-agent as a subprocess —
identical shapes are what make that swap a one-line change.
Shape¶
Every agent invocation — any vendor CLI, local Ollama, or the future C# host — resolves to exactly one JSON object:
{
"status": "completed",
"summary": "Added null-guard to PathFor and a regression test.",
"changed_files": ["src/Store/PathFor.cs", "tests/PathForTests.cs"],
"tests_attempted": ["dotnet test → 174/174 passed"],
"known_risks": ["traversal behavior on UNC paths untested"],
"needs_human_review": false
}
Fields¶
| Field | Type | Semantics |
|---|---|---|
status |
string enum | Terminal outcome of the invocation. One of completed, failed, timed_out, rate_limited, approval_required. Nothing else, ever. |
summary |
string | One paragraph, human-readable, what happened. Empty string allowed, never null. |
changed_files |
string[] | Repo-relative paths the agent believes it changed. A claim, not truth — hosts must measure ground truth from git diff (FLT-3). Empty array when nothing changed. |
tests_attempted |
string[] | Commands the agent says it ran, with claimed outcomes. Unverified — the validation gate reruns tests itself and its exit code is the only trusted signal. |
known_risks |
string[] | Anything a reviewer should double-check. Hosts may append their own entries (e.g. "CLI exited 1 despite completed claim"). |
needs_human_review |
bool | Agent or host asserts a human must look before this work lands. Hosts may set it true; they must never set it false over an agent's true. |
status semantics¶
| Value | Meaning | Host reaction |
|---|---|---|
completed |
Agent believes the task is done. | Proceed to review + validation. Belief ≠ truth. |
failed |
Errored or gave up. | Escalate to next agent on the ladder. |
timed_out |
Host killed it at the timeout. | Escalate; consider retry with larger budget. |
rate_limited |
Vendor throttled the account. | Escalate to a different vendor now; this vendor retries later. |
approval_required |
Agent stopped at a decision it may not make (destructive step, ambiguous spec). | Route to the human gate with whatever partial work exists. |
Parsing rules (defensive, both implementations)¶
CLI agents emit free-form text; the contract block is requested via a prompt suffix, so parsing must survive non-compliance:
- Prefer the last fenced
``json block containing a"status"` key. - Else the last bare top-level
{...}that parses and has"status". - Unknown
statusstring → coerce tofailed(a malformed claim of success is not success). - Unknown extra fields → drop. Missing list fields → empty list.
Missing
needs_human_review→false. - Nonzero process exit +
"completed"claim → downgrade tofailed, append aknown_risksentry, setneeds_human_review: true. - No parseable block at all: exit 0 →
completedwith output tail assummary, aknown_risksnote, andneeds_human_review: true; exit ≠ 0 →failed. Rate-limit markers in output →rate_limited.
Invocation quirks the host owns (not part of the wire shape)¶
codex exec --json <prompt>— parse the JSONL event stream.claude -p <prompt>.agy --model "Gemini 3.1 Pro (High)" -p <prompt>— never-m; stdin must be closed (< NUL/stdin=DEVNULL) or agy hangs.grok -p <prompt>— prompt hard-capped at 32 KB.- All agents run with stdin closed, inside an isolated git worktree, under a host-owned timeout. Hosts own worktree creation, diff collection, retries, and escalation — agents never do.