Skip to content
Build

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.

Example run · habit-tracker
Snapshot → sandbox volume
Parse · 412 symbols indexed
Select · 4 segments, ≈ 396 tokens
Call · prefix read from cache
Verify · 31 / 31 tests passed
Diff ready · 3 files · +84 −12
How a run works

Seven steps, one engine.
Every Build run goes through the same pipeline.

Click a step, or let it play.

01 / 07

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.

01

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.
src/streak.tstree-sitter-typescript
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).

Rust.rs
TypeScript.ts .mts .cts
TSX.tsx
JavaScript.js .mjs .cjs .jsx
Python.py .pyi
02

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.
03

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.
Context manifesttask “fix the streak reset”
  • 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—
Budget used≈ 396 of 4,000 tokens

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.

04

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.
Turn 1
tools + system + task contextnew
writes the prefix to the cache (1.25×)
Turn 2
read from cache · 0.05×new
reads turn 1, writes the new part
Turn 3
read from cache · 0.05×new
reads everything up to the last breakpoint
cache read · 0.05× input pricecache write · 1.25×new tokens · 1×

Bar widths are illustrative. Prices below are measured.

Cost of one requestUS dollars · lower is better
No marker, same request twiceread 0 · write 0 tokens$0.0146
Marked, coldread 0 · write 4,927 tokens$0.0248
Marked, warm — same requestread 4,927 · write 0 tokens$0.0012
Later content changed, prefix sameread 1,767 · write 3,160 tokens$0.0163
Prefix changed — full missread 0 · write 4,934 tokens$0.0248
Breakpoint on a tool result, then continueread 5,982 · write 0 tokens$0.0014

Measured live, 26 Sep 2026 · anthropic/claude-opus-5.5 via OpenRouter · ADR 0010

05

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.

  1. 1
    Related 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.

  2. 2
    The whole suite

    Your project's own test command. This is always the final check — the targeted run never replaces it.

  3. 3
    Independent 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.

PASSEDThe full suite ran in the sandbox and passed.
FAILEDShown with its output. The agent gets the failing tests by name — up to two repair turns.
UNVERIFIEDSomething passed, but not in a way that proves the change: edited tests, or old failures unrelated to it.
UNAVAILABLENo test command or no sandbox runtime. Reported as such, never as passing.
Sandbox container · default profile
Networknone
User1000:1000, not root
Root file systemread-only · /tmp 64 MiB, noexec
Capabilitiesall dropped · no-new-privileges
Limits2 GiB memory · 2 CPUs · 256 processes
Time10 min by default, 30 min at most
Your foldernever mounted — the task works on a copy

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.

06

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.
src/streak.ts+6 −2
  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); }
PASSED · 31 / 31 in the sandbox · Claude Opus 5.5
Privacy

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/240
    Runs accepted by hidden testsevery arm, in a network-less container

Questions

Does Build understand my code, or just search it?
Both, and it tells you which. Tree-sitter gives exact syntax for five languages; language servers (rust-analyzer, typescript-language-server) answer definitions, references and calls. Every graph edge is labelled RESOLVED, SYNTACTIC, HEURISTIC or UNRESOLVED, and no confidence percentage is made up.
What if my language isn't in the list?
Lexical search and the sandboxed test run still work. Parsing, symbols and the graph are available for Rust, TypeScript, TSX, JavaScript and Python today.
Will the model ever get half a function?
No. A definition goes in whole, or as a signature that is explicitly labelled [signature]. The middle of a body is never cut. Whatever doesn't fit the budget is listed as “Not included (context budget)” — in the prompt and in the manifest.
Is prompt caching on for every model?
Cache markers are placed only where they were measured to work: anthropic/* models via OpenRouter. They are never pasted into other APIs. Caching doesn't change the content of the prompt — the same text goes out either way.
Do tests run on my machine?
They run in a Docker container on your Mac with no network, a read-only root, no capabilities and fixed limits. Your real project folder and home directory are never mounted. If no container runtime is available, Build says the sandbox is unavailable instead of falling back to your shell.
Can the model silently switch?
No. Before a task starts the engine checks the model exists and supports tools; otherwise the task is not created. If a streamed response reports a different model than the one bound, the response is rejected.

Stop pasting files into a chat.
Let Build read the project for you.