A Rust engine under the agent.
It reads your code before the model does.
Build parses your project, links every symbol, sends the model only what a change needs, and runs your tests in a sandbox before it shows you a diff.
Seven steps, one engine.
Every Build run goes through the same pipeline.
Click a step, or let it play.
Copy the project into a sandbox volume
The task gets its own copy of your project in a Docker volume. Your real folder is never mounted, and secrets, .git and node_modules are left out.
Parse into syntax trees.
Tree-sitter turns each file into a concrete syntax tree. The engine walks it and records every definition — functions, methods, classes, structs, traits, interfaces, types, constants, macros, imports — with its exact byte range.
- IncrementalA file is re-parsed only when its BLAKE3 hash or grammar version changes.
- ConsistentUpdates are published as whole generations — a query never sees half an old index and half a new one.
- BoundedUp to 20,000 files per workspace, 4 parses in parallel, 1 MiB and 2 s per file. Anything skipped is reported.
- HonestFiles with syntax errors keep the symbols that were found and are marked as such.
import { db } from "./store"; export function streakFor(habit: Habit, today: Day) { const days = db.checkins(habit.id); let n = 0, d = today; while (days.has(d)) { n++; d = prev(d); } return n; } export interface Habit { id: string; name: string }
- program
Hover a line or a node. Definitions become symbols with a byte range, a line range and an id: BLAKE3(workspace, path, kind, name, ordinal).
Link into a symbol graph.
Language servers — rust-analyzer and typescript-language-server — run in network-less containers on a copy of your files and answer definitions, references and incoming calls. The index adds what contains what.
- Labelled evidenceEvery edge says how it is known. A reference is never presented as proof that a call happens at runtime.
- Never staleAnswers are tied to the document version and a digest of the files; if either changed, the answer is thrown away.
- Bounded queriesNeighbourhoods go 3 levels deep, 200 nodes, 12 edges per node — a busy helper can't eat the budget.
- FallbackNo server? Same-name definitions and word matches stand in, labelled HEURISTIC.
Select only what the task needs.
The context engine starts from what you named and where tests fail, follows the graph a short way, and scores every candidate with written reasons. Those reasons go into the prompt too.
- SeedsPaths and names in your request or the test output, error locations from tracebacks and stack traces.
- Whole or signatureA definition goes in whole or as a labelled [signature] — never half a body.
- Budget10% of the model's context window, between 4,000 and 20,000 tokens. What doesn't fit is listed, not dropped silently.
- Default since the benchmarkThe pre-registered run found it no worse than the baseline on any held-out task, so it is on by default.
- streakForsrc/streak.tsnamed in the requestwhole≈ 82 tok
- Habitsrc/streak.tstype the target useswhole≈ 20 tok
- streak testssrc/streak.test.tstest that names the targetwhole≈ 263 tok
- db.checkinssrc/store.tscalled by the target[signature]≈ 31 tok
- exportCsvsrc/export.tssame directory, not usednot included—
- Chartsrc/ui/Chart.tsxno link to the tasknot included—
Illustrative task. Every segment is stored with its path, byte range, BLAKE3 digest and reason, and the prompt block is re-rendered from the manifest before the call.
Cache the part that doesn't change.
Tools, system prompt and task context form a stable prefix — no timestamps, nothing random. One cache marker sits on it; a second moves to the newest message every turn, so each turn reads the one before.
- Provider-specificMarkers go only where they were measured to work: anthropic/* via OpenRouter. Other APIs never get the field.
- Same contentMarking doesn't change a word of the prompt. A cache miss costs money, never correctness.
- Counted properlyCache reads and writes are tracked separately; cost is the provider's own figure.
Bar widths are illustrative. Prices below are measured.
Measured live, 26 Sep 2026 · anthropic/claude-opus-5.5 via OpenRouter · ADR 0010
Verify before you see it.
Checks are run by the engine, not suggested by the model. Results are compared test by test with a baseline taken before the change, so a regression is named, and an old unrelated failure isn't blamed on it.
- 1Related tests
Test files that changed, or that name the changed module, run first as a fast check. If they fail, repair starts without waiting for the full suite.
- 2The whole suite
Your project's own test command. This is always the final check — the targeted run never replaces it.
- 3Independent acceptance
If the change edited or deleted a test file, the original tests are put back and the suite runs again. Edited tests that pass while the originals fail give UNVERIFIED, never success.
A container with nothing to lose.
The sandbox is a Docker container on your Mac, created per operation and recorded in a ledger so it can be recovered after a crash. If no runtime is installed, Build says so — it never falls back to your shell.
Review a diff that passed.
You get the change, the test result and the context manifest — what the model saw and why. Pick the model for every conversation; it is bound when the task starts.
- No silent fallbackIf the model is missing or can't use tools, the task isn't created.
- No model swapsA streamed answer from a different model than the one bound is rejected.
- Bounded loopUp to 40 turns, 16 tool calls per turn, 2 repair turns. Extra calls are reported, not run.
export function streakFor(habit, today) {- let n = 0, d = today;+ let n = 0;+ let d = days.has(today) ? today : prev(today); while (days.has(d)) { n++; d = prev(d); }
What leaves your Mac,
and what never does.
Leaves your Mac
- Your message and the context segments listed in the manifest
- Tool results the agent needs for the next turn
- Sent from the Rust core to the model you picked, over https
Stays on your Mac
- Your project folder — the sandbox works on a copy in a local volume
- The provider key, in the macOS Keychain; the interface never sees it
- The syntax index and symbol graph, in a local SQLite database
Measured,
not promised
Pre-registered benchmark: 20 tasks, 4 arms, 3 repeats, hidden tests the agent never saw. One model (Claude Opus 5.5 via OpenRouter), one machine.
Read the benchmark- −49%Cost per accepted taskcontext engine + prompt cache vs baseline · $0.0405 → $0.0205 · 240 runs
- −18%Turns to finish a taskcontext engine vs baseline · 3.05 → 2.50 on average
- −95%A repeated request, cache warm$0.0248 cold → $0.0012 warm
- 240/240Runs accepted by hidden testsevery arm, in a network-less container