Cheat sheets

Coding Agents: Landscape and Capstones

Pick a scaffold, set the budgets and gates, run the capstone agents and the three coding-agent projects.

  • Concepts and terms
  • Tasks and commands
  • Annotated code
  • Diagrams
  • Comparisons
  • Defaults and numbers
  • Troubleshooting
  • Questions and tips

Source

JSON tool calls vs CodeAct#

FactorJSON tool callsCodeAct
One action isOne structured JSON payloadA whole Python program
AuditEasy, one action per turnHarder, wide blast radius
CompositionLimitedLoops, chains tools, catches errors
SafetyValidator on every callNeeds a hardened sandbox
Toy blast radius1 file per actionEvery failing file at once
Dominant inManaged servicesOpen platforms

Three capstone agents#

FactorTerminal agent (01)Issue-to-PR (16)Migration (09)
Triggeragent run <repo> "<task>"Issue label @agent fix thismigrate <repo> --target java17
SandboxE2B or Daytona, git worktreeDaytona or E2B per taskDaytona per branch
Turn cap50 turns30 agent turns20 agent turns
Cost cap$5 per task$20 per PR, 5 PRs per repo per day$8 per repo
Other cap200k context30 min wall-clock30 min per repo
Gate before PRTests passFull CI in sandbox, coverage deltaTests pass, coverage not down 2%
On failureStop hook writes a trace bundleCI red: halt, no PRFailure class plus repro

Use X when#

Use JSON tool calls when
each action needs an explicit validator and an easy audit.
Use CodeAct when
one action must chain tools over many files. Harden the sandbox first.
Use a terminal agent when
the work is CLI in, pull request out, with a live plan.
Use issue-to-PR when
the UX is a GitHub label. No interactive loop, no terminal.
Use a migration agent when
the test suite is the ground truth across many repos.
Use deterministic recipes first
they catch 70 to 80% of mechanical migration fixes.

Numbers to know#

SWE-bench Verified
4% to 80.9% in under three years. 500 human-curated tasks.
Scaffold delta
Claude Sonnet 4.5 got 43.2% on SWE-agent v1 and 59.8% on Cline.
Easy tail
161 of 500 Verified tasks need only a 1 or 2 line change.
SWE-bench Pro
10+ line changes. Frontier systems score 23 to 59%.
Verifier loop
a 10+ point delta on SWE-bench.
Live-SWE-agent
79.2% on SWE-bench Verified with Opus 4.5.

Terminal agent harness (01)#

callstool callsrunclientUser CLIagent runprocessHarnessBun + Ink TUIprocessPlan, act, observeTodoWrite plan statemodelModelvia OpenRouterprocessTool dispatcherMCP StreamableHTTPprocessSandboxE2B or Daytona,worktreequeueHooks8 lifecycle eventsstoreOpenTelemetryLangfuse: spans,tokens, $clientPRvia GitHub appcallstool callsrunclientUser CLIagent runprocessHarnessBun + InkTUIprocessPlan,act,observeTodoWriteplanstatemodelModelviaOpenRouterprocessTooldispatcherMCPStreamableHTTPprocessSandboxE2B orDaytona,worktreequeueHooks8lifecycleeventsstoreOpenTelemetryLangfuse:spans,tokens, $clientPRviaGitHubapp

Budgets in the code#

SettingDefaultNote
max_turns50Capstone 01 Budget
max_tokens200_000Capstone 01 context ceiling
max_dollars5.00Capstone 01, per task
TRUNCATE_BYTES4096Tool output cap in 01
BUDGET_TURNS20Migration, with BUDGET_USD 8.0
step_budget12L29 AgentRun

Q&A#

Which coding agent is best?
Wrong question. Measure reliability on your own task distribution.
Why does the toy CodeAct take fewer turns?
One snippet rewrites every failing file at once.
Why no assert for a safety gate?
python -O strips asserts.

Run the lessons#

TaskCode
Compare the toy scaffolds (L09)code/main.py
Plan, act, observe loop (01)python main.py
TS harness demo plus offline evalnpm start
Simulate 50 migrations (09)python main.py
Dispatch 20 issues over 3 repos (16)python main.py
Fix the fizz fixture (29)python3 code/main.py
Test the end-to-end demopython3 -m pytest code/tests/ -v

The three projects#

Factortiny-coding-agentrust-agent-shellpr-review-reporter
BuildsBounded repair loop with exact patchesRust stdin/stdout action loopAnchored patch review report
LanguagePython 3.10+Rust plus a Python adapterTypeScript on Node 22.18+, Python
InputWorkspace plus proposals.jsonWorkspace plus requests.jsonlUnified diff, stdin or --repo
Outputrepair-trace.jsonJSONL events per request idreview.json, review.html, SARIF
Budget--max-steps default 6--limit default 50Diff at most one megabyte
SafetyPath containment, one test commandNo OS shell, 16 KiB readsHTML escaping, exact quote gate
End statescompleted, failed, budget_exhaustedok, error, rejectedAccepted or rejected findings

Project commands#

CommandWhat it does
project_test.py <project> --init learning-artifacts/<project>Creates a learner workspace
project_test.py <project> --stage 1 --path <workspace>Grades one stage
project_test.py <project> --all --solution --strictChecks the reference
cli.py <workspace> <proposals> --copy-to <dir> --out <trace>Repairs a disposable copy
client.py <workspace> <requests> --limit 8 --out <file>Rust shell through the adapter
node cli.ts --diff samples/change.diff --sarif review.sarifWrites JSON, HTML and SARIF
--repo /path --base REF --head REFRead-only Git diff mode
--candidates recorded-findings.jsonValidates another reviewer's findings

Gate chain and sandbox (29)#

chain = GateChain(gates=(
WhitelistGate(allowed=frozenset({"read_file", "write_file", "run_tests"})),allow-set
RegexGate.from_strings(items=(r"\brm\s+-rf\b", r"\bsudo\b", r"^/etc/")),refused patterns
BudgetGate(max_tokens=budget),observation budget
))
outcome = self.chain.evaluate(call, ctx)stops at the first deny
sandbox = Sandbox(project_root=repo, timeout_seconds=10.0)denylist plus path jail

Troubleshooting#

SymptomCauseFix
Loop crashes on 8MB of ripgrep matchesUntruncated tool outputCap each call at 4k tokens
rm -rf reaches the sandboxNo PreToolUse guarddestructive_guard sets blocked
High Verified score, weak real resultsEasy tail, 161 of 500 tasksScore a Pro-like subset of your backlog
budget_exhausted30 min, $8 or 20 turns usedFile the repo with the current diff
coverage_regressionCoverage dropped more than 2%File under the class, no PR
policy_deniedcan("force_push") returns FalseBranch protection, app not in bypass list
patch must match exactly onceOld text missing or not uniqueUse one unique exact old string
path escapes rootPath leaves the canonical rootPass a relative path inside the root