# volt for agents

volt is a printed circuit board design tool. A board is a set of text files in Volt (`.volt`, a small declarative language with units, typed nets and pins, parts and blocks): `design.volt` is the board; `.volt/routes.volt` and `fills.volt` are the copper the router writes. The tool evaluates the files, checks the board, routes it, and imports and exports KiCad. The web app runs it all in the page (the board stays there: only a quote, the hosted component library and the parts catalog go over the network); the command-line tool `volt` reads and writes the same files.

## How to reach the API

In the web app, every action is a command of one registry, available on the page as `window.volt`. Use it for everything — reading the board, changing it, and moving the person's view — rather than clicking or typing in the interface.

**If your page tool cuts long results short or withholds text with `key = value` or `?x=` in it** (browser extensions often do), do not print big answers: every call takes options as a third argument, `volt.call(name, input, { select, dump, mask })`, and the page keeps the text of the last answer:

- `select`: only part of the result, by path: `'summary'`, `'pins.*.name'` (`*`: every item or value), `'issues.0'`; a list of paths gives `{ path: value }`;
- `volt.read(offset?, length?, { mask? })`: a page of the last answer's text (900 characters by default), headed by where it is and the next call: `[0–900 of 26013 characters; next: volt.read(900)]`;
- `dump: true`, or `volt.dump({ offset?, length?, mask? })` after the call: the text goes into the page as `<article id="volt-agent-output">` (off screen, in the page's text and accessibility tree, for page-text tools) and the call answers only `{ element, length, total }`;
- `mask: true` writes `=` as `＝` and `?` as `？` (full-width; read them as `=` and `?`).

`help { topic: "agents" }` is this whole guide with the command catalogue (the site's `/agents.md`): read it in pages without leaving the project.

- `await volt.help(topic?)`: this text (topics: `quickstart`, `agents`, `api`, `workflows`, `volt`, `format`); `volt.commands()`: the catalogue (names, descriptions, JSON Schemas);
- `await volt.hello(name, about?, vendor?)`: introduce yourself (the person sees you connected, with your maker's mark — `vendor`: `anthropic`, `openai`, `google`, `cursor`, `github`, else recognised in `name` — and your changes are attributed to you).

- `await volt.call(name, input)`: run a command; resolves with its result, or throws an error that says what was wrong (`invalid_input: …`, `not_found: … (did you mean …?)`);
- `await volt.batch([{ name, input }, …])`: several commands in one round trip (`batch.run`), e.g. an edit and the view actions that show it;
- `await volt.explain(name, input)`: like `call`, resolving with `{ result, summary }`.

**Every answer says what happened.** Object results carry `summary`: one or two sentences with the numbers that matter (what changed, the check before → after, what is still unrouted) and the natural next step, often a view action. For example: `Placed R1 to (10, 3) mm (undoable); check: incomplete — 0 errors, 2 warnings (track-dangling ×2), 2 unrouted nets (LED_A, VIN) [warnings 0 → 2; unrouted nets 0 → 2]. Next: view.select { ids: ["comp:R1"] } and view.focus to show the person; route.run { wait_ms: 25000 }.` The summary is generated from the structured result and never replaces it: read the fields for exact values.

If your JavaScript runs in an isolated context (a browser extension, an agent's page tool) and does not see `window.volt`, use messages, which every context of the page shares:

```js
const id = Math.random();
const answer = new Promise((ok) => addEventListener('message', function on(e) {
if (e.data?.volt === 'response' && e.data.id === id) { removeEventListener('message', on); ok(e.data); }
}));
postMessage({ volt: 'request', id, op: 'call', name: 'project.summary', input: {}, agent: 'codex' }, '*');
await answer; // { ok: true, result, summary } or { ok: false, error }
```

`op` is `call` (with `name`, `input` and the `options` above), `read` (`offset`, `length`, `mask`), `dump`, `hello` (with `name`), `help` (`topic`) or `commands`; `agent` attributes the call to you. The page marks itself with `<html data-volt-api>`.

**If the page loads again** (a reload, a navigation away and back, the browser discarding a background tab), globals you set on the page are gone, but `window.volt` comes back knowing you (the agents that said `hello` in this tab are kept by name, and `volt.author` is yours again) and the journal goes on from its number: a `reload` event says it happened (`journal.since`, `journal.next { kinds: ["reload"] }`). Opening `/agents.md` in the project's tab leaves the project: read `help { topic: "agents" }` instead.

**WebMCP:** where the browser provides it (`document.modelContext`, the current draft of the W3C Web Machine Learning group; earlier drafts and browsers had `navigator.modelContext`, used when the document has none), the same commands are registered as tools: `'modelContext' in document || 'modelContext' in navigator` tells whether it is there, `document.documentElement.dataset.voltWebmcp` which one the page used (absent: none, use `window.volt`). Tools are named `board_move` for `board.move` (the summary is the first text of each tool result). A tool that starts a job (`route_run`, `build_run`, `place_run`, `fill_run`) answers when the job ends, however long it takes (no `wait_ms`); cancelling a call (its `signal`) ends its wait, and cancels the job it started. The command-line tool runs the same commands: `volt call <name> '<json>'` (its JSON has `summary` too), `volt commands`.

## Conventions

- **Ids** are `kind:key`: `comp:R1`, `pad:R1.2` (component and pad number), `net:GND`, `track:3`, `via:5`, `zone:2` (a pour's fill), `pour:0`, `area:0` (a keepout or rule area), `class:power` (a net class), `block:power` (a block instance), `graphic:0`, `board`, `file:design.volt`. Commands that take a component (`board.move`, `board.connect`, …) accept `R1` or `comp:R1`. Check issues list their objects as `ids`, ready for `view.highlight`.
- **Units:** lengths in mm, angles in degrees counter-clockwise, areas in mm²; origin at the board's lower-left corner, Y up. Points in the API are `[x, y]` (mm); in Volt they are `(10mm, 5mm)` with units written.
- **Which command:** `check.run` after an edit (status, counts, the first 20 problems); `report.get` to page through all problems by severity or code; `project.summary` for the board in a few lines; `board.stats` for areas, density, copper and lengths; `model.object` for one object. `view.focus` fits objects into view (the core moves the camera); `view.zoom` zooms by a factor or to 1:1 (the page does it). `files.write`/`files.edit` change the source as text; `board.*` change one value in place (the literal that defines it).
- **Every change is undoable** and attributed (`history.list`, `history.undo`); generated files (`.volt/…`) are the tools': change the design and route instead.

## Work with the person: narrate with the view

The person watches the board while you work — often talking to you — and you control their view through the API. Make each step visible, and keep it quick:

- **Show what you changed, in the same round trip.** After an edit, `view.select { ids }` (the inspector and the code show the object) and `view.focus { ids }` (the camera brings it into view): `volt.batch([{ name: 'board.add', input: {…} }, { name: 'view.select', input: { ids: ['comp:C5'] } }, { name: 'view.focus', input: { ids: ['comp:C5'] } }])`.
- **Point at problems:** `view.highlight { ids }` with an issue's `ids` (or `net:VBUS`), then `view.focus`; clear the highlight (`ids: []`) when you move on.
- **Say what you are doing:** `agent.status { text: "routing the power nets", focus: ["net:VBUS"] }` before long work; the person sees it next to your name.
- **Show results:** after routing, `view.focus {}` (the whole board); `view.mode { view: "3d" }` for a look as fabricated, `view.mode { view: "2d" }` back; `view.layers { only: ["F.Cu", "zones"] }` to show one layer; `view.mode { mode: "split" }` with `editor.open { path, line }` when the person wants to see the code.
- **Follow the summary's `Next:`** — it names the view action that fits.
- **Stay fast:** no `view.snapshot` unless you need pixels (the state calls describe what is on screen); one `files.write` of a whole design rather than many small edits; `wait_ms` on the job's command (`route.run { wait_ms: 25000 }`) instead of polling; `route.run { time_limit: 10, wait_ms: 25000 }` for a first result in seconds.
- **Wait for the person** when you need a choice: `journal.wait { for: "selection", wait_ms: 25000 }`, then `view.state`.

## Workflows

- **Start a turn:** `journal.since { seq }` with the number you read last (0 the first time): what the person and others changed, the latest evaluation and selection. `view.state` tells what the person is looking at.
- **Addresses and demos:** the page's address names what is open: `/p/<name>` a project in this browser's storage, `/demo/<slug>` a demo board, `/` the start page (`/new` makes a board, `/import?url=…` offers to import a KiCad board or zip from an address). You may be given such an address to open ("open https://app.volt0.ai/demo/blinker and route it"): the page opens it by itself; `view.state` → `route` says what is open and whether it is still `loading` (wait with `journal.wait { for: "eval_ok" }`). `workspace.demos` lists the demos, `workspace.demo { slug }` opens one: a demo is not stored until its first change (an edit, routing), which saves it as a project in this browser (the address becomes `/p/<name>`; the demo's address opens that copy from then on, `?original` the demo). `workspace.link` gives the address to share: a demo's opens anywhere, a project's only in this browser (projects stay on the person's machine; `export.repository` makes a zip to send). The person's navigation (a link, back, forward) is in the journal as `route` events.
- **A new board from a request** ("a driver board for a brushless motor"): `workspace.new { name }` (a fresh project in the browser; one still named `Untitled board` takes the board's name, `Board { name }`, once it evaluates; `workspace.rename` renames it) or `project.new { name }`; find the parts: `parts.search { query: "gate driver bldc" }`, `parts.search { query: "stm32g4" }` — curated parts have verified pin tables; `parts.describe { id, functions: "TIM1_CH*" }` shows which MCU pins can carry a function (PWM, ADC, SPI) so you choose pins, and its `notes` list the support parts (charge-pump and regulator capacitors…); `parts.use { id }` puts it into the board as `parts/<id>.volt`: place it as `<id>.Part`. Standard parts (`cap`, `res`, `led`, `header`, `hole`, `testPoint`) and footprints (`soic(8)`, `qfn(…)`, `catalog["…"]`) need no declaration. Then write the whole design in one `files.write` of `design.volt` (nets, board outline and layers, components with `pins`; see `help { topic: "format" }`), `check.run`, fix, `route.run { time_limit: 10, wait_ms: 25000 }`. **Never invent pin numbers:** a part that is not in the registry needs its datasheet (the person's PDF or link, else the manufacturer's for the exact part number): read it into a part sheet — `parts.sheet` gives the schema and the guide (pins, the package drawing, the land pattern, ratings, the application circuit, the 3D body) — `parts.check { sheet }` until it reports no errors (it catches misread drawings: swapped axes, a land pattern off the leads), then `parts.create { sheet }`; tell the person which parts are drafts.
- **Compose from blocks:** the circuit around a chip is not yours to invent. Before drawing one, `blocks.list` (`{ query: "regulator" }`, `{ category: "usb" }`): proven blocks (`Ldo`, `Buck`, `UsbC2`, `UsbUart`, `Crystal`, `McuSupport`, `I2cPullups`, `LedIndicator`, `Button`, `Divider`, `LevelShifter`, …) that size their parts from parameters, place them and type their pins. `blocks.describe { name, args: { voltage: "5V" } }` shows the parameters, the parts it makes and the values it computes (the LED's resistor, the crystal's load capacitors, an LDO's dissipation). Write the board as blocks joined by nets: `ldo = Ldo(vin = VBUS, vout = V3, gnd = GND, current = 200mA) {}`. Type the pins of the parts you add (`pinTypes { VDD: io.powerIn(min = 1.8V, max = 3.6V), GND: .ground, SDA: .openDrain }`) and declare supplies that come from connectors (`net VIN { voltage = 12V }`): `check.run` then runs the electrical rules (`erc-…`: outputs fighting, a rail nothing supplies, a floating input, an I2C bus without pull-ups, a 5 V pull-up on a 3.3 V pin) and reports the blocks' warnings (`warn`) with the line of the call.
- **Place parts:** leave `at` out and say what matters as rules; the engine places the rest and `check` verifies every rule: `J1 = UsbC { place = [edge(.bottom)] }` (on the bottom edge, its mating side facing out: the rotation follows), `C3 = cap(100nF) { place = [near(U1.pin.VDD, max = 2mm)] }` (decoupling), `ports = row([J2, J3, J4], evenly = true)`, `column([…], pitch = 8mm)`, `together([…], max = 10mm)`, `inside(box(…))`, `outside(around(U1, 3mm))`, `rotations([0deg, 180deg])`, `facing(.up)`, `soft(rule, weight = 2)` for a preference; leave the board's outline out too (it is sized around the parts, its edges through the edge parts). Then `place.run { wait_ms: 25000 }` (or `route.run`, which places first); a broken rule is a `place-…` error naming the rule and its line. `place.pin` keeps the engine's positions for good. Placing by hand: `model.components` gives each component's `bounds`, `board.measure { a, b }` the distance and the copper gap, `board.stats` how full each side is.
- **Read the board:** `project.summary`, `board.stats`, `model.components`, `model.nets` (each net's length, vias, layers), `model.object { id }`, `model.bom`, `report.get`.
- **Order parts:** say what each part must meet, not which part: `res(10kohm, tolerance = 1pct, power = 0.1W)`, `cap(100nF, voltage = 16V, dielectric = .X7R, package = "0402")`, `led(color = .red)`, `device(…, category = .regulator, params = { "Output Voltage": 3.3V })` or an `mpn`. `parts.select` chooses an orderable part for every group from the assembler's catalog (basic parts first, enough stock, reuse, price) and records it in `.volt/parts.volt` with the reason; `parts.alternatives { ref }` ranks the others and says why; `parts.pin { refs, code }` writes `orderCode` into the source; `parts.bom` prices the bill of materials. `check` warns about parts missing, stale or short of stock.
- **Add a component:** `board.add { name: "C5", part: "cap(100nF, package = \"0402\")", at: [12, 8] }` and `board.connect { ref, pin, net }`, or write the binding with `files.edit`: `C5 = cap(100nF, package = "0402") { at = (12mm, 8mm); pins { a: V3, b: GND } }`. The language: `help { topic: "volt" }`; the board format: `help { topic: "format" }`; the standard library's functions, types and blocks: `lang.search { query: "rounded rectangle" }`, `lang.describe { name: "wireToBoard" }` (signature, defaults, the pins of the part it makes).
- **Move or connect:** `board.move { ref, at, rotation, side }`, `board.connect`, `board.set`: they change the literal in the source; a computed value answers with `choices` (call again with `choice`).
- **Route:** `route.run { wait_ms: 25000 }` starts the job and answers when it ends (or with `pending` after 25 s: then `jobs.wait { id, wait_ms: 25000 }` again; `place.run` and `fill.run` take `wait_ms` too); `route.plan` estimates in milliseconds whether the board will route and where it is crowded, before routing. The job's `summary` says how many nets were routed, the copper added and the check. `board.unrouted` lists what is still missing (the two pads, the gap, where); `route.diagnose { net }` says why one fails (the nets in its way, a class too wide for its pads, parts too close) and the one thing to do. The routing comes first; a `polish` job (its id in the result's `polish`) then cuts vias and length in the background as a change of its own: go on working (your next edit cancels it); `export.fab` waits for it.
- **The person's comments:** the person pins comments to the board (on a part, a point or an area) for you: they are requests. At the start of a turn `comments.list { status: "open" }` (or watch for `comment` events: `journal.next { after, kinds: ["comment"] }`: `comment.added`, `.replied`, `.status`, `.edited`, `.removed`, also when the person edits `comments.volt` by hand); each has `text`, `at` (mm), `on` (the object it is about, e.g. `comp:U1`) or `area`, and its thread. Do what it asks, then `comments.reply { id, text: "what you did", status: "resolved" }`; if it should not be done, reply why with `status: "dismissed"`; ask back with a reply and leave it `open`. `comments.add { on, text }` points the person at something. They live in `comments.volt`.
- **Fix an issue:** `report.get { severity: "error" }`; each issue has `at`, `ids` and a message; `view.highlight { ids }` and `view.focus { ids }` show it.
- **The person's window:** `view.state` → `view.viewport` gives its size, the layout (`wide`, `compact`, `narrow`: a window side by side with yours is often narrow; `phone`) and the part of the board no panel covers (`free`). `view.chrome { hidden: true }` shows the board alone (e.g. before `view.snapshot`), `{ hidden: false }` brings the interface back; `left`/`right` open or collapse a panel. Opening the page with `?ui=hidden`, `?ui=compact` or `?ui=narrow` starts that way.
- **Export:** `export.fab` (fabrication files: Gerber, drill, BOM, placement list, for fabs and assembly; the board must check `complete` unless `force`), `export.kicad`, `export.repository` (a board repository for git and the command-line tool); the app offers the output as a download (`workspace.download`).
- **Undo:** every change is attributed and undoable (`history.list`, `history.undo`).

## Limits

- Calls answer within a few seconds; long work (routing, optimising, importing) is a job: `wait_ms` on its command waits for it in the same call, `jobs.wait` after. A job's `time_limit` (default `routing.timeLimit`, 60 s) bounds it by the clock, placing and checking included; routing usually ends much sooner, and `route.run { time_limit: 10 }` gives a first result in seconds.
- Waits (`journal.next`, `journal.wait`, `jobs.wait`) last at most 30 s: call again to keep waiting. Waits do not wait inside `batch.run`.
- Large answers come in pages (`offset`, `limit`) and summaries: never a whole big board at once.
- Inputs are checked against their schema: an unknown field is an `invalid_input` error naming the fields there are.
- `view.snapshot` gives an image of the canvas when you need pixels; the state calls are cheaper.
- The parts registry is small for now: curated parts are listed by `parts.search`; anything else is read from its datasheet (`parts.sheet`, `parts.check`, `parts.create`) and stays with the board.

## Command catalogue

Generated from the registry. Inputs and results are JSON, shown here as TypeScript-like types (`?`: optional); `commands.describe` and `volt.commands()` give the JSON Schemas.

### help

#### `help` — Help

What volt is, how to call commands, the common workflows, and where the project stands now. Start here. _(reads only)_

Shortcut: `F1`.

```ts
input: {
  // Only the section of the topic whose heading contains this (case-insensitive), e.g.
  // `Placement rules`: long topics in parts.
  section?: string | null
  // `quickstart` (read first: the shortest path to a routed board), `api`, `workflows`,
  // `agents` (the whole guide for agents with the command catalogue: the site's `/agents.md`),
  // `volt` (the language), `format` (the board format), or empty for the overview.
  topic?: "overview" | "quickstart" | "api" | "workflows" | "agents" | "volt" | "format" | "" | null
}
result: {
  // The journal's last number: read changes after it (`journal.since`, `journal.next`).
  journal: integer
  // The project now.
  project: {
    // The board's name (`Board { name }`; `board` when the design gives none), of the last good
    // evaluation.
    board?: string | null
    // Check errors.
    errors: integer
    // `ok`: the files evaluate; `error`: they do not (the last good evaluation is kept);
    // `empty`: no design yet.
    eval: string
    // The first evaluation errors (`file`, `line`, `column`, `message`).
    eval_errors: unknown[]
    // The good evaluation is older than the files.
    stale: boolean
    // The check status of the last good evaluation: `error`, `incomplete`, `complete`.
    status?: string | null
    // Nets not yet joined.
    unrouted: integer
    // The files' version this evaluation is of.
    version: integer
    // Check warnings.
    warnings: integer
  }
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Markdown.
  text: string
}
```

### commands

#### `commands.list` — List commands

Every command with a one-line description and its annotations; filter by group (`board`, `files`, …) or by a word. `commands.describe` gives one command's whole description and JSON Schemas. _(reads only)_

```ts
input: {
  // Only this group (the part of the name before the dot).
  group?: string | null
  // Only commands whose name, title or description contains this (case-insensitive).
  query?: string | null
}
result: ({
  // Deletes or overwrites data (undoable all the same, unless its description says not).
  destructive: boolean
  // Who carries it out: `core` (everywhere), `app` (only the web app: it needs the
  // interface), `net` (the CLI and the web app, over the network).
  host: string | string | string
  // The default keyboard shortcut in the web app (`Mod`: Cmd on macOS, Ctrl elsewhere).
  keys?: string | null
  // Starts a job and answers at once (`wait_ms`, `jobs.wait`).
  long: boolean
  // `group.verb`: what `commands.describe` and calls take.
  name: string
  // Changes nothing.
  read_only: boolean
  // The description's first sentence (`commands.describe` gives the whole).
  summary: string
  // The palette's label.
  title: string
})[]
```

#### `commands.describe` — Describe a command

A command's full description with the JSON Schemas of its input and result. _(reads only)_

```ts
input: {
  // The command's name, e.g. `board.move` (`commands.list` lists them).
  name: string
}
result: any
```

### agent

#### `agent.hello` — Agent: introduce

An agent introduces itself by name (and maker, `vendor`): the interface shows it as connected, with its maker's mark. Returns the help, the project's state and the journal number to read changes from.

```ts
input: {
  // What it is (one line), shown to the person.
  about?: string | null
  // The agent's name, e.g. `claude-code` or `codex`.
  name: string
  // Whose tool it is: `anthropic`, `openai`, `google`, `cursor`, `github` (or another maker's
  // name); the interface shows the maker's mark as the agent's avatar. Default: recognised in
  // `name` and `about` (`claude-code` → `anthropic`, `codex` → `openai`, …).
  vendor?: string | null
}
result: {
  // Call with this author from now on (the page's API does it for you after `agent.hello`).
  author: string
  // The overview (`help`'s text, Markdown).
  help: string
  // The journal's last number: read changes after it (`journal.since`, `journal.next`).
  journal: integer
  // The project now.
  project: {
    // The board's name (`Board { name }`; `board` when the design gives none), of the last good
    // evaluation.
    board?: string | null
    // Check errors.
    errors: integer
    // `ok`: the files evaluate; `error`: they do not (the last good evaluation is kept);
    // `empty`: no design yet.
    eval: string
    // The first evaluation errors (`file`, `line`, `column`, `message`).
    eval_errors: unknown[]
    // The good evaluation is older than the files.
    stale: boolean
    // The check status of the last good evaluation: `error`, `incomplete`, `complete`.
    status?: string | null
    // Nets not yet joined.
    unrouted: integer
    // The files' version this evaluation is of.
    version: integer
    // Check warnings.
    warnings: integer
  }
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### batch

#### `batch.run` — Several calls at once

Run several commands in one round trip, in order (e.g. an edit, then `view.select` and `view.focus` on what changed): each answer with its `summary`. Stops at the first error unless `keep_going`. Commands the web app carries out (`host: app`, e.g. `view.zoom`, `view.snapshot`) and waits (`wait_ms`) do not run inside a batch: call them on their own. At most 50 calls; earlier calls' changes stay when a later one fails.

```ts
input: {
  // The calls, in order: `[{ name: "board.move", input: { ref: "R1", at: [10, 5] } }, { name: "view.focus", input: { ids: ["comp:R1"] } }]`.
  calls: {
    // Its input, as `commands.describe` gives its schema (default: none).
    input?: {}
    // The command, e.g. `board.move`.
    name: string
  }[]
  // Run the rest after a call fails (default: stop).
  keep_going?: boolean
}
result: {
  // All ran without error.
  ok: boolean
  // One per call run: `{ name, ok, result?, error?, summary? }`.
  results: unknown[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### agent

#### `agent.status` — Agent: say what you do

Say in a short line what the agent is doing (shown to the person next to its name, like a collaborator's status) and which objects it is looking at or touching (`focus`, shown on the board). Call it before long work; changes no file.

```ts
input: {
  // Ids of what it is looking at or touching (`comp:U1`, `net:GND`, …).
  focus?: string[]
  // One short line, e.g. `routing the USB pair`.
  text: string
}
result: {}
```

### page

#### `page.loaded` — The page loaded

The web app reports that its page loaded again in the same tab (a reload, a navigation away and back, the browser discarding the tab): the journal goes on from the number the page had (`journal`), the agents it knew are restored, and a `reload` event tells agents that their page globals are gone. _(for the interface)_

```ts
input: {
  // The agents that had introduced themselves (`agent.hello`) in this tab.
  agents?: ({
    about?: string | null
    name: string
    vendor?: string | null
  })[]
  // The browser had discarded the tab (to save memory) and loaded it again.
  discarded?: boolean
  // The journal's last number before the load: numbering goes on after it.
  journal?: integer
  // How the page was reached: `reload`, `navigate`, `back_forward`.
  navigation?: string | null
}
result: {}
```

### files

#### `files.list` — List files

The project's files with their size, number of lines, version and whether they are generated (written by the tools: `.volt/…`). _(reads only)_

```ts
input: {
  // Only paths matching this pattern (`*`, `**`, `?`), e.g. `**/*.volt`.
  glob?: string | null
}
result: {
  // Size in bytes (UTF-8).
  bytes: integer
  // Written by the tools (`.volt/…`: routes, fills, placement, parts): change the design and
  // re-run the tool rather than editing it.
  generated: boolean
  // Lines of text.
  lines: integer
  // Project-relative path.
  path: string
  // Changes with every write: give it as `expect` to `files.write` / `files.edit`.
  version: integer
}[]
```

#### `files.read` — Read a file

A file's text (or a range of its lines, `from`/`to`, 1-based) and its version. Long files come in parts: at most 2000 lines per call (`partial`: read on from `to` + 1). _(reads only)_

```ts
input: {
  // First line (1-based, default 1).
  from?: integer | null
  // A project-relative path, e.g. `design.volt` or `blocks/power.volt`.
  path: string
  // Last line (inclusive; default: up to 2000 lines after `from`).
  to?: integer | null
}
result: {
  // The first line returned (1-based).
  from: integer
  // Written by the tools (`.volt/…`).
  generated: boolean
  // Lines of the whole file.
  lines: integer
  // The text is a part of the file (read on from `to` + 1).
  partial: boolean
  // The file (normalised).
  path: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The lines `from`..=`to`, with their line breaks.
  text: string
  // The last line returned (inclusive).
  to: integer
  // Give it as `expect` when writing, to be sure nobody changed the file since.
  version: integer
}
```

#### `files.write` — Write a file

Replace a file's whole text, creating it if needed (one undoable change; `null` when the text is the same). Give `expect` (the version you read) to make sure nobody changed it since. Generated files (`.volt/…`) need `generated: true`. For a few changes in a long file `files.edit` is safer. _(destructive)_

```ts
input: {
  // The version you read (`files.read`); the write fails if the file changed since. 0: the
  // file must not exist.
  expect?: integer | null
  // Allow writing a generated file.
  generated?: boolean
  // A project-relative path, e.g. `design.volt` or `blocks/power.volt`.
  path: string
  // The file's whole new text.
  text: string
}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
} | null
```

#### `files.edit` — Edit a file

Replace exact text in a file: each edit's `find` must occur exactly once (or set `all`); the edits apply in order, all or none. The minimal way for an agent to change a source; one undoable change (`null` when nothing changed).

```ts
input: {
  // Applied in order, each to the text the previous one left; all or none.
  edits: {
    // Replace every occurrence (otherwise `find` must occur exactly once).
    all?: boolean
    // Exact text to find (whitespace and line breaks included).
    find: string
    // What to put in its place (empty: delete it).
    replace: string
  }[]
  // The version you read (`files.read`); the edit fails if the file changed since.
  expect?: integer | null
  // Allow editing a generated file (`.volt/…`).
  generated?: boolean
  // A project-relative path, e.g. `design.volt` or `blocks/power.volt`.
  path: string
}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
} | null
```

#### `files.create` — New file

Create a file (a `conflict` error if it exists); undoable.

```ts
input: {
  // A project-relative path, e.g. `design.volt` or `blocks/power.volt`.
  path: string
  // Its text (default: empty; a `.volt` file gets the header `volt 1`).
  text?: string
}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
} | null
```

#### `files.rename` — Rename or move a file

Rename or move a file within the project (undoable; a `conflict` error if the new path exists). References to it in other files (`use "…"`) are not changed.

```ts
input: {
  // The file's path now.
  from: string
  // Its new path (must not exist).
  to: string
}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
} | null
```

#### `files.delete` — Delete a file

Delete a file (undoable). _(destructive)_

```ts
input: {
  // A project-relative path, e.g. `design.volt` or `blocks/power.volt`.
  path: string
}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
} | null
```

#### `files.search` — Search in files

Find text in the project's files (case-insensitive unless `case`): every match with its file, line and column. _(reads only)_

Shortcut: `Mod+Shift+F`.

```ts
input: {
  // Match case.
  case?: boolean
  // Only files matching this pattern (`*`, `**`, `?`), e.g. `**/*.volt`.
  glob?: string | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // The text to find (literal, not a pattern).
  query: string
}
result: {
  // This page's items.
  items: {
    // 1-based, in characters.
    column: integer
    // 1-based.
    line: integer
    // The file.
    path: string
    // The line (cut to 200 characters).
    text: string
  }[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

#### `files.format` — Format Volt

Format Volt files in the canonical layout (`volt fmt`; comments kept): one file, or every source that is not generated. Files with syntax errors are left as they are (`unparsable`). Undoable.

Shortcut: `Shift+Alt+F`.

```ts
input: {
  // One file; default: every Volt source that is not generated.
  path?: string | null
}
result: {
  // Files whose layout changed.
  changed: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null`: nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
  // Files with syntax errors (left as they are).
  unparsable: string[]
}
```

#### `files.sync` — Report external changes

The host reports files that changed outside the app (an editor, an agent in a terminal, `git checkout`), or loads the workspace (`replace`). Not undoable: git is the history of those. _(for the interface)_

```ts
input: {
  // The files that changed, with their new text.
  changes: ({
    // Project-relative path.
    path: string
    // The new text; absent or null: the file was deleted.
    text?: string | null
  })[]
  // These are all the files (loading a workspace): others are dropped, the history starts anew.
  replace?: boolean
}
result: {
  // Files that really changed (the same text is no change).
  changed: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### project

#### `project.summary` — Project summary

The board in a few lines: name, size, layers, counts, the check status and the first problems, the files, running jobs, and the journal's last number. _(reads only)_

```ts
input: {}
result: {
  // The board's outline, `[x0, y0, x1, y1]` mm.
  bounds: number[]
  // `{ components, nets, tracks, vias, zones, pours, errors, warnings, unrouted, unplaced }`.
  counts: any
  // The board's description.
  description: string
  // The first errors of the check (at most 10), as `report.get` gives them.
  errors: unknown[]
  // The evaluation and the check's counts.
  eval: {
    // The board's name (`Board { name }`; `board` when the design gives none), of the last good
    // evaluation.
    board?: string | null
    // Check errors.
    errors: integer
    // `ok`: the files evaluate; `error`: they do not (the last good evaluation is kept);
    // `empty`: no design yet.
    eval: string
    // The first evaluation errors (`file`, `line`, `column`, `message`).
    eval_errors: unknown[]
    // The good evaluation is older than the files.
    stale: boolean
    // The check status of the last good evaluation: `error`, `incomplete`, `complete`.
    status?: string | null
    // Nets not yet joined.
    unrouted: integer
    // The files' version this evaluation is of.
    version: integer
    // Check warnings.
    warnings: integer
  }
  // The project's files.
  files: string[]
  // Jobs still queued or running.
  jobs: ({
    // Who started it.
    author: string
    // Why it failed.
    error?: string | null
    // When it ended (`null`: still queued or running).
    finished?: number | null
    // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
    id: integer
    // `route`, `optimize`, `fill`, `import.kicad`, …
    kind: string
    // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
    progress: any
    // The result, when done.
    result: any
    // When it started (host time, ms since the epoch).
    started: number
    // `queued`, `running`, `done`, `failed` or `cancelled`.
    status: "queued" | "running" | "done" | "failed" | "cancelled"
  })[]
  // The journal's last number.
  journal: integer
  // Copper layers, top to bottom.
  layers: string[]
  // The board's name (empty: no design evaluates).
  name: string
  // `[width, height]` in mm.
  size: number[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `project.new` — New project

Start a new, empty board in this workspace (the skeleton `volt init` writes). Fails if there is a design already, unless `replace`.

```ts
input: {
  // The board's name.
  name: string
  // Replace the design that is there.
  replace?: boolean
}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
} | null
```

### model

#### `model.components` — List components

Components with their value, footprint, placement (mm, degrees, side) and courtyard bounds, in natural refdes order and in pages; filter by a refdes/value/footprint pattern (`*`, `?`; a plain word matches anywhere). _(reads only)_

```ts
input: {
  // A pattern (`*`, `?`) matched against the name (and for components the value and
  // footprint), e.g. `U*` or `*100n*`.
  filter?: string | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
}
result: {
  // This page's items.
  items: ({
    // `[x, y]` mm, absent when not placed.
    at?: number[] | null
    // The courtyard's bounds on the board, `[x0, y0, x1, y1]` mm (what it occupies: keep other
    // courtyards outside it).
    bounds?: number[] | null
    // The footprint's name.
    footprint: string
    // `comp:<refdes>`.
    id: string
    // Pads of its footprint.
    pins: integer
    // Degrees, counter-clockwise.
    rotation: number
    // `top` or `bottom`.
    side: string
    // Its value (`10k`, `100nF`, a part's name).
    value: string
  })[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

#### `model.nets` — List nets

Nets with their class, number of pins, whether they are routed, and their track length (mm), vias and layers, in pages; filter by a name pattern (`*`, `?`; a plain word matches anywhere). _(reads only)_

```ts
input: {
  // A pattern (`*`, `?`) matched against the name (and for components the value and
  // footprint), e.g. `U*` or `*100n*`.
  filter?: string | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
}
result: {
  // This page's items.
  items: {
    // Its net class.
    class: string
    // `net:<name>`.
    id: string
    // Copper layers its tracks use.
    layers: string[]
    // mm of track.
    length: number
    // Pins on it.
    pins: integer
    // `routed`, `unrouted` (islands not joined), or `single` (fewer than two pins).
    state: string
    // Vias on it.
    vias: integer
  }[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

#### `model.object` — Describe an object

Any object by id — `board`, `comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `via:5`, `zone:2` (a pour's fill), `pour:0`, `area:0` (a keepout or rule area), `class:power` (a net class), `block:power` (a block instance), `graphic:0` — what it is, where it is (mm), its net, and where the source declares it (`source`: file, line, column). _(reads only)_

```ts
input: {
  // The object's id: `board`, `comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `via:5`, `zone:2`,
  // `pour:0`, `area:0`, `class:power`, `block:power`, `graphic:0`.
  id: string
}
result: any
```

#### `model.tree` — Object tree

The design as a tree, as the interface's Board panel shows it: the board, the components grouped by the block instances that declare them (`block:power/regs[0]`), the nets, net classes, pours, keepouts and rule areas, and copper graphics, each with the id `view.select` and `model.object` take. Filter by a name pattern (`*`, `?`); long lists are cut at `limit` per group (`more` says how many were left out). _(reads only)_

```ts
input: {
  // Only objects whose name matches (`*`, `?`; case-insensitive), with the groups they are in.
  filter?: string | null
  // At most this many objects per group (default 1000).
  limit?: integer | null
}
result: {
  // The nodes under it (absent: none).
  children: any[]
  // The object's id (`board`, `comp:U1`, `block:power`, `net:GND`, …); groups: `group:components`, ….
  id: string
  // `board`, `group`, `block`, `component`, `net`, `class`, `pour`, `area`, `graphic`.
  kind: string
  // The name shown.
  label: string
  // A short second text (a value, a footprint, a net, a count).
  meta: string
  // Objects left out of this group (over `limit`).
  more: integer
}[]
```

### board

#### `board.pick` — Objects at a point

What lies at a board point (mm), nearest first: pads and vias, tracks, zones, components, as ids with their distance. The canvas uses this for clicks. _(reads only)_

```ts
input: {
  // Only on these layers (`F.Cu`, …); default every layer.
  layers?: string[]
  // At most this many hits (default 20, at most 200).
  limit?: integer | null
  // How far from the point (mm, default 0.1).
  radius?: number | null
  // The point's x (mm).
  x: number
  // The point's y (mm).
  y: number
}
result: {
  // mm from the point (0: on it).
  distance: number
  // The object's id (`pad:R1.2`, `track:3`, `comp:R1`, …).
  id: string
  // The layers it is on.
  layers: string[]
}[]
```

#### `board.region` — Objects in a region

The ids of the objects whose bounds meet (or, with `inside`, lie wholly inside) a box on the board (mm), in pages: the canvas's rubber-band selection. _(reads only)_

```ts
input: {
  // Only objects wholly inside.
  inside?: boolean
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // One corner's x (mm).
  x0: number
  // The opposite corner's x (mm).
  x1: number
  // One corner's y (mm).
  y0: number
  // The opposite corner's y (mm).
  y1: number
}
result: {
  // This page's items.
  items: string[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

### report

#### `report.get` — Check report

The whole check report: errors and warnings in pages (filter by `severity` or `code`; each with its location `at` (mm), its objects as `ids` and, for the electrical rules and blocks, its source line), the counts by code, the unrouted nets, the unplaced components and statistics. `check.run` gives the first problems only. _(reads only)_

```ts
input: {
  // Only issues of this code (`clearance`, `short`, `unrouted`, …).
  code?: string | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // `error` or `warning`; default both.
  severity?: "error" | "warning" | null
}
result: {
  // Issues by severity and code: `{ error: { clearance: 2 }, warning: { … } }`.
  counts: any
  // The evaluation and the check's counts.
  eval: {
    // The board's name (`Board { name }`; `board` when the design gives none), of the last good
    // evaluation.
    board?: string | null
    // Check errors.
    errors: integer
    // `ok`: the files evaluate; `error`: they do not (the last good evaluation is kept);
    // `empty`: no design yet.
    eval: string
    // The first evaluation errors (`file`, `line`, `column`, `message`).
    eval_errors: unknown[]
    // The good evaluation is older than the files.
    stale: boolean
    // The check status of the last good evaluation: `error`, `incomplete`, `complete`.
    status?: string | null
    // Nets not yet joined.
    unrouted: integer
    // The files' version this evaluation is of.
    version: integer
    // Check warnings.
    warnings: integer
  }
  // The issues matching the filters: `{ severity, code, message, at, items, ids, nets, file?, line?, column? }`.
  issues: {
    // This page's items.
    items: unknown[]
    // More items follow (call again with `offset` + the number returned).
    more: boolean
    // The index of this page's first item in the whole list.
    offset: integer
    // Items in the whole list (all pages).
    total: integer
  }
  // The check's statistics.
  stats: any
  // `error`, `incomplete` or `complete`.
  status: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Components without a position.
  unplaced: string[]
  // Unrouted nets (at most 50): `{ net, islands, unplacedPins }`.
  unrouted: unknown[]
}
```

### board

#### `board.stats` — Board statistics

Numbers about the board: outline area (less cutouts) and size, component area and density per side, copper per layer (track length and area, pour area, pads, coverage %), vias by size, nets routed and unrouted, the longest nets, the track widths in use and the rules' minimums. Areas in mm², lengths in mm. _(reads only)_

```ts
input: {}
result: {
  // mm²: the outline less its cutouts.
  area: number
  // The bottom side's, as `top`.
  bottom: {
    // Components on this side.
    components: integer
    // mm² of courtyards.
    courtyard_area: number
    // Courtyard area / board area, %.
    density: number
  }
  // Copper layers.
  copper_layers: integer
  // Copper per layer.
  layers: {
    // (tracks + pours + pads) / board area, % (approximate: overlaps count twice).
    coverage: number
    // The copper layer.
    layer: string
    // mm² of pads on this layer.
    pad_area: number
    // mm² of pour fill.
    pour_area: number
    // mm² (tracks as drawn, overlaps counted once per piece).
    track_area: number
    // mm.
    track_length: number
    // Tracks on it.
    tracks: integer
  }[]
  // The five nets with the most track: `{ net, length, vias }`.
  longest: unknown[]
  // Nets in all (with single-pin nets).
  nets: integer
  // mm of outline.
  perimeter: number
  // Nets of two or more pins that are joined.
  routed: integer
  // The rules' floors: `minClearance`, `minTrackWidth`, `copperToEdge` (mm).
  rules: any
  // `[width, height]` mm of the outline's bounding box.
  size: number[]
  // The check: `error`, `incomplete` or `complete`.
  status: string
  // The numbers in a sentence.
  summary: string
  // Board thickness (mm).
  thickness: number
  // The top side's components and how much of the board their courtyards take.
  top: {
    // Components on this side.
    components: integer
    // mm² of courtyards.
    courtyard_area: number
    // Courtyard area / board area, %.
    density: number
  }
  // mm of track on all layers.
  track_length: number
  // Track widths in use (mm) and how many tracks have each.
  track_widths: { [key: string]: integer }
  // Nets not yet joined.
  unrouted: integer
  // Vias by size, `"diameter/drill"` (mm) → count.
  via_sizes: { [key: string]: integer }
  // Vias in all.
  vias: integer
}
```

#### `board.unrouted` — Missing connections

Every connection still missing, as the shortest pin-to-pin links that would join each unrouted net's islands: the two pads, the gap (mm, centre to centre) and its midpoint `at`. Pass `ids` to `view.highlight` / `view.focus` to show one. _(reads only)_

```ts
input: {
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Only this net (`GND` or `net:GND`).
  net?: string | null
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
}
result: {
  // This page's items.
  items: {
    // The midpoint `[x, y]` mm.
    at: number[]
    // One pad to join (`pad:R1.2`).
    from: string
    // mm, pad centre to pad centre.
    gap: number
    // For `view.highlight` / `view.focus`.
    ids: string[]
    // The net's name.
    net: string
    // The other pad.
    to: string
  }[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // Unrouted nets.
  nets: integer
  // The index of this page's first item in the whole list.
  offset: integer
  // The missing connections in a sentence.
  summary: string
  // Items in the whole list (all pages).
  total: integer
  // Pins of unplaced components that cannot be joined yet.
  unplaced_pins: string[]
}
```

#### `board.measure` — Measure

Distance between two objects (ids such as `comp:R1`, `pad:U1.4`, `via:3`, `track:7`) or points (`[x, y]` mm): centre to centre, and for copper the edge-to-edge gap, the clearance the rules require between their nets and the margin (gap − required; negative: a violation). _(reads only)_

```ts
input: {
  // From: an object id (`comp:R1`, `pad:R1.2`, `via:3`, `track:7`) or a point `[x, y]` (mm).
  a: number[] | string
  // To: an object id or a point `[x, y]` (mm).
  b: number[] | string
}
result: {
  // `a`'s centre `[x, y]` (mm): a component's origin, a pad's centre, else the middle of its bounds.
  a_at: number[]
  // `b`'s centre (mm).
  b_at: number[]
  // Centre to centre (mm).
  distance: number
  // `b − a` along x (mm).
  dx: number
  // `b − a` along y (mm).
  dy: number
  // Copper edge to copper edge (mm), when both are copper; 0 when they touch.
  gap?: number | null
  // The layers the gap is on (where both have copper; empty: no layer in common).
  layers: string[]
  // gap − required (negative: closer than allowed).
  margin?: number | null
  // The clearance the rules require between their nets (mm; net classes, pairs and the rules' minimum).
  required?: number | null
  // Same net: no clearance applies.
  same_net: boolean
  // The measurement in a sentence.
  summary: string
}
```

### model

#### `model.bom` — Bill of materials

Components grouped into BOM lines by value, footprint, MPN and orderable code: quantity, refdes, fields; in pages. Filter by a pattern like `model.components`. `parts.bom` gives the priced BOM by chosen part. _(reads only)_

```ts
input: {
  // A pattern (`*`, `?`) matched against the name (and for components the value and
  // footprint), e.g. `U*` or `*100n*`.
  filter?: string | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
}
result: {
  // This page's items.
  items: ({
    // The orderable code: pinned in the design or chosen (`.volt/parts.volt`; `parts.bom` prices it).
    code?: string | null
    // Other fields, those every component of the line has with the same value.
    fields: { [key: string]: string }
    // The footprint's name.
    footprint: string
    // The `Manufacturer` field.
    manufacturer?: string | null
    // The `MPN` field.
    mpn?: string | null
    // Components on the line (per board).
    qty: integer
    // The refdes, in natural order.
    refs: string[]
    // The components' value.
    value: string
  })[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

### parts

#### `parts.search` — Search parts

Find parts and footprints by words (`gate driver bldc`, `stm32g4`, `esp32-c6`, `usb-c`, `qwiic`, `0603 resistor`, `qfn 48`): parts with pin tables (the registry's curated parts and this board's own, each with its status: `verified`, `checked` or `draft`), the standard library's parts (`cap`, `res`, `led`, `usbC`, `button`, `header`, …), the standard circuit blocks (`UsbC2`, `Ldo`, …) and the standard footprints. Each hit says how to use it; the summary points to the hosted footprint library (`library.search`) for the rest. A part that is not there: read its datasheet into a part sheet (`parts.sheet`, or `parts.sheet { from: id }` to start from a similar part) and `parts.create` it. _(reads only)_

```ts
input: {
  // Only this category: `mcu`, `gate-driver`, `passive`, `connector`, `footprint`, …
  category?: string | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // Words to find (`gate driver`, `stm32g4 48`, `qfn`); empty lists everything.
  query?: string
}
result: {
  // This page's items.
  items: ({
    // `mcu`, `gate-driver`, `passive`, `connector`, `footprint`, …
    category: string
    // What it is.
    description: string
    // Pass it to `parts.describe` / `parts.use`.
    id: string
    // `part` (a sheet with a pin table), `library` (a standard part function), `block` (a
    // standard circuit block, `blocks.describe`) or `footprint`.
    kind: string
    // Manufacturer part number (curated parts).
    mpn?: string | null
    // Parts: `registry` or `board` (this board's own).
    origin?: string | null
    // The package (curated parts).
    package?: string | null
    // Parts: `verified`, `checked` or `draft`.
    status?: string | string | string | null
    // How to use it in Volt.
    usage: string
    // The pin table is confirmed by an independent source.
    verified?: boolean | null
  })[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What was found, and where else to look.
  summary: string
  // Items in the whole list (all pages).
  total: integer
}
```

#### `parts.describe` — Describe a part

A part by id: its full pin table (number, name, type, the functions each pin can carry), footprint, datasheet, ratings, application circuit, notes, provenance and status, and its Volt module. `functions` filters the pins by what they can do (`TIM1_CH*`, `ADC`, `SPI`): to choose MCU pins. _(reads only)_

```ts
input: {
  // Only pins that can carry these functions: a pattern (`TIM1_CH*`, `ADC*`, `*SPI*`), or a word.
  functions?: string | null
  // The id from `parts.search` (or the MPN, or a catalog code).
  id: string
}
result: any
```

#### `parts.use` — Use a part

Put a part of the registry into the board (once; undoable): its module `parts/<Name>.volt` (and its sheet `parts/<Name>.json`), used by `design.volt` as `<Name>`. Returns the part to place, `<Name>.Part`: in `design.volt` `U1 = <Name>.Part { pins { … } }` (no `at`: the engine places it), connecting its pins by their logical names.

```ts
input: {
  // The id from `parts.search` (or the MPN).
  id: string
}
result: {
  // The module was added now (false: the board had it).
  added: boolean
  // What the check found (errors make a `draft`).
  findings: ({
    // `pins-missing`, `land-off-leads`, …
    code: string
    level: string | string
    message: string
  })[]
  // The module's name in the design.
  name: string
  // Numbered pads of the part (pins with several pads, like GND, count once per pad).
  pads: integer
  // What to place: `board.add { part: part }` (`<Name>.Part`).
  part: string
  // Logical pin names (for `pins { NAME: net }` and `board.connect`).
  pins: string[]
  // Datasheet pin names that are no Volt names → the logical name to write (`3V3` → `P3V3`).
  renamed: { [key: string]: string }
  // How far a part is checked.
  status: string | string | string
  // What happened and what to do next.
  summary: string
  // The undoable change (`null`: it was declared already).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `parts.sheet` — How to describe a part

The part sheet's JSON Schema, the guide to reading a datasheet into one (the pin table, the package drawing, the land pattern, ratings, the application circuit, the 3D body; conventions and the mistakes the checks catch) and an example; `only: "guide" | "schema" | "example"` for one of them. Read it before `parts.check` / `parts.create`. `from: id` instead returns that part's whole sheet as the starting point for a new one (a variant, or another part in the same package: e.g. a module of the same size with its own pinout). _(reads only)_

```ts
input: {
  // A part's id (`parts.search`): its whole sheet as the starting point for a new one — a
  // variant, or a part with the same package and another pinout (`ESP32_C3_MINI_1` for
  // another module of its size). Change `id`, `mpn`, `datasheet`, the pins and whatever else
  // differs, from the new part's datasheet.
  from?: string | null
  // Only this part of the answer: `guide`, `schema` or `example` (default: all three).
  only?: "guide" | "schema" | "example" | null
}
result: {
  // A complete small sheet.
  example?: any
  // How to read a datasheet into a sheet (Markdown).
  guide?: string | null
  // The part sheet's JSON Schema.
  schema?: any
  // With `from`: that part's sheet, the starting point for a new one.
  sheet?: any
  summary: string
}
```

#### `parts.check` — Check a part sheet

Judge a part sheet without writing anything: its pin table against the package, the drawing's dimensions against each other, the datasheet's land pattern against the leads and against the footprint we compute (IPC-7351B), the ratings; compile it and evaluate it. Returns the status (`draft` with errors, `checked`), every finding, the footprint (expression and pads) and the Volt module. Fix the sheet until there are no errors, then `parts.create`. _(reads only)_

```ts
input: {
  // The part sheet (`parts.sheet` gives its schema and the guide).
  sheet: {
    // The circuit the datasheet asks for around the part.
    application?: {
      notes?: string[]
      page?: string | null
      parts?: ({
        // The two (or more) things it connects: pin names of the part, or nets (`GND`, `VIN`).
        between?: string[]
        // `cap`, `res`, `inductor`, `diode`, `led`, `crystal`, `mosfet`, … (from the value if empty).
        kind?: string | null
        // What else it must meet (`25V X7R`, `1%`, `0402`).
        rating?: string | null
        // As in the datasheet's schematic (`C1`, `R_SENSE`).
        ref?: string
        // `100nF`, `10kohm`, `4.7uH`.
        value?: string
      })[]
    } | null
    // `mcu`, `gate-driver`, `regulator`, `sensor`, `interface`, `connector`, `resistor`, …
    category?: string
    // The datasheet: a link (`url`) or a string.
    datasheet?: {
      // The document's revision or date (`SLVSDJ3D, 2022-03`).
      revision?: string | null
      url?: string
    } | null
    // One line in our own words.
    description?: string
    // A footprint written in Volt, used instead of the one computed from `package`
    // (`qfn(40, size = (6mm, 6mm), ep = (4.6mm, 4.6mm))`, `fp.MyConnector`).
    footprint?: string | null
    // The registry id and the part's name in Volt (`DRV8323RS_RGZ`): letters, digits, `_`.
    // Empty: from the MPN.
    id?: string
    // Areas of the footprint other copper and parts must keep out of, from the datasheet's
    // layout guidelines: a module's antenna area and the clearance around it.
    keepouts?: ({
      // What must stay out: `tracks`, `vias`, `pours` (the default: no copper), `pads`,
      // `footprints` (other parts).
      forbid?: ("tracks" | "vias" | "pours" | "pads" | "footprints")[]
      // The copper layers: `all` (default), `top` (the part's side) or `bottom`.
      layers?: "all" | "top" | "bottom"
      // What it is (`antenna`).
      name?: string | null
      // Where the datasheet asks for it (`Figure 3-2, antenna keep-out`).
      page?: string | null
      // A polygon instead of the rectangle: `[[x, y], …]`.
      points?: number[][]
      x0?: number | null
      x1?: number | null
      y0?: number | null
      y1?: number | null
    })[]
    manufacturer?: string | null
    // A 3D model of the hosted library by id (`Connector_USB:USB_C_Receptacle_…`, from
    // `library.search { kind: "models" }`), drawn instead of the solids when it loads.
    model?: string | null
    // The manufacturer's part number this sheet was read for (`DRV8323RSRGZR`).
    mpn: string
    // Design notes in our own words (what each supply pin needs, layout advice).
    notes?: string[]
    // The assembler's catalog codes of this part.
    orderCodes?: string[]
    // The package: dimensions as the datasheet's drawing gives them, or only a name the
    // standard footprints know (`SOT-23-5`, `QFN-32(5x5)`). A string is its name.
    package?: {
      // A dimension: `nom`, or the `min`/`max` the drawing gives (any may be missing).
      ball?: number | (number | null)[] | {
        max?: number | null
        min?: number | null
        nom?: number | null
      }
      body?: {
        // Height above the board (`A`).
        a?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Along the rows (two-row packages), along X (four-sided).
        d?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Across the rows, leads excluded (two-row), along Y (four-sided).
        e?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
      } | null
      // Positions of a full row that have no pin (`[2, 4]`): the rest renumber in order.
      deleted?: integer[]
      exposedPad?: {
        // Along the rows (two-row packages, as `body.d`), along X (four-sided) (`D2`).
        d2?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Across the rows (two-row packages, as `body.e`), along Y (four-sided) (`E2`).
        e2?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Its pin number when it is a pin (default: one after the last pin).
        number?: string | null
      } | null
      family?: "tssop" | "msop" | "ssop" | "tqfp" | "dip" | "module" | "connector" | "other" | string | string | string | string | string | string | string | string | string | null
      // Ball grid: columns and rows, ball diameter.
      grid?: integer[] | null
      // The datasheet's own land pattern (recommended footprint), if it gives one.
      landPattern?: {
        pads: ({
          // A plated hole.
          drill?: number | null
          h: number
          number: string
          // `rect` (default), `round`, `oval`, `roundrect`.
          shape?: string | null
          // Size along X and Y.
          w: number
          x: number
          y: number
        })[]
        page?: string | null
      } | null
      lead?: {
        // Lead or terminal width (`b`).
        b?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Flat leads that leave the body straight, without the gull-wing bend (SOT-563, SOT-523,
        // SOT-723, SC-89): the land pattern takes the no-lead fillet goals. Those package names say
        // it on their own.
        flat?: boolean
        // Length of the foot on the board (`L`).
        l?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
      } | null
      // As the datasheet or the catalog names it (`VQFN-48 (RGZ)`, `SOT-23-5`, `QFN-32(5x5)`).
      name?: string | null
      // Where the dimensions are in the datasheet (`"p. 93"`).
      page?: string | null
      // Pins of the package, the exposed pad not counted.
      pins?: integer | null
      // Four-sided packages with unequal sides: pins on the left (and right) side, and on the
      // bottom (and top) side.
      pinsPerSide?: integer[] | null
      // Lead or pad pitch (`e`).
      pitch?: number | null
      // Lead tip to lead tip across the rows (`E`, `HE`); no-lead packages: none.
      span?: number | (number | null)[] | {
        max?: number | null
        min?: number | null
        nom?: number | null
      }
      // A dimension: `nom`, or the `min`/`max` the drawing gives (any may be missing).
      spanY?: number | (number | null)[] | {
        max?: number | null
        min?: number | null
        nom?: number | null
      }
    }
    // Every pad of the package that is a pin (the exposed pad too, when it is one).
    pins?: ({
      description?: string | null
      // Alternate functions (MCUs: `TIM1_CH1`, `USART2_TX`).
      functions?: string[]
      // As the datasheet names it (`nFAULT`, `PA0`, `VREF+`).
      name: string
      number: string
      // What a pin does electrically (the electrical rules check it).
      type?: "ground" | "input" | "output" | "passive" | "unspecified" | string | string | string | string | string | string | string | string
    })[]
    // The letters of the refdes (`U`, `Q`, `J`).
    prefix?: string
    // Where the facts come from and how far they are checked.
    provenance?: {
      // Who wrote the sheet (`agent:<name>`, a person, `pipeline`).
      by?: string | null
      footprint?: {
        license?: string | null
        note?: string | null
        notice?: string | null
        source?: string | null
        url?: string | null
        // Checked against its source by an independent reading or a structured manufacturer
        // source (set by the registry, never by a single reading).
        verified?: boolean
      }
      pins?: {
        license?: string | null
        note?: string | null
        notice?: string | null
        source?: string | null
        url?: string | null
        // Checked against its source by an independent reading or a structured manufacturer
        // source (set by the registry, never by a single reading).
        verified?: boolean
      }
    }
    ratings?: {
      absMax?: ({
        max?: number | null
        min?: number | null
        nom?: number | null
        // The pins it applies to, by name (`["VM", "VDRAIN"]`): the electrical rules use it.
        pins?: string[]
        // `V` (default), `A`, `°C`, `Hz`, …
        unit?: string | null
        what?: string
      })[]
      recommended?: ({
        max?: number | null
        min?: number | null
        nom?: number | null
        // The pins it applies to, by name (`["VM", "VDRAIN"]`): the electrical rules use it.
        pins?: string[]
        // `V` (default), `A`, `°C`, `Hz`, …
        unit?: string | null
        what?: string
      })[]
    }
    // The 3D body as solids in Volt, from the mechanical drawing (connectors, modules, odd
    // shapes): `[solid.shell((8.94mm, 7.3mm, 3.2mm), wall = 0.2mm, radius = 1.3mm), …]`. Without
    // it the 3D view draws the package from its dimensions.
    solids?: string | null
    tags?: string[]
    // Other part numbers with the same package and pins (reel sizes, temperature grades).
    variants?: string[]
  }
}
result: {
  errors: integer
  findings: ({
    // `pins-missing`, `land-off-leads`, …
    code: string
    level: string | string
    message: string
  })[]
  // The footprint's Volt expression, `null` when none could be made.
  footprint?: string | null
  // How it was made: `dimensions`, `name`, `land pattern`, `given`.
  footprint_from?: string | null
  // The largest distance between a pad of the datasheet's land pattern and ours, mm.
  land_deviation?: number | null
  // The largest difference in a pad's width or height between the datasheet's land pattern
  // and ours, mm.
  land_size_deviation?: number | null
  // The part's name in Volt and the module `parts.create` would write.
  name: string
  // The computed footprint's pads (centre and size, mm; pin 1 top left).
  pads: {
    h: number
    number: string
    w: number
    x: number
    y: number
  }[]
  // How far a part is checked.
  status: string | string | string
  summary: string
  volt: string
  warnings: integer
}
```

#### `parts.create` — Add a part from its datasheet

Add a part sheet to the board (undoable): checks it as `parts.check` does, writes `parts/<Name>.json` (the sheet) and `parts/<Name>.volt` (the part, its footprint, pin types with the ratings as electrical limits, its 3D body) and lets `design.volt` use it. A sheet with errors is written as a `draft` (say so to the person: not for fabrication) only with `draft: true`. Returns the part to place, `<Name>.Part`, and the pins renamed for Volt (`3V3` → `P3V3`). A part like one of the registry: start from its sheet, `parts.sheet { from: id }`.

```ts
input: {
  // Write it even with errors, as a `draft`.
  draft?: boolean
  // The part sheet (`parts.sheet` gives its schema and the guide).
  sheet: {
    // The circuit the datasheet asks for around the part.
    application?: {
      notes?: string[]
      page?: string | null
      parts?: ({
        // The two (or more) things it connects: pin names of the part, or nets (`GND`, `VIN`).
        between?: string[]
        // `cap`, `res`, `inductor`, `diode`, `led`, `crystal`, `mosfet`, … (from the value if empty).
        kind?: string | null
        // What else it must meet (`25V X7R`, `1%`, `0402`).
        rating?: string | null
        // As in the datasheet's schematic (`C1`, `R_SENSE`).
        ref?: string
        // `100nF`, `10kohm`, `4.7uH`.
        value?: string
      })[]
    } | null
    // `mcu`, `gate-driver`, `regulator`, `sensor`, `interface`, `connector`, `resistor`, …
    category?: string
    // The datasheet: a link (`url`) or a string.
    datasheet?: {
      // The document's revision or date (`SLVSDJ3D, 2022-03`).
      revision?: string | null
      url?: string
    } | null
    // One line in our own words.
    description?: string
    // A footprint written in Volt, used instead of the one computed from `package`
    // (`qfn(40, size = (6mm, 6mm), ep = (4.6mm, 4.6mm))`, `fp.MyConnector`).
    footprint?: string | null
    // The registry id and the part's name in Volt (`DRV8323RS_RGZ`): letters, digits, `_`.
    // Empty: from the MPN.
    id?: string
    // Areas of the footprint other copper and parts must keep out of, from the datasheet's
    // layout guidelines: a module's antenna area and the clearance around it.
    keepouts?: ({
      // What must stay out: `tracks`, `vias`, `pours` (the default: no copper), `pads`,
      // `footprints` (other parts).
      forbid?: ("tracks" | "vias" | "pours" | "pads" | "footprints")[]
      // The copper layers: `all` (default), `top` (the part's side) or `bottom`.
      layers?: "all" | "top" | "bottom"
      // What it is (`antenna`).
      name?: string | null
      // Where the datasheet asks for it (`Figure 3-2, antenna keep-out`).
      page?: string | null
      // A polygon instead of the rectangle: `[[x, y], …]`.
      points?: number[][]
      x0?: number | null
      x1?: number | null
      y0?: number | null
      y1?: number | null
    })[]
    manufacturer?: string | null
    // A 3D model of the hosted library by id (`Connector_USB:USB_C_Receptacle_…`, from
    // `library.search { kind: "models" }`), drawn instead of the solids when it loads.
    model?: string | null
    // The manufacturer's part number this sheet was read for (`DRV8323RSRGZR`).
    mpn: string
    // Design notes in our own words (what each supply pin needs, layout advice).
    notes?: string[]
    // The assembler's catalog codes of this part.
    orderCodes?: string[]
    // The package: dimensions as the datasheet's drawing gives them, or only a name the
    // standard footprints know (`SOT-23-5`, `QFN-32(5x5)`). A string is its name.
    package?: {
      // A dimension: `nom`, or the `min`/`max` the drawing gives (any may be missing).
      ball?: number | (number | null)[] | {
        max?: number | null
        min?: number | null
        nom?: number | null
      }
      body?: {
        // Height above the board (`A`).
        a?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Along the rows (two-row packages), along X (four-sided).
        d?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Across the rows, leads excluded (two-row), along Y (four-sided).
        e?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
      } | null
      // Positions of a full row that have no pin (`[2, 4]`): the rest renumber in order.
      deleted?: integer[]
      exposedPad?: {
        // Along the rows (two-row packages, as `body.d`), along X (four-sided) (`D2`).
        d2?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Across the rows (two-row packages, as `body.e`), along Y (four-sided) (`E2`).
        e2?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Its pin number when it is a pin (default: one after the last pin).
        number?: string | null
      } | null
      family?: "tssop" | "msop" | "ssop" | "tqfp" | "dip" | "module" | "connector" | "other" | string | string | string | string | string | string | string | string | string | null
      // Ball grid: columns and rows, ball diameter.
      grid?: integer[] | null
      // The datasheet's own land pattern (recommended footprint), if it gives one.
      landPattern?: {
        pads: ({
          // A plated hole.
          drill?: number | null
          h: number
          number: string
          // `rect` (default), `round`, `oval`, `roundrect`.
          shape?: string | null
          // Size along X and Y.
          w: number
          x: number
          y: number
        })[]
        page?: string | null
      } | null
      lead?: {
        // Lead or terminal width (`b`).
        b?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
        // Flat leads that leave the body straight, without the gull-wing bend (SOT-563, SOT-523,
        // SOT-723, SC-89): the land pattern takes the no-lead fillet goals. Those package names say
        // it on their own.
        flat?: boolean
        // Length of the foot on the board (`L`).
        l?: number | (number | null)[] | {
          max?: number | null
          min?: number | null
          nom?: number | null
        }
      } | null
      // As the datasheet or the catalog names it (`VQFN-48 (RGZ)`, `SOT-23-5`, `QFN-32(5x5)`).
      name?: string | null
      // Where the dimensions are in the datasheet (`"p. 93"`).
      page?: string | null
      // Pins of the package, the exposed pad not counted.
      pins?: integer | null
      // Four-sided packages with unequal sides: pins on the left (and right) side, and on the
      // bottom (and top) side.
      pinsPerSide?: integer[] | null
      // Lead or pad pitch (`e`).
      pitch?: number | null
      // Lead tip to lead tip across the rows (`E`, `HE`); no-lead packages: none.
      span?: number | (number | null)[] | {
        max?: number | null
        min?: number | null
        nom?: number | null
      }
      // A dimension: `nom`, or the `min`/`max` the drawing gives (any may be missing).
      spanY?: number | (number | null)[] | {
        max?: number | null
        min?: number | null
        nom?: number | null
      }
    }
    // Every pad of the package that is a pin (the exposed pad too, when it is one).
    pins?: ({
      description?: string | null
      // Alternate functions (MCUs: `TIM1_CH1`, `USART2_TX`).
      functions?: string[]
      // As the datasheet names it (`nFAULT`, `PA0`, `VREF+`).
      name: string
      number: string
      // What a pin does electrically (the electrical rules check it).
      type?: "ground" | "input" | "output" | "passive" | "unspecified" | string | string | string | string | string | string | string | string
    })[]
    // The letters of the refdes (`U`, `Q`, `J`).
    prefix?: string
    // Where the facts come from and how far they are checked.
    provenance?: {
      // Who wrote the sheet (`agent:<name>`, a person, `pipeline`).
      by?: string | null
      footprint?: {
        license?: string | null
        note?: string | null
        notice?: string | null
        source?: string | null
        url?: string | null
        // Checked against its source by an independent reading or a structured manufacturer
        // source (set by the registry, never by a single reading).
        verified?: boolean
      }
      pins?: {
        license?: string | null
        note?: string | null
        notice?: string | null
        source?: string | null
        url?: string | null
        // Checked against its source by an independent reading or a structured manufacturer
        // source (set by the registry, never by a single reading).
        verified?: boolean
      }
    }
    ratings?: {
      absMax?: ({
        max?: number | null
        min?: number | null
        nom?: number | null
        // The pins it applies to, by name (`["VM", "VDRAIN"]`): the electrical rules use it.
        pins?: string[]
        // `V` (default), `A`, `°C`, `Hz`, …
        unit?: string | null
        what?: string
      })[]
      recommended?: ({
        max?: number | null
        min?: number | null
        nom?: number | null
        // The pins it applies to, by name (`["VM", "VDRAIN"]`): the electrical rules use it.
        pins?: string[]
        // `V` (default), `A`, `°C`, `Hz`, …
        unit?: string | null
        what?: string
      })[]
    }
    // The 3D body as solids in Volt, from the mechanical drawing (connectors, modules, odd
    // shapes): `[solid.shell((8.94mm, 7.3mm, 3.2mm), wall = 0.2mm, radius = 1.3mm), …]`. Without
    // it the 3D view draws the package from its dimensions.
    solids?: string | null
    tags?: string[]
    // Other part numbers with the same package and pins (reel sizes, temperature grades).
    variants?: string[]
  }
}
result: {
  // The module was added now (false: the board had it).
  added: boolean
  // What the check found (errors make a `draft`).
  findings: ({
    // `pins-missing`, `land-off-leads`, …
    code: string
    level: string | string
    message: string
  })[]
  // The module's name in the design.
  name: string
  // Numbered pads of the part (pins with several pads, like GND, count once per pad).
  pads: integer
  // What to place: `board.add { part: part }` (`<Name>.Part`).
  part: string
  // Logical pin names (for `pins { NAME: net }` and `board.connect`).
  pins: string[]
  // Datasheet pin names that are no Volt names → the logical name to write (`3V3` → `P3V3`).
  renamed: { [key: string]: string }
  // How far a part is checked.
  status: string | string | string
  // What happened and what to do next.
  summary: string
  // The undoable change (`null`: it was declared already).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `parts.package` — Footprint of a package

The standard footprint for a package name as datasheets and catalogs write it (`SOT-23-5`, `SOIC-8`, `QFN-32(5x5)`, `LQFP-48(7x7)`, `SMA(DO-214AC)`, `0603`, `SMD,D6.3xL7.7mm`): its Volt expression and pads. `null` when the name alone is not enough (give the package's dimensions in a part sheet). _(reads only)_

```ts
input: {
  // The part's category where it decides (`capacitor`, `inductor`, `crystal`).
  category?: string
  // The package's name (`SOT-23-5`, `QFN-32(5x5)`, `0603`).
  name: string
}
result: {
  // What the name left open and was assumed.
  assumed: string[]
  // The footprint in Volt (`null`: not known by name).
  footprint?: string | null
  pads: {
    h: number
    number: string
    w: number
    x: number
    y: number
  }[]
  summary: string
}
```

### blocks

#### `blocks.list` — List circuit blocks

The standard library's circuit blocks (`Ldo`, `Buck`, `UsbC2`, `Crystal`, `I2cPullups`, …): proven sub-circuits that size their parts from parameters, place them (decoupling next to its pin) and type their pins for the electrical rules. Compose a board from them instead of drawing the circuit: `ldo = Ldo(vin = VBUS, vout = V3, gnd = GND) {}`. `query` filters by words, `category` by `power`, `usb`, `mcu`, `signal`. _(reads only)_

```ts
input: {
  // `power`, `usb`, `mcu` or `signal`.
  category?: "power" | "usb" | "mcu" | "signal" | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // Words to find in the name and documentation (`regulator`, `usb`, `pull-up`); empty: all.
  query?: string
}
result: {
  // This page's items.
  items: {
    // `power`, `usb`, `mcu` or `signal`.
    category: string
    // The block's name, e.g. `Ldo` (`blocks.describe` takes it).
    name: string
    // The other parameters (with defaults).
    optional: string[]
    // Its ports: the nets and pins it offers to the board.
    ports: string[]
    // The parameters it needs (nets, mostly).
    required: string[]
    // What it is, in a sentence.
    summary: string
    // How to use it: a call with nets for the required parameters.
    usage: string
  }[]
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

#### `blocks.describe` — Describe a circuit block

A block's documentation (what it does, the equations it sizes its parts by), its parameters (type, default, meaning), ports and computed values, and an evaluated example: its parts (by their path inside the block, nested blocks included: `power.J`, what `override <instance>.power.J.… = …` names) with their values, footprints and connections, the values computed (`args` sets parameters as Volt expressions: `{ "voltage": "5V", "current": "500mA" }`), its warnings and the electrical rules' findings about the block itself. _(reads only)_

```ts
input: {
  // Parameters for the example, as Volt expressions (`{ "voltage": "5V" }`); the required
  // ones get nets named after them (`vin` → `VIN`).
  args?: { [key: string]: string }
  // The block's name (`Ldo`).
  name: string
}
result: any
```

### lang

#### `lang.search` — Search the standard library

Find the standard library's functions, types, enums, blocks and values by words in their name and documentation (`rounded rectangle`, `connector`, `outline`, `diode`, `wireToBoard`): each hit with its kind, signature and the first sentence of its documentation, a name equal to the query first. Read one with `lang.describe`. _(reads only)_

```ts
input: {
  // `function`, `type`, `enum`, `block`, `value` or `builtin`.
  kind?: "function" | "type" | "enum" | "block" | "value" | "builtin" | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // Words to find in the names, signatures and documentation (`rounded rect`, `pin header`).
  query: string
}
result: any
```

#### `lang.describe` — Describe a standard library name

One name of the standard library (`rect`, `wireToBoard`, `diode`, `Board`, `Ldo`): its signature, parameters (type, default, meaning) or fields, its documentation and an example; for a function that makes a `Part` or a `Footprint`, it evaluated with its defaults (`args` sets parameters as Volt expressions: `{ "n": "4", "pitch": "1mm" }`): a part's pins by logical name with their pads, the footprint and its pads. An unknown name answers with the closest ones. _(reads only)_

```ts
input: {
  // Parameters of a function that makes a part, as Volt expressions (`{ "n": "4" }`): the part
  // is evaluated with them to list its pins.
  args?: { [key: string]: string }
  // The name (`rect`, `wireToBoard`, `Board`).
  name: string
}
result: any
```

### parts

#### `parts.select` — Choose parts

Choose an orderable part for every group of components from the catalog (what each part's `spec` asks: value within tolerance, package, ratings, dielectric, colour; or its `mpn`): basic parts first, then preferred extended, then extended; enough stock for the boards; reuse of parts the board already has; the lowest price. Writes `.volt/parts.volt` (undoable) with the reason for each choice. Choices whose need did not change are kept unless `all`. Pinned codes (`orderCode`) are taken as they are.

```ts
input: {
  // Choose every group anew (default: keep the recorded choices whose need did not change).
  all?: boolean
  // Boards to build (default: as recorded, else 5).
  boards?: integer | null
  // Stock must cover the pieces this many times (default 1.5).
  margin?: number | null
}
result: {
  // Boards the quantities are for.
  boards: integer
  // The catalog chosen from.
  catalog: string
  // One line per group.
  choices: ({
    // The orderable code (`null`: unresolved).
    code?: string | null
    // Kept from the record (its need did not change).
    kept: boolean
    // The chosen part's manufacturer part number.
    mpn?: string | null
    // What its part must meet, as a key (value, package, ratings).
    need: string
    // The code is pinned in the source (`orderCode`).
    pinned: boolean
    // Why this part (or why none).
    reason: string
    // The group's components.
    refs: string[]
    // Pieces in stock when chosen.
    stock?: integer | null
    // The assembler's library tier (basic parts cost no loading fee).
    tier?: "basic" | "preferred" | "extended" | null
    // USD per piece at the quantity needed.
    unit_price?: number | null
  })[]
  // Groups with a part.
  chosen: integer
  // The file written.
  file: string
  // Groups of components that need a part.
  groups: integer
  // Choices kept from the record.
  kept: integer
  // Groups whose part is not stocked enough.
  short: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change of `.volt/parts.volt` (`null`: unchanged).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
  // Groups without one (see their `reason`).
  unresolved: integer
}
```

#### `parts.alternatives` — Alternatives for a part

The orderable parts (from the offers loaded) that meet a component's need, ranked best first, each with why it ranks where it does: to compare, or to swap one in with `parts.pin`. Changes nothing. _(reads only)_

```ts
input: {
  // At most this many (default 10).
  limit?: integer
  // A component of the group (`C1` or `comp:C1`).
  ref: string
}
result: {
  // Ranked, best first.
  alternatives: ({
    // It is the choice.
    chosen: boolean
    code: string
    description?: string | null
    manufacturer?: string | null
    mpn?: string | null
    package?: string | null
    params: { [key: string]: string }
    stock?: integer | null
    // The assembler's library a part is in, which sets what assembling it costs: basic parts are
    // loaded on the machines already, preferred extended ones cost no loading fee on the economic
    // service, other extended parts are loaded for a fee per unique part.
    tier: "basic" | "preferred" | "extended"
    unitPrice?: number | null
    // Why it ranks after the choice (empty for the choice itself).
    whyNot: string
  })[]
  // The catalog searched (offers loaded with `parts.offers`).
  catalog: string
  // The recorded choice's code, if any.
  chosen?: string | null
  // What its part must meet, as a key.
  need: string
  // The code pinned in the source (`orderCode`), if any.
  pinned?: string | null
  // Why the first is first (or why there is none).
  reason: string
  // The components of the group.
  refs: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `parts.pin` — Pin a part

Pin components to an orderable code: writes `orderCode = "…"` into each component in the source (undoable; `code` empty removes it) and records the choice. `group` (default): every component of the same need as the first.

```ts
input: {
  // The orderable code; empty removes the pin.
  code: string
  // Pin every component of the first one's group (default true).
  group?: boolean
  // Components (`C1` or `comp:C1`).
  refs: string[]
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // Components now pinned in the source.
  pinned: string[]
  // The record after the change.
  selection?: {
    // Boards the quantities are for.
    boards: integer
    // The catalog chosen from.
    catalog: string
    // One line per group.
    choices: ({
      // The orderable code (`null`: unresolved).
      code?: string | null
      // Kept from the record (its need did not change).
      kept: boolean
      // The chosen part's manufacturer part number.
      mpn?: string | null
      // What its part must meet, as a key (value, package, ratings).
      need: string
      // The code is pinned in the source (`orderCode`).
      pinned: boolean
      // Why this part (or why none).
      reason: string
      // The group's components.
      refs: string[]
      // Pieces in stock when chosen.
      stock?: integer | null
      // The assembler's library tier (basic parts cost no loading fee).
      tier?: "basic" | "preferred" | "extended" | null
      // USD per piece at the quantity needed.
      unit_price?: number | null
    })[]
    // Groups with a part.
    chosen: integer
    // The file written.
    file: string
    // Groups of components that need a part.
    groups: integer
    // Choices kept from the record.
    kept: integer
    // Groups whose part is not stocked enough.
    short: integer
    // The undoable change of `.volt/parts.volt` (`null`: unchanged).
    transaction?: {
      // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
      // disk), `app`.
      author: string
      // The files it changed.
      files: string[]
      // 0: not recorded in the history.
      id: integer
      // What it did, e.g. `move R1`.
      label: string
      // When (host time, ms since the epoch).
      time: number
      // Recorded in the history: `history.undo` undoes it.
      undoable: boolean
      // Each changed file's version now (give it as `expect` to the next `files.write` /
      // `files.edit` of that file); empty for a change read back from the history.
      versions?: { [key: string]: integer }
    } | null
    // Groups without one (see their `reason`).
    unresolved: integer
  } | null
  // Components whose fields the source computes (inside a loop or block): give them
  // `orderCode` with `board.override`.
  skipped: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `parts.bom` — Priced bill of materials

The bill of materials by chosen part: refs, value, footprint, orderable code, MPN, tier, stock and price when chosen, the cost for `boards` boards; lines without a part say why. Reads `.volt/parts.volt`. _(reads only)_

```ts
input: {
  // Boards to price (default: as recorded).
  boards?: integer | null
}
result: {
  // Boards priced.
  boards: integer
  // The catalog the parts were chosen from.
  catalog: string
  // The day stock and prices are of.
  date: string
  // Unique extended parts (each costs a loading fee at assembly).
  extended: integer
  // One line per group of components.
  lines: ({
    // The orderable code (`null`: none yet, see `status`).
    code?: string | null
    description?: string | null
    // The footprint's name.
    footprint: string
    manufacturer?: string | null
    // Manufacturer part number.
    mpn?: string | null
    // The reason of the choice, or why there is none.
    note: string
    // The part's package.
    package?: string | null
    // The code is pinned in the source.
    pinned: boolean
    // Per board.
    qty: integer
    refs: string[]
    // `ok`, `short` (not enough stock when chosen), `stale` (the need changed), `unresolved`,
    // `missing` (not chosen yet), `pinned` (pinned, not looked up).
    status: string
    // Stock when chosen.
    stock?: integer | null
    // The assembler's library tier.
    tier?: "basic" | "preferred" | "extended" | null
    // USD for all boards.
    total?: number | null
    // USD per piece when chosen.
    unit_price?: number | null
    value: string
  })[]
  // Lines that need attention (no part, stale, short).
  open: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // USD for the priced lines, all boards.
  total: number
  // Distinct orderable codes.
  unique_parts: integer
  // Lines without a price.
  unpriced: integer
}
```

#### `parts.needs` — What parts are needed

Every group of assembled components with what its part must meet, and the volt API requests that fetch the offers for them (hosts fetch them and pass the answers to `parts.offers`). _(reads only)_

```ts
input: {
  // The API's address (default https://api.volt0.ai).
  api?: string | null
}
result: {
  // Each group of assembled components with what its part must meet.
  needs: ({
    // The footprint (for BOMs).
    footprint: string
    // What they need, in one line: the group's identity (`resistor 10kΩ ±1% 0603 ≥ 0.1W`,
    // `pinned T10001`, `mpn ABC-1`); a recorded choice is stale when it changes.
    key: string
    mpn?: string | null
    // The package key ([`package_key`]): the spec's, else the footprint's.
    package?: string | null
    // An orderable code the design pins.
    pinned?: string | null
    // The components, by refdes in natural order.
    refs: string[]
    // What an orderable part must meet (Volt's `Spec`, quantities as their texts: `"16V"`).
    spec?: {
      // `resistor`, `capacitor`, `inductor`, `ferriteBead`, `led`, `diode`, `transistor`,
      // `crystal`, `oscillator`, `fuse`, `connector`, `switch`, `regulator`, `ic`, `other`.
      category?: string | null
      color?: string | null
      current?: string | null
      // A ceramic capacitor's dielectric: this one or a more stable one.
      dielectric?: string | null
      // The catalog's package code (`null`: the footprint's).
      package?: string | null
      // Other parameters by the catalog's attribute name.
      params?: { [key: string]: {
        max?: string | null
        min?: string | null
      } | string | number | boolean }
      power?: string | null
      // The largest tolerance accepted (a share: `0.01`).
      tolerance?: number | null
      value?: {
        max?: string | null
        min?: string | null
      } | string | number | boolean | null
      // Ratings the part must reach at least.
      voltage?: string | null
    } | null
    // The value as printed.
    value?: string
  })[]
  // Offers loaded so far.
  offers: integer
  // `GET` these and pass each answer to `parts.offers`.
  requests: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `parts.offers` — Load catalog offers

Add orderable parts to the catalog the choice is made from (the volt API's answers or a snapshot's records: `{ code, mpn, manufacturer, package, category, description, params, stock, prices, library }`). Kept in memory for this session. _(for the interface)_

```ts
input: {
  // Offers (the API's `Part` records).
  offers?: ({
    // `First / Second` category names.
    category?: string | null
    // The assembler's part number: what the BOM orders.
    code: string
    datasheet?: string | null
    description?: string | null
    // `basic`, `preferred`, `extended` (`null`: not known).
    library?: "basic" | "preferred" | "extended" | null
    manufacturer?: string | null
    mpn?: string | null
    // The package as the catalog names it (`0603`, `SOT-23-5`, `LQFP-48(7x7)`).
    package?: string | null
    // Attribute name → value as the catalog writes it (`Resistance: 10kΩ`).
    params?: { [key: string]: string }
    prices?: ({
      from: integer
      price: number
      to?: integer | null
    })[]
    stock?: integer | null
    // When the catalog's record was read (unix seconds).
    updatedAt?: integer | null
  })[]
  // Drop the offers loaded before.
  replace?: boolean
  // Where they come from (`api.volt0.ai`, a snapshot's name).
  source?: string | null
}
result: {
  // Offers given now.
  added: integer
  // The catalog's name (its sources).
  catalog: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Offers in the catalog now.
  total: integer
}
```

### library

#### `library.search` — Search the component library

Find footprints (default) or 3D models in the hosted component library — KiCad's footprint and 3D model libraries (15,000 footprints, 7,000 models; CC-BY-SA 4.0 with the KiCad exception: designs that use them stay yours) — by words: `soic 8`, `usb c receptacle`, `0603 resistor`, `qfn 32 5x5`. Each hit is a `Lib:Name` id with its description, pad count and whether it has a 3D model. Use one with `library.footprint`; a 3D model by its id goes into a footprint's `models` as `Model3d { path = "Lib:Name" }`. _(reads only)_

```ts
input: {
  // `footprints` (default) or `models`.
  kind?: "footprints" | "models" | null
  // At most this many items (default 50, at most 500).
  limit?: integer
  // Skip this many items (default 0): pass the previous `offset` + the number of items it
  // returned to read the next page.
  offset?: integer
  // Words to find (in the id first, then tags and description).
  query: string
}
result: {
  // This page's items.
  items: ({
    // What it is.
    description: string
    // `Lib:Name`.
    id: string
    // Its 3D model (`<Lib>.3dshapes/<Name>`), if the library has one.
    model?: string | null
    // Distinct pad numbers (footprints).
    pads: integer
    // A model's size, mm (models).
    size?: number[] | null
    // Words the library tags it with.
    tags: string
    // Through-hole pads (else surface mount).
    through_hole: boolean
  })[]
  // The library searched, its release and licence.
  library: string
  // The library's licence.
  license: string
  // Its text.
  license_url: string
  // More items follow (call again with `offset` + the number returned).
  more: boolean
  // The index of this page's first item in the whole list.
  offset: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Items in the whole list (all pages).
  total: integer
}
```

#### `library.footprint` — Use a library footprint

Write a footprint of the hosted component library (`Lib:Name`, from `library.search`) into the project's `footprints.volt` (once; undoable) as KiCad draws it — pads, courtyard, silkscreen, its 3D model — with its source and licence, and let `design.volt` use that file. Returns the expression that places it: in design.volt `U1 = fp.SOIC_8_3_9x4_9mm_P1_27mm { pins { "1": … } }`, its pins by pad number.

```ts
input: {
  // `Lib:Name`, e.g. `Package_SO:SOIC-8_3.9x4.9mm_P1.27mm`.
  id: string
  // The name of its binding in `footprints.volt` (default: from the footprint's name).
  name?: string | null
}
result: {
  // Written now (else it was there).
  added: boolean
  // The library footprint (`Lib:Name`).
  id: string
  // Its licence.
  license: string
  // The licence's text.
  license_url: string
  // Its 3D model file, as the footprint names it.
  model?: string | null
  // Its binding in `footprints.volt`.
  name: string
  // Its pad numbers: connect pins by them (`pins { \"1\": GND }`).
  pads: string[]
  // What places it: `board.add`'s `part`.
  part: string
  // Where it comes from (library, release, id), as recorded in `footprints.volt`.
  source: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null`: nothing to write).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

### board

#### `board.move` — Move or rotate a component

Place a component: position (mm), rotation (degrees) and side; changes the literal in the source. A computed position (a formula, a block, a loop) gives choices (edit the formula, or add an override): call again with `choice`. Show the person: `view.select` and `view.focus` the component (in the same `batch.run`).

```ts
input: {
  // `[x, y]` in mm (board coordinates).
  at?: number[] | null
  // Apply this one of the `choices` a previous call offered (its `index`).
  choice?: integer | null
  // The component (`R1` or `comp:R1`).
  ref: string
  // Degrees, counter-clockwise.
  rotation?: number | null
  // `top` or `bottom` (flips the component to that side).
  side?: "top" | "bottom" | null
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

### place

#### `place.pin` — Pin the engine's placement

Write where the engine placed components (those the design gives no `at`) into the source as their `at` (and `rotation` when the engine chose it), so they stay there for good. `refs`: these components (default: every one the engine placed).

```ts
input: {
  // Components (`R1` or `comp:R1`); empty: every component the engine placed.
  refs?: string[]
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // Components now placed in the source.
  pinned: string[]
  // Components whose position the source computes (a loop, a block's shared value): move them
  // one by one with `board.move`, which offers the choices.
  skipped: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

### board

#### `board.connect` — Connect a pin

Connect a component's pin to a net (declared if new) in the component's `pins` in the source (undoable). A pin computed in the source (a block, a loop) gives `choices`: call again with `choice`.

```ts
input: {
  // Apply this one of the `choices` a previous call offered (its `index`).
  choice?: integer | null
  // The net's name (declared if new), e.g. `GND`.
  net: string
  // The pin (its number or logical name).
  pin: string
  // The component (`R1` or `comp:R1`).
  ref: string
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.disconnect` — Disconnect a pin

Remove a pin's net from the component's `pins` in the source (undoable); the pin is then unconnected.

```ts
input: {
  // Apply this one of the `choices` a previous call offered (its `index`).
  choice?: integer | null
  // The pin (its number or logical name).
  pin: string
  // The component (`R1` or `comp:R1`).
  ref: string
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.add` — Add a component

Add a component to the design: a name, what it is (a Volt expression: a part such as `cap(100nF, package = "0402")` or a declared part's name, e.g. one `parts.use` declared), and where (`[x, y]` mm; left out, the engine places it). Show the person: `view.select` and `view.focus` it.

```ts
input: {
  // `[x, y]` in mm; left out, the engine places it (`place.run`, `route.run`).
  at?: number[] | null
  // The new component's name (its refdes), e.g. `C5`.
  name: string
  // What it is: a Volt expression, e.g. `cap(100nF, package = "0402")` or `Sensor`.
  part: string
  // Degrees, counter-clockwise (default 0).
  rotation?: number | null
  // `top` (default) or `bottom`.
  side?: "top" | "bottom" | null
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.remove` — Remove a component

Remove a component's declaration from the source (undoable); its copper goes stale and is ripped up at the next `route.run`. A component a block or loop declares gives `choices`. _(destructive)_

```ts
input: {
  // Apply this one of the `choices` a previous call offered (its `index`).
  choice?: integer | null
  // The component (`R1` or `comp:R1`).
  ref: string
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.rename` — Rename

Rename a component, net or other declaration everywhere in the project's sources (undoable).

```ts
input: {
  // The current name (a component, net, part, block …; `comp:R1` and `net:GND` work too).
  name: string
  // The new name.
  to: string
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.set` — Set a field

Set a field of a component or other entity (by its name or path) to a Volt literal, e.g. `value` to `"10k"` or `rotation` to `90deg` (undoable). A computed value gives `choices`: call again with `choice`. For positions `board.move` is simpler.

```ts
input: {
  // Apply this one of the `choices` a previous call offered (its `index`).
  choice?: integer | null
  // The entity: a refdes, or a path like `channels[3]/R`.
  entity: string
  // The field, e.g. `value`, `rotation`, `at`, `fields.MPN`.
  field: string
  // A Volt literal, e.g. `"10k"`, `90deg`, `(1mm, 2mm)`, `.bottom`.
  value: string
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.fab` — Fabrication

How the board is made — material, thickness, solder mask and silkscreen colours, surface finish, copper weight, vias, gold fingers, edge plating, castellated holes, order mark, flex coverlay and stiffener — written into the board's `fab` in the design (undoable). Without input: the board's fabrication as it is.

```ts
input: {
  // Castellated (half) holes on the edge.
  castellated?: boolean | null
  // Flex coverlay colour: `yellow`, `black`, `white`.
  coverlay?: "yellow" | "black" | "white" | null
  // Plated board edges.
  edgePlating?: boolean | null
  // Surface finish: `haslLeadFree`, `hasl`, `enig`, `enepig`, `osp`, `immersionSilver`,
  // `immersionTin`, `hardGold`.
  finish?: "haslLeadFree" | "hasl" | "enig" | "enepig" | "osp" | "immersionSilver" | "immersionTin" | "hardGold" | null
  // Hard-gold edge connector fingers.
  goldFingers?: boolean | null
  // Copper thickness of the inner layers, mm.
  innerCopper?: number | null
  // Solder mask colour: `green`, `matteGreen`, `red`, `yellow`, `blue`, `purple`, `white`,
  // `black`, `matteBlack`; `none`: no solder mask (bare copper under the finish).
  mask?: "green" | "matteGreen" | "red" | "yellow" | "blue" | "purple" | "white" | "black" | "matteBlack" | "none" | null
  // `fr4`, `aluminium` or `flex`.
  material?: "fr4" | "aluminium" | "flex" | null
  // The fab's order number: `fab` (where the fab likes), `none`, `specified` (where the
  // silkscreen marks it), `barcode`.
  orderMark?: "fab" | "none" | "specified" | "barcode" | null
  // Copper thickness of the outer layers, mm (0.035 = 1 oz, 0.07 = 2 oz).
  outerCopper?: number | null
  // Silkscreen colour: `white`, `black`, `yellow`.
  silk?: "white" | "black" | "yellow" | null
  // Flex stiffener: `none`, `polyimide`, `fr4`, `steel`.
  stiffener?: "none" | "polyimide" | "fr4" | "steel" | null
  // Board thickness, mm.
  thickness?: number | null
  // Flex: a see-through (clear polyimide) substrate.
  transparentFlex?: boolean | null
  // Vias: `tented` (the mask covers them), `untented` (`rules.mask.viaOpenings`), `plugged`,
  // `epoxyFilled`, `copperFilled` (the board's `viaFill`; tented).
  vias?: "tented" | "untented" | "plugged" | "epoxyFilled" | "copperFilled" | null
}
result: {
  // The sources changed (false: nothing to change).
  applied: boolean
  // The board's fabrication now (after the edit, if the sources evaluate).
  fab: {
    // Castellated (half) holes on the edge.
    castellated?: boolean | null
    // Flex coverlay colour: `yellow`, `black`, `white`.
    coverlay?: "yellow" | "black" | "white" | null
    // Plated board edges.
    edgePlating?: boolean | null
    // Surface finish: `haslLeadFree`, `hasl`, `enig`, `enepig`, `osp`, `immersionSilver`,
    // `immersionTin`, `hardGold`.
    finish?: "haslLeadFree" | "hasl" | "enig" | "enepig" | "osp" | "immersionSilver" | "immersionTin" | "hardGold" | null
    // Hard-gold edge connector fingers.
    goldFingers?: boolean | null
    // Copper thickness of the inner layers, mm.
    innerCopper?: number | null
    // Solder mask colour: `green`, `matteGreen`, `red`, `yellow`, `blue`, `purple`, `white`,
    // `black`, `matteBlack`; `none`: no solder mask (bare copper under the finish).
    mask?: "green" | "matteGreen" | "red" | "yellow" | "blue" | "purple" | "white" | "black" | "matteBlack" | "none" | null
    // `fr4`, `aluminium` or `flex`.
    material?: "fr4" | "aluminium" | "flex" | null
    // The fab's order number: `fab` (where the fab likes), `none`, `specified` (where the
    // silkscreen marks it), `barcode`.
    orderMark?: "fab" | "none" | "specified" | "barcode" | null
    // Copper thickness of the outer layers, mm (0.035 = 1 oz, 0.07 = 2 oz).
    outerCopper?: number | null
    // Silkscreen colour: `white`, `black`, `yellow`.
    silk?: "white" | "black" | "yellow" | null
    // Flex stiffener: `none`, `polyimide`, `fr4`, `steel`.
    stiffener?: "none" | "polyimide" | "fr4" | "steel" | null
    // Board thickness, mm.
    thickness?: number | null
    // Flex: a see-through (clear polyimide) substrate.
    transparentFlex?: boolean | null
    // Vias: `tented` (the mask covers them), `untented` (`rules.mask.viaOpenings`), `plugged`,
    // `epoxyFilled`, `copperFilled` (the board's `viaFill`; tented).
    vias?: "tented" | "untented" | "plugged" | "epoxyFilled" | "copperFilled" | null
  }
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

#### `board.override` — Override a field

Add (or replace) an `override` in the design for a field of an entity a block or loop declares, e.g. `entity: "channels[3]/R", field: "at"`: what `board.move` and `board.set` offer as a choice for computed values. Undoable.

```ts
input: {
  // The entity's path, e.g. `channels[3]/R` or `power/ldo`.
  entity: string
  // The field, e.g. `at`, `value`, `rotation`.
  field: string
  // A Volt literal, e.g. `(10mm, 5mm)`, `"10k"`, `90deg`.
  value: string
}
result: {
  // The source was changed (false: choose one of `choices`).
  applied: boolean
  // When the value is computed in the source (a formula, a loop, a block): the ways to change
  // it; call again with `choice`.
  choices: {
    // What it changes in the source.
    description: string
    // Pass it as `choice` to apply it.
    index: integer
    // What it does, in a few words.
    title: string
  }[]
  // The files changed.
  files: string[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The undoable change (`null` when nothing changed).
  transaction?: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  } | null
}
```

### model

#### `model.query` — Query by selector

Objects a selector matches — the selector language of design rules (`copper(net = GND) and ofComponent("U*")`, `kicad("A.NetClass == 'Power'")`) — with stable ids (at most `limit`). _(reads only)_

```ts
input: {
  // At most this many matches (default 100, at most 1000).
  limit?: integer | null
  // A selector in Volt (`copper(net = GND)`, `ofComponent("U*") and copper(on = .outer)`,
  // `kicad("A.Type == 'Via'")`).
  select: string
}
result: any
```

### check

#### `check.run` — Check

Evaluate the sources and check the board (design rules, electrical rules, placement rules, connectivity): status (`error`: rule violations or sources that do not evaluate, `incomplete`: unrouted nets or unplaced parts, `complete`), counts and the first 20 problems of each severity (`report.get` pages through all of them). Changes nothing. _(reads only)_

Shortcut: `Shift+C`.

```ts
input: {}
result: {
  // The first 20 errors: `{ code, message, at: [x, y] mm, items, ids, file?, line?, column? }`
  // (`ids` for `view.highlight` / `view.focus`; `report.get` pages through all of them).
  errors: unknown[]
  // The evaluation and the check's counts.
  eval: {
    // The board's name (`Board { name }`; `board` when the design gives none), of the last good
    // evaluation.
    board?: string | null
    // Check errors.
    errors: integer
    // `ok`: the files evaluate; `error`: they do not (the last good evaluation is kept);
    // `empty`: no design yet.
    eval: string
    // The first evaluation errors (`file`, `line`, `column`, `message`).
    eval_errors: unknown[]
    // The good evaluation is older than the files.
    stale: boolean
    // The check status of the last good evaluation: `error`, `incomplete`, `complete`.
    status?: string | null
    // Nets not yet joined.
    unrouted: integer
    // The files' version this evaluation is of.
    version: integer
    // Check warnings.
    warnings: integer
  }
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The first 20 warnings, as `errors`.
  warnings: unknown[]
}
```

### route

#### `route.plan` — Plan the routing

Estimate the routing before routing it, in milliseconds: a global route of every connection over tiles of about 1 mm with the layers' capacity. `overflow` 0 means it fits; otherwise `hotspots` names the crowded places and the nets through them (move parts apart there, turn them, or add layers: `layers` estimates another layer list, e.g. ["F.Cu", "In1.Cu", "In2.Cu", "B.Cu"]). _(reads only)_

```ts
input: {
  // Routing layers to estimate with (default: the design's).
  layers?: string[]
}
result: {
  // Connections estimated (nets with a pour join it by vias and are left out).
  connections: integer
  // The most crowded tiles, worst first: `at` (mm), `overflow`, the `nets` through them.
  hotspots: unknown[]
  // The layers estimated with.
  layers: string[]
  // Length of the global routes (mm).
  length: number
  // Tracks over capacity summed over every tile boundary: 0 fits.
  overflow: number
  // Boundaries over capacity.
  overflowed_edges: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Tile size (mm).
  tile: number
  // Vias the global routes need.
  vias: integer
}
```

#### `route.diagnose` — Why a net fails

Why a net's connections are missing and one thing that would help, in a few seconds: the net is routed alone, through the other nets' copper at a price, and at the minimum width. `cause`: `blocked` (alone it routes: `in_the_way` names the nets whose copper stops it), `too_wide` (its class is too wide for the pads in `unreachable`), `enclosed` (even alone and narrow it cannot reach them: `crowding` names the parts too close, with the gap in mm), or `complete`; `action` says what to do (move a part by about a millimetre, re-route together, a narrower class, 4 layers). Changes nothing. _(reads only)_

```ts
input: {
  // The net whose connections are missing (`board.unrouted` lists them).
  net: string
}
result: any
```

#### `route.run` — Route

Route the unrouted nets and re-route stale copper (`all`: rip up and re-route everything); places the components the design gives no `at` first, and refills the pours. Starts a job and answers with it (`wait_ms` waits for it in the same call, else `jobs.wait`); the routes are written to `.volt/routes.volt` and `fills.volt` as one undoable change by `router`. `time_limit` (s) bounds the work: 10 gives a first result quickly on a small board. `build.run` also re-places parts while connections are missing. _(starts a job)_

Shortcut: `Mod+Enter`.

```ts
input: {
  // Rip up and re-route everything.
  all?: boolean
  // The router's budget in seconds, instead of the design's `routing.timeLimit` (60 s by
  // default; in the browser the budget is work, about one native core-second each, so it
  // takes longer by the clock). A small budget (5–15) gives a first result fast.
  time_limit?: number | null
  // Wait up to this long (ms, at most 30000) for the job to end: the answer is then the ended
  // job with its result, or the running one with `pending` (wait on with `jobs.wait`).
  wait_ms?: integer | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### build

#### `build.run` — Build the board

From the design to a routed board in one job: place what the engine places (recorded positions are kept unless `all`), route everything, and while connections are missing place the engine's parts anew with more room and route again (`rounds`, default 3); the best result is written (routes, fills, placement) as one undoable change. `time_limit` (s) bounds the whole build (the rounds share it); `wait_ms` waits for the job in the same call. _(starts a job)_

```ts
input: {
  // Place every component the engine places anew, recorded or not.
  all?: boolean
  // Rounds of placing with more room and routing again while connections are missing (1–6).
  rounds?: integer | null
  // The whole build's budget in seconds, shared by its rounds (default: the design's `routing.timeLimit`).
  time_limit?: number | null
  // Wait up to this long (ms, at most 30000) for the job to end (as `route.run`'s).
  wait_ms?: integer | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### place

#### `place.run` — Place

Place the components the design gives no `at` (and size an automatic outline): recorded positions are kept, the others chosen near what they connect to and by the design's placement rules (`all`: place every such component anew). Starts a job (`wait_ms` waits for it in the same call); the positions are written to `.volt/place.volt` as one undoable change (`place.pin` writes them into the source). _(starts a job)_

```ts
input: {
  // Place every component the engine places anew, recorded or not.
  all?: boolean
  // Wait up to this long (ms, at most 30000) for the job to end (as `route.run`'s).
  wait_ms?: integer | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### fill

#### `fill.run` — Fill pours

Refill the pours around the current copper (`.volt/fills.volt`, one undoable change); routing refills them too. Starts a job (`wait_ms` waits for it in the same call). _(starts a job)_

```ts
input: {
  // Wait up to this long (ms, at most 30000) for the job to end (as `route.run`'s).
  wait_ms?: integer | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### optimize

#### `optimize.run` — Propose placements

Search placements within each component's `freedom` that route better. Starts a job; the proposals are in the job's result (`jobs.wait`): nothing is edited. _(starts a job)_

```ts
input: {
  // Steps of the search (default 5, 1–100).
  steps?: integer | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### import

#### `import.kicad` — Import a KiCad board

Make this workspace's project from a KiCad board (`.kicad_pcb` text of KiCad 4–10, with its `.kicad_pro` and `.kicad_dru` if present): design, footprints, routes and fills. Replaces the files of the workspace (undoable). Starts a job (`jobs.wait`). _(destructive, starts a job)_

```ts
input: {
  // The `.kicad_dru` text (custom rules).
  dru?: string | null
  // The board's name (default `board`).
  name?: string | null
  // Leave tracks, vias and fills out (placement only).
  no_routes?: boolean
  // The `.kicad_pcb` text.
  pcb: string
  // The `.kicad_pro` text (net classes, board setup).
  pro?: string | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `import.json` — Import a design as JSON

Make this workspace's project from the model's JSON (`volt show`'s output, e.g. of an older version). Replaces the files of the workspace (undoable). Starts a job (`jobs.wait`). _(destructive, starts a job)_

```ts
input: {
  // The design (or `volt show`'s whole output, with `layout`).
  design: {}
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `import.srj` — Import a routing problem

Make this workspace's project from a Simple Route JSON problem (obstacles and connections). Replaces the files of the workspace (undoable). Starts a job (`jobs.wait`). _(destructive, starts a job)_

```ts
input: {
  // The problem's JSON text.
  problem: string
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### export

#### `export.kicad` — Export to KiCad

The board as KiCad files (`.kicad_pcb`, `.kicad_pro`, `.kicad_dru` with custom rules, the pin assignment table). Changes nothing: gives an output (`output` id) that `workspace.download` offers the person in the web app; the CLI writes it to a directory. _(reads only)_

```ts
input: {}
result: {
  // The files in it: `[path, bytes]`.
  files: [string, integer][]
  // The output's name (the download's, without extension).
  name: string
  // The output's id: the app offers it as a download (the command-line tool writes it).
  output: string
  // The board's check status when exported: `error`, `incomplete` or `complete`.
  status?: string | null
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `export.repository` — Download as a board repository

Every file of the project plus the files every board repository has (AGENTS.md, FORMAT.md, the check hook): what `volt init` writes, ready for git and the command-line tool. Gives an output (`output` id) that `workspace.download` offers as a zip. _(reads only)_

Shortcut: `Mod+Shift+S`.

```ts
input: {}
result: {
  // The files in it: `[path, bytes]`.
  files: [string, integer][]
  // The output's name (the download's, without extension).
  name: string
  // The output's id: the app offers it as a download (the command-line tool writes it).
  output: string
  // The board's check status when exported: `error`, `incomplete` or `complete`.
  status?: string | null
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `export.fab` — Fabrication files

Everything a fab and an assembly service need, as one zip: Gerber X2 per layer (copper, solder mask, silkscreen, paste, outline), Excellon drill files (plated and non-plated), the bill of materials and the placement list (CSV), and a summary for the order form (`README.md`). `<name>-gerbers.zip` inside is what to upload to the fab. Refuses a board whose check is not `complete`, or whose assembled components have no orderable part (`parts.select`), unless `force`. `only` gives one part instead: `gerbers` (the Gerber and drill archive), `bom`, `cpl`. Gives an output (`output` id, for `workspace.download`) and `fab`, what the files hold. _(reads only)_

```ts
input: {
  // Export a board whose check is not `complete` (unrouted nets, errors); the summary says so.
  force?: boolean
  // One part only: `gerbers` (the Gerber and drill archive for the fab), `bom` (bill of
  // materials), `cpl` (placement list). Default: everything in one zip.
  only?: "gerbers" | "bom" | "cpl" | null
}
result: {
  // What the files hold: layers, size, smallest track, gap and hole, holes, parts, warnings.
  fab: any
  // The files in it: `[path, bytes]`.
  files: [string, integer][]
  // The output's name (the download's, without extension).
  name: string
  // The output's id: the app offers it as a download (the command-line tool writes it).
  output: string
  // The board's check status when exported: `error`, `incomplete` or `complete`.
  status?: string | null
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `export.sources` — Release the sources

The project's Volt sources (`.volt` files) as released for fabrication; refuses unless the check is `complete`. Gives an output (`output` id, for `workspace.download`). _(reads only)_

```ts
input: {}
result: {
  // The files in it: `[path, bytes]`.
  files: [string, integer][]
  // The output's name (the download's, without extension).
  name: string
  // The output's id: the app offers it as a download (the command-line tool writes it).
  output: string
  // The board's check status when exported: `error`, `incomplete` or `complete`.
  status?: string | null
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### history

#### `history.list` — History

The recent changes, newest first: who made each (the person, which agent, the router), what, which files; and what can be redone. _(reads only)_

```ts
input: {
  // At most this many changes of each list (default 50, at most 200).
  limit?: integer | null
}
result: {
  // Changes that can be undone, newest first (`history.undo` undoes the first).
  done: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  }[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Undone changes that can be redone, next first.
  undone: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The files it changed.
    files: string[]
    // 0: not recorded in the history.
    id: integer
    // What it did, e.g. `move R1`.
    label: string
    // When (host time, ms since the epoch).
    time: number
    // Recorded in the history: `history.undo` undoes it.
    undoable: boolean
    // Each changed file's version now (give it as `expect` to the next `files.write` /
    // `files.edit` of that file); empty for a change read back from the history.
    versions?: { [key: string]: integer }
  }[]
}
```

#### `history.undo` — Undo

Undo the latest change (whoever made it). Refuses, saying why, if a file changed since outside the app.

Shortcut: `Mod+Z`.

```ts
input: {}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
}
```

#### `history.redo` — Redo

Redo the change undone last.

Shortcut: `Mod+Shift+Z`.

```ts
input: {}
result: {
  // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
  // disk), `app`.
  author: string
  // The files it changed.
  files: string[]
  // 0: not recorded in the history.
  id: integer
  // What it did, e.g. `move R1`.
  label: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // When (host time, ms since the epoch).
  time: number
  // Recorded in the history: `history.undo` undoes it.
  undoable: boolean
  // Each changed file's version now (give it as `expect` to the next `files.write` /
  // `files.edit` of that file); empty for a change read back from the history.
  versions?: { [key: string]: integer }
}
```

### journal

#### `journal.since` — What changed since

A summary of everything since a journal number: changes by author and kind, files changed and by whom, the latest evaluation, selection and view, and the most recent events. The call for an agent starting its turn. _(reads only)_

```ts
input: {
  // Recent events to include (default 20).
  limit?: integer | null
  // The journal number you read up to (0: from the start).
  seq?: integer
}
result: {
  // Events by author, then by kind.
  counts: { [key: string]: { [key: string]: integer } }
  // Files changed, with who changed them.
  files: { [key: string]: string[] }
  // The journal's last number: read after it next time.
  last: integer
  // The latest evaluation result, selection and view, if they changed.
  latest: { [key: string]: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The details (kind-specific: the files changed, the selection, the job, …).
    data: any
    // `files.changed`, `files.external`, `eval`, `selection`, `view`, `editor`, `job.started`,
    // `job.progress`, `job.finished`, `history.undo`, `history.redo`, `agent`, `comment.added`,
    // `comment.replied`, `comment.status`, `comment.edited`, `comment.removed` (whoever changed
    // `comments.volt`, by a command or by editing the file), `reload` (the web app's page loaded
    // again: page globals are gone), …
    kind: string
    // Its journal number (increasing): read on after it.
    seq: integer
    // One line for people and agents.
    summary: string
    // Host time, ms since the epoch.
    time: number
  } }
  // The most recent events (at most `limit`), oldest first.
  recent: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The details (kind-specific: the files changed, the selection, the job, …).
    data: any
    // `files.changed`, `files.external`, `eval`, `selection`, `view`, `editor`, `job.started`,
    // `job.progress`, `job.finished`, `history.undo`, `history.redo`, `agent`, `comment.added`,
    // `comment.replied`, `comment.status`, `comment.edited`, `comment.removed` (whoever changed
    // `comments.volt`, by a command or by editing the file), `reload` (the web app's page loaded
    // again: page globals are gone), …
    kind: string
    // Its journal number (increasing): read on after it.
    seq: integer
    // One line for people and agents.
    summary: string
    // Host time, ms since the epoch.
    time: number
  }[]
  // The number asked about.
  since: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Events after `since` were dropped (the journal is bounded): re-read the state.
  truncated: boolean
}
```

#### `journal.next` — Next events

Events after a journal number, optionally of some kinds (`files`, `eval`, `selection`, `view`, `editor`, `job`, `agent`, `history`, `comment`: the person's comments on the board). With `wait_ms` the call waits (up to 30 s) until one arrives. _(reads only)_

```ts
input: {
  // Events after this number.
  after: integer
  // Kinds or kind prefixes (`files`, `eval`, `selection`, `view`, `editor`, `job`, `agent`,
  // `history`, `comment`, `route`, `workspace`); default all.
  kinds?: string[]
  // At most this many events (default 100, at most 1000).
  limit?: integer | null
  // Wait this long for an event (ms, at most 30000; the host waits).
  wait_ms?: integer | null
}
result: {
  // The events, oldest first.
  events: {
    // Who made it: `user` (the person), `agent:<name>`, `router` (a job), `external` (a change on
    // disk), `app`.
    author: string
    // The details (kind-specific: the files changed, the selection, the job, …).
    data: any
    // `files.changed`, `files.external`, `eval`, `selection`, `view`, `editor`, `job.started`,
    // `job.progress`, `job.finished`, `history.undo`, `history.redo`, `agent`, `comment.added`,
    // `comment.replied`, `comment.status`, `comment.edited`, `comment.removed` (whoever changed
    // `comments.volt`, by a command or by editing the file), `reload` (the web app's page loaded
    // again: page globals are gone), …
    kind: string
    // Its journal number (increasing): read on after it.
    seq: integer
    // One line for people and agents.
    summary: string
    // Host time, ms since the epoch.
    time: number
  }[]
  // The last number in the journal (read after it next time).
  last: integer
  // More events matched than were returned (read again after the last returned one).
  more: boolean
  // Nothing yet: the host waits and asks again (agents never see this set when they gave a timeout, unless it ran out).
  pending: boolean
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Events after the requested number were dropped (the history is bounded): re-read the state.
  truncated: boolean
}
```

#### `journal.wait` — Wait for

Wait (up to `wait_ms`, at most 30 s) until a condition holds: a job ended (`job`), the person selected something (`selection`), the board evaluated without errors (`eval_ok`), the check is complete (`complete`), or any file changed (`change`). _(reads only)_

```ts
input: {
  // Count only events after this journal number (for `selection` and `change`; default: now).
  after?: integer | null
  // `job` (the job `job` ended), `selection` (the person selected something), `eval_ok` (the
  // sources evaluate without errors), `complete` (the check is `complete`) or `change` (a file
  // changed).
  for: "job" | "selection" | "eval_ok" | "complete" | "change"
  // The job (for `job`).
  job?: integer | null
  // Wait this long for the condition (ms, at most 30000; the host waits). 0 or absent: answer
  // at once.
  wait_ms?: integer | null
}
result: {
  // What holds (the job, the selection, the evaluation).
  detail: any
  // The condition holds.
  done: boolean
  // The journal's last number.
  journal: integer
  // Not yet: the host waits and asks again.
  pending: boolean
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### jobs

#### `jobs.list` — Jobs

Running and recent jobs with their status and progress. _(reads only)_

```ts
input: {}
result: ({
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
})[]
```

#### `jobs.status` — Job status

One job's status, progress and result (`jobs.wait` waits for its end). _(reads only)_

```ts
input: {
  // The job's id (from the command that started it, or `jobs.list`).
  id: integer
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `jobs.wait` — Wait for a job

Wait until a job ends (up to `wait_ms`, at most 30 s); returns the job with `pending` if it still runs. _(reads only)_

```ts
input: {
  // The job's id (from the command that started it, or `jobs.list`).
  id: integer
  // Wait this long for the job to end (ms, at most 30000; the host waits). 0 or absent:
  // answer at once.
  wait_ms?: integer | null
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The job still runs after the wait: call `jobs.wait` again.
  pending: boolean
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `jobs.cancel` — Cancel a job

Cancel a queued or running job; nothing it would have written is written.

```ts
input: {
  // The job's id (from the command that started it, or `jobs.list`).
  id: integer
}
result: {
  // Who started it.
  author: string
  // Why it failed.
  error?: string | null
  // When it ended (`null`: still queued or running).
  finished?: number | null
  // Pass it to `jobs.wait`, `jobs.status`, `jobs.cancel`.
  id: integer
  // `route`, `optimize`, `fill`, `import.kicad`, …
  kind: string
  // The latest progress report (kind-specific: e.g. `{ "phase": "negotiate", "routed": 12, "total": 40 }`).
  progress: any
  // The result, when done.
  result: any
  // When it started (host time, ms since the epoch).
  started: number
  // `queued`, `running`, `done`, `failed` or `cancelled`.
  status: "queued" | "running" | "done" | "failed" | "cancelled"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

### view

#### `view.state` — What the person sees

The open view (board 2D/3D, code), camera, visible layers, selection and hover (described), highlight, the issues in view, the editor's file, cursor and diagnostics there, open panels, and the agents at work. _(reads only)_

```ts
input: {}
result: {
  // The agents connected, with their status and focus.
  agents: ({
    // What it is looking at or touching (ids), shown like a collaborator's cursor.
    focus: string[]
    // Its last call (host time, ms since the epoch).
    last_seen: number
    // The name it gave (`agent.hello`); its calls are by `agent:<name>`.
    name: string
    // What it says it is doing (`agent.status`).
    status: string
    // Whose tool it is (`anthropic`, `openai`, `google`, `cursor`, `github`), from `agent.hello`
    // (stated, or recognised in its name); the interface draws the maker's mark as its avatar.
    vendor?: string | null
  })[]
  // Diagnostics of the editor's active file (at most 50).
  diagnostics: unknown[]
  // The editor: open files, the active one, the cursor.
  editor: {
    // The file shown.
    active?: string | null
    // Where the cursor is.
    cursor?: {
      // 1-based, in characters.
      column: integer
      // 1-based.
      line: integer
      // The file.
      path: string
    } | null
    // Open files (tabs), in order.
    open?: string[]
    // Counts requests to show the cursor (`editor.open`, a selection's declaration): the
    // editor scrolls to the cursor when this changes, never for the cursor it reports itself.
    reveal?: integer
    // The text selected in the editor, if short.
    selected_text?: string | null
  }
  // The evaluation and the check's counts.
  eval: {
    // The board's name (`Board { name }`; `board` when the design gives none), of the last good
    // evaluation.
    board?: string | null
    // Check errors.
    errors: integer
    // `ok`: the files evaluate; `error`: they do not (the last good evaluation is kept);
    // `empty`: no design yet.
    eval: string
    // The first evaluation errors (`file`, `line`, `column`, `message`).
    eval_errors: unknown[]
    // The good evaluation is older than the files.
    stale: boolean
    // The check status of the last good evaluation: `error`, `incomplete`, `complete`.
    status?: string | null
    // Nets not yet joined.
    unrouted: integer
    // The files' version this evaluation is of.
    version: integer
    // Check warnings.
    warnings: integer
  }
  // The object under the pointer, described.
  hovered?: any
  // Check issues whose location is on screen (at most 30): `{ severity, code, message, at, ids }`.
  issues_in_view: unknown[]
  // The journal's last number.
  journal: integer
  // The address the person sees: what is open (`/p/<project>`, `/demo/<slug>`, …) and whether
  // it has finished opening.
  route: {
    // `start`, `project`, `demo`, `folder`, `repository` (served by `volt view`), `new`,
    // `import` or `unknown` (no such page).
    kind?: string
    // Why, in plain words, when the status is not `ready`.
    message?: string | null
    // The project's or folder's name, or the demo's slug.
    name?: string | null
    // The path, e.g. `/demo/blinker`.
    path?: string
    // `loading` while the address is being opened, `ready`, `waiting` (for the person: a click
    // to allow a folder or an import), `missing` (e.g. a project this browser does not have:
    // projects stay in the browser they were made in) or `failed`.
    status?: string
    // The whole address (origin, path and query).
    url?: string
  }
  // The selected objects, described (at most 20).
  selected: unknown[]
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // The view: mode, 2D/3D, side, camera, layers, selection, highlight, theme, panels, window.
  view: {
    // The layer edits go to.
    active_layer?: string | null
    // The objects declared where the editor's cursor is (the board shows them).
    at_cursor?: string[]
    // Where the camera looks.
    camera?: {
      // 3D: degrees around the board.
      azimuth?: number | null
      // The board region on screen, `[x0, y0, x1, y1]` in mm (2D).
      bounds?: number[] | null
      // 3D: mm from the target.
      distance?: number | null
      // 3D: degrees above the board.
      elevation?: number | null
      // 3D orbit: target `[x, y, z]` (mm), azimuth and elevation (degrees), distance (mm).
      target?: number[] | null
    }
    // What of the interface is shown.
    chrome?: {
      // Everything but the board hidden (`⌘.`).
      hidden?: boolean
      // The left panel: `auto` (by the window's width), `open` or `collapsed`.
      left?: string
      // The right panel (the inspector): `auto`, `open` or `collapsed`.
      right?: string
    }
    // The layers apart (`view.explode`): side by side in 2D, lifted apart in 3D.
    exploded?: boolean
    // Highlighted objects or nets (agents use it to show what they mean).
    highlight?: string[]
    // The object under the person's pointer.
    hover?: string | null
    // Copper and other layers shown (`F.Cu`, `B.Cu`, `In1.Cu`, `silk`, `courtyard`, `ratsnest`,
    // `zones`, `issues`, …); empty: the default set.
    layers?: string[]
    // `board`, `code` or `split`.
    mode?: string
    // 3D: library 3D models of the parts instead of procedural ones where a library has them
    // (`view.library`; on by default: the person's KiCad library, else the hosted one).
    models?: boolean
    // The colours the board is drawn in (`view.palette`): `standard`, `neon`, `blueprint`,
    // `pastel` or `solder`.
    palette?: string
    // Panels by name (`files`, `inspector`, `actions`, `history`, `report`, `bom`, `agents`,
    // `help`, `palette`, `assistant`) and whether they are open.
    panels?: { [key: string]: boolean }
    // Rulers along the board's top and left edges in 2D (`view.rulers`): board millimetres.
    rulers?: boolean
    // Selected objects (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `via:5`, `zone:2`).
    selection?: string[]
    // The side of the board towards the person: `top`, or `bottom` (seen from below, mirrored:
    // the bottom layers and the parts on the bottom are near). `view.flip` turns it.
    side?: string
    // `light` or `dark`.
    theme?: string
    // `2d`, `3d` or `photo` (the 3D view as a photograph).
    view?: string
    // The window and what of the board the person sees.
    viewport?: {
      // Device pixels per CSS pixel.
      dpr?: number
      // The part of the board's canvas no panel covers, `[left, top, right, bottom]` in CSS
      // pixels: what the person can see of the board (a snapshot shows the whole canvas).
      free?: number[] | null
      // CSS pixels.
      height?: number
      // `wide` (both panels open), `compact` (the left panel collapsed; the inspector opens with a
      // selection), `narrow` (both collapsed, e.g. a window side by side with an agent's) or
      // `phone` (both collapsed; a panel opens as a sheet from the bottom).
      layout?: string
      // CSS pixels.
      width?: number
    }
  }
  // What the interface has open (workspace kind and project).
  workspace: any
}
```

#### `view.select` — Select

Select objects by id (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, …), replacing the selection or adding to it; empty clears it. The editor shows where the selection is declared.

Shortcut: `Escape`.

```ts
input: {
  // Add to the selection instead of replacing it.
  add?: boolean
  // Object ids (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `block:power`, `file:design.volt`, …);
  // empty clears the selection.
  ids: string[]
}
result: unknown[]
```

#### `view.highlight` — Highlight

Highlight objects or nets (ids: `comp:U1`, `net:VBUS`, an issue's `ids`) to show the person what you mean, replacing the previous highlight; empty clears. Pair it with `view.focus`.

```ts
input: {
  // Object ids (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `block:power`, `class:power`, …);
  // empty: see the command.
  ids?: string[]
}
result: {}
```

#### `view.focus` — Focus

Move the camera to fit these objects (ids; `net:GND` fits its pads), or the whole board when empty, with a margin. Use it after a change or with an issue's `ids` so the person sees what you mean; `view.zoom` zooms by a factor instead.

Shortcut: `F`.

```ts
input: {
  // Object ids (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `block:power`, `class:power`, …);
  // empty: see the command.
  ids?: string[]
}
result: {
  // 3D: degrees around the board.
  azimuth?: number | null
  // The board region on screen, `[x0, y0, x1, y1]` in mm (2D).
  bounds?: number[] | null
  // 3D: mm from the target.
  distance?: number | null
  // 3D: degrees above the board.
  elevation?: number | null
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // 3D orbit: target `[x, y, z]` (mm), azimuth and elevation (degrees), distance (mm).
  target?: number[] | null
}
```

#### `view.layers` — Show layers

Show or hide layers (`F.Cu`, `B.Cu`, `In1.Cu`, `silk`, `courtyard`, `ratsnest`, `zones`, `issues`, `grid`: the millimetre grid under the board; in 3D also `mask` and `parts`), show only some, or choose the active layer. The same toggles apply to the 2D and the 3D view.

```ts
input: {
  // The copper layer edits go to, e.g. `F.Cu`.
  active?: string | null
  // Layers to hide (applied after `show`).
  hide?: string[]
  // Show only these (then `show` and `hide` apply).
  only?: string[] | null
  // Layers to show: copper (`F.Cu`, `In1.Cu`, `B.Cu`, …) or `silk`, `mask`, `parts`, `courtyard`,
  // `ratsnest`, `zones`, `issues`, `outline`, `grid`.
  show?: string[]
}
result: {
  // The layer edits go to.
  active_layer?: string | null
  // The objects declared where the editor's cursor is (the board shows them).
  at_cursor?: string[]
  // Where the camera looks.
  camera?: {
    // 3D: degrees around the board.
    azimuth?: number | null
    // The board region on screen, `[x0, y0, x1, y1]` in mm (2D).
    bounds?: number[] | null
    // 3D: mm from the target.
    distance?: number | null
    // 3D: degrees above the board.
    elevation?: number | null
    // 3D orbit: target `[x, y, z]` (mm), azimuth and elevation (degrees), distance (mm).
    target?: number[] | null
  }
  // What of the interface is shown.
  chrome?: {
    // Everything but the board hidden (`⌘.`).
    hidden?: boolean
    // The left panel: `auto` (by the window's width), `open` or `collapsed`.
    left?: string
    // The right panel (the inspector): `auto`, `open` or `collapsed`.
    right?: string
  }
  // The layers apart (`view.explode`): side by side in 2D, lifted apart in 3D.
  exploded?: boolean
  // Highlighted objects or nets (agents use it to show what they mean).
  highlight?: string[]
  // The object under the person's pointer.
  hover?: string | null
  // Copper and other layers shown (`F.Cu`, `B.Cu`, `In1.Cu`, `silk`, `courtyard`, `ratsnest`,
  // `zones`, `issues`, …); empty: the default set.
  layers?: string[]
  // `board`, `code` or `split`.
  mode?: string
  // 3D: library 3D models of the parts instead of procedural ones where a library has them
  // (`view.library`; on by default: the person's KiCad library, else the hosted one).
  models?: boolean
  // The colours the board is drawn in (`view.palette`): `standard`, `neon`, `blueprint`,
  // `pastel` or `solder`.
  palette?: string
  // Panels by name (`files`, `inspector`, `actions`, `history`, `report`, `bom`, `agents`,
  // `help`, `palette`, `assistant`) and whether they are open.
  panels?: { [key: string]: boolean }
  // Rulers along the board's top and left edges in 2D (`view.rulers`): board millimetres.
  rulers?: boolean
  // Selected objects (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `via:5`, `zone:2`).
  selection?: string[]
  // The side of the board towards the person: `top`, or `bottom` (seen from below, mirrored:
  // the bottom layers and the parts on the bottom are near). `view.flip` turns it.
  side?: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // `light` or `dark`.
  theme?: string
  // `2d`, `3d` or `photo` (the 3D view as a photograph).
  view?: string
  // The window and what of the board the person sees.
  viewport?: {
    // Device pixels per CSS pixel.
    dpr?: number
    // The part of the board's canvas no panel covers, `[left, top, right, bottom]` in CSS
    // pixels: what the person can see of the board (a snapshot shows the whole canvas).
    free?: number[] | null
    // CSS pixels.
    height?: number
    // `wide` (both panels open), `compact` (the left panel collapsed; the inspector opens with a
    // selection), `narrow` (both collapsed, e.g. a window side by side with an agent's) or
    // `phone` (both collapsed; a panel opens as a sheet from the bottom).
    layout?: string
    // CSS pixels.
    width?: number
  }
}
```

#### `view.grid` — Grid

Show or hide the millimetre grid under the board (a cutting mat: 1 mm lines, stronger every 10 mm, at the board's origin); default: toggle.

Shortcut: `Shift+'`.

```ts
input: {
  // Default: toggle.
  show?: boolean | null
}
result: {
  // The layer edits go to.
  active_layer?: string | null
  // The objects declared where the editor's cursor is (the board shows them).
  at_cursor?: string[]
  // Where the camera looks.
  camera?: {
    // 3D: degrees around the board.
    azimuth?: number | null
    // The board region on screen, `[x0, y0, x1, y1]` in mm (2D).
    bounds?: number[] | null
    // 3D: mm from the target.
    distance?: number | null
    // 3D: degrees above the board.
    elevation?: number | null
    // 3D orbit: target `[x, y, z]` (mm), azimuth and elevation (degrees), distance (mm).
    target?: number[] | null
  }
  // What of the interface is shown.
  chrome?: {
    // Everything but the board hidden (`⌘.`).
    hidden?: boolean
    // The left panel: `auto` (by the window's width), `open` or `collapsed`.
    left?: string
    // The right panel (the inspector): `auto`, `open` or `collapsed`.
    right?: string
  }
  // The layers apart (`view.explode`): side by side in 2D, lifted apart in 3D.
  exploded?: boolean
  // Highlighted objects or nets (agents use it to show what they mean).
  highlight?: string[]
  // The object under the person's pointer.
  hover?: string | null
  // Copper and other layers shown (`F.Cu`, `B.Cu`, `In1.Cu`, `silk`, `courtyard`, `ratsnest`,
  // `zones`, `issues`, …); empty: the default set.
  layers?: string[]
  // `board`, `code` or `split`.
  mode?: string
  // 3D: library 3D models of the parts instead of procedural ones where a library has them
  // (`view.library`; on by default: the person's KiCad library, else the hosted one).
  models?: boolean
  // The colours the board is drawn in (`view.palette`): `standard`, `neon`, `blueprint`,
  // `pastel` or `solder`.
  palette?: string
  // Panels by name (`files`, `inspector`, `actions`, `history`, `report`, `bom`, `agents`,
  // `help`, `palette`, `assistant`) and whether they are open.
  panels?: { [key: string]: boolean }
  // Rulers along the board's top and left edges in 2D (`view.rulers`): board millimetres.
  rulers?: boolean
  // Selected objects (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `via:5`, `zone:2`).
  selection?: string[]
  // The side of the board towards the person: `top`, or `bottom` (seen from below, mirrored:
  // the bottom layers and the parts on the bottom are near). `view.flip` turns it.
  side?: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // `light` or `dark`.
  theme?: string
  // `2d`, `3d` or `photo` (the 3D view as a photograph).
  view?: string
  // The window and what of the board the person sees.
  viewport?: {
    // Device pixels per CSS pixel.
    dpr?: number
    // The part of the board's canvas no panel covers, `[left, top, right, bottom]` in CSS
    // pixels: what the person can see of the board (a snapshot shows the whole canvas).
    free?: number[] | null
    // CSS pixels.
    height?: number
    // `wide` (both panels open), `compact` (the left panel collapsed; the inspector opens with a
    // selection), `narrow` (both collapsed, e.g. a window side by side with an agent's) or
    // `phone` (both collapsed; a panel opens as a sheet from the bottom).
    layout?: string
    // CSS pixels.
    width?: number
  }
}
```

#### `view.mode` — Switch view

Show the board (`board`), the code (`code`) or both (`split`); the board in `2d`, `3d` or `photo` (the 3D view as a photograph).

Shortcut: `Mod+\`.

```ts
input: {
  // `board`, `code` or `split`. With neither `mode` nor `view`: toggles board ↔ split.
  mode?: "board" | "code" | "split" | null
  // `2d`, `3d` or `photo` (the 3D view as a photograph).
  view?: "2d" | "3d" | "photo" | null
}
result: {
  // The layer edits go to.
  active_layer?: string | null
  // The objects declared where the editor's cursor is (the board shows them).
  at_cursor?: string[]
  // Where the camera looks.
  camera?: {
    // 3D: degrees around the board.
    azimuth?: number | null
    // The board region on screen, `[x0, y0, x1, y1]` in mm (2D).
    bounds?: number[] | null
    // 3D: mm from the target.
    distance?: number | null
    // 3D: degrees above the board.
    elevation?: number | null
    // 3D orbit: target `[x, y, z]` (mm), azimuth and elevation (degrees), distance (mm).
    target?: number[] | null
  }
  // What of the interface is shown.
  chrome?: {
    // Everything but the board hidden (`⌘.`).
    hidden?: boolean
    // The left panel: `auto` (by the window's width), `open` or `collapsed`.
    left?: string
    // The right panel (the inspector): `auto`, `open` or `collapsed`.
    right?: string
  }
  // The layers apart (`view.explode`): side by side in 2D, lifted apart in 3D.
  exploded?: boolean
  // Highlighted objects or nets (agents use it to show what they mean).
  highlight?: string[]
  // The object under the person's pointer.
  hover?: string | null
  // Copper and other layers shown (`F.Cu`, `B.Cu`, `In1.Cu`, `silk`, `courtyard`, `ratsnest`,
  // `zones`, `issues`, …); empty: the default set.
  layers?: string[]
  // `board`, `code` or `split`.
  mode?: string
  // 3D: library 3D models of the parts instead of procedural ones where a library has them
  // (`view.library`; on by default: the person's KiCad library, else the hosted one).
  models?: boolean
  // The colours the board is drawn in (`view.palette`): `standard`, `neon`, `blueprint`,
  // `pastel` or `solder`.
  palette?: string
  // Panels by name (`files`, `inspector`, `actions`, `history`, `report`, `bom`, `agents`,
  // `help`, `palette`, `assistant`) and whether they are open.
  panels?: { [key: string]: boolean }
  // Rulers along the board's top and left edges in 2D (`view.rulers`): board millimetres.
  rulers?: boolean
  // Selected objects (`comp:R1`, `pad:R1.2`, `net:GND`, `track:3`, `via:5`, `zone:2`).
  selection?: string[]
  // The side of the board towards the person: `top`, or `bottom` (seen from below, mirrored:
  // the bottom layers and the parts on the bottom are near). `view.flip` turns it.
  side?: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // `light` or `dark`.
  theme?: string
  // `2d`, `3d` or `photo` (the 3D view as a photograph).
  view?: string
  // The window and what of the board the person sees.
  viewport?: {
    // Device pixels per CSS pixel.
    dpr?: number
    // The part of the board's canvas no panel covers, `[left, top, right, bottom]` in CSS
    // pixels: what the person can see of the board (a snapshot shows the whole canvas).
    free?: number[] | null
    // CSS pixels.
    height?: number
    // `wide` (both panels open), `compact` (the left panel collapsed; the inspector opens with a
    // selection), `narrow` (both collapsed, e.g. a window side by side with an agent's) or
    // `phone` (both collapsed; a panel opens as a sheet from the bottom).
    layout?: string
    // CSS pixels.
    width?: number
  }
}
```

#### `view.theme` — Theme

Light or dark.

```ts
input: {
  // `light` or `dark`; default: switch.
  theme?: "light" | "dark" | null
}
result: {}
```

#### `view.palette` — Board colours

The colours the board is drawn in, in the light and the dark theme alike: `standard` (red top, blue bottom, distinct inner layers), `neon` (saturated, on near black), `blueprint` (pale copper on blue paper), `pastel` (the standard hues, softened), `solder` (green solder mask, copper top, tin bottom). `view.state` → `view.palette`.

```ts
input: {
  // The palette.
  name: "standard" | "neon" | "blueprint" | "pastel" | "solder"
}
result: {}
```

#### `view.flip` — Flip the board over

Show the other side of the board (default: turn it over), like turning it in your hand: in 2D the view from below (mirrored; the bottom layers and bottom-side parts are near), in 3D the camera goes round to the other side. Use it to show the person bottom-side parts; `view.state` → `view.side` says which side is shown.

Shortcut: `Shift+F`.

```ts
input: {
  // The side to show, `top` or `bottom`; default: the other one.
  side?: "top" | "bottom" | null
}
result: {
  // The side shown now.
  side: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `view.panel` — Toggle a panel

Open, close or toggle a panel: `files`, `inspector`, `actions` (Route, the outputs, Order), `history`, `report`, `bom` (the bill of materials with the chosen parts), `agents`, `help` (the keys and the API for agents), `palette` (quick actions), `assistant` (the built-in assistant's chat).

```ts
input: {
  // The panel.
  name: "files" | "inspector" | "actions" | "history" | "report" | "bom" | "agents" | "help" | "palette" | "assistant"
  // Default: toggle.
  open?: boolean | null
}
result: {}
```

#### `view.chrome` — Show or hide the interface

Hide everything but the board (`hidden`), or open or collapse a side panel (`left`, `right`: `open`, `collapsed`, `auto` = by the window's width). For a narrow window, or to give the board the whole window before a snapshot. `view.state` reports the window and the part of the board that is visible (`view.viewport`).

Shortcut: `Mod+.`.

```ts
input: {
  // Hide (true) or show (false) everything but the board; default: toggle when nothing else
  // is given.
  hidden?: boolean | null
  // The left panel (files, the object tree): `open`, `collapsed` or `auto` (by the window's width).
  left?: "open" | "collapsed" | "auto" | null
  // The right panel (the inspector): `open`, `collapsed` or `auto`.
  right?: "open" | "collapsed" | "auto" | null
}
result: {
  // Everything but the board hidden (`⌘.`).
  hidden?: boolean
  // The left panel: `auto` (by the window's width), `open` or `collapsed`.
  left?: string
  // The right panel (the inspector): `auto`, `open` or `collapsed`.
  right?: string
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `view.update` — Report the view

The interface reports what changed on screen (camera after a pan, hover, selection by click). Merged into one journal event per gesture. _(for the interface)_

```ts
input: {
  camera?: {
    // 3D: degrees around the board.
    azimuth?: number | null
    // The board region on screen, `[x0, y0, x1, y1]` in mm (2D).
    bounds?: number[] | null
    // 3D: mm from the target.
    distance?: number | null
    // 3D: degrees above the board.
    elevation?: number | null
    // 3D orbit: target `[x, y, z]` (mm), azimuth and elevation (degrees), distance (mm).
    target?: number[] | null
  } | null
  // The hovered object (`null` clears).
  hover?: string | null
  layers?: string[] | null
  mode?: string | null
  // The address the person sees (after a link, back or forward, or an open).
  route?: {
    // `start`, `project`, `demo`, `folder`, `repository` (served by `volt view`), `new`,
    // `import` or `unknown` (no such page).
    kind?: string
    // Why, in plain words, when the status is not `ready`.
    message?: string | null
    // The project's or folder's name, or the demo's slug.
    name?: string | null
    // The path, e.g. `/demo/blinker`.
    path?: string
    // `loading` while the address is being opened, `ready`, `waiting` (for the person: a click
    // to allow a folder or an import), `missing` (e.g. a project this browser does not have:
    // projects stay in the browser they were made in) or `failed`.
    status?: string
    // The whole address (origin, path and query).
    url?: string
  } | null
  selection?: string[] | null
  view?: string | null
  // The window (size, layout, the visible part of the board).
  viewport?: {
    // Device pixels per CSS pixel.
    dpr?: number
    // The part of the board's canvas no panel covers, `[left, top, right, bottom]` in CSS
    // pixels: what the person can see of the board (a snapshot shows the whole canvas).
    free?: number[] | null
    // CSS pixels.
    height?: number
    // `wide` (both panels open), `compact` (the left panel collapsed; the inspector opens with a
    // selection), `narrow` (both collapsed, e.g. a window side by side with an agent's) or
    // `phone` (both collapsed; a panel opens as a sheet from the bottom).
    layout?: string
    // CSS pixels.
    width?: number
  } | null
  // What the interface has open (workspace kind and project).
  workspace?: any
}
result: {}
```

### editor

#### `editor.open` — Open a file

Open a file in the editor, optionally at a line and column (the board view switches to `split` so the code shows).

Shortcut: `Mod+P`.

```ts
input: {
  // The column (1-based, in characters; default 1).
  column?: integer | null
  // The line to show and put the cursor on (1-based; default 1).
  line?: integer | null
  // A project-relative path, e.g. `design.volt`.
  path: string
}
result: {
  // The file shown.
  active?: string | null
  // Where the cursor is.
  cursor?: {
    // 1-based, in characters.
    column: integer
    // 1-based.
    line: integer
    // The file.
    path: string
  } | null
  // Open files (tabs), in order.
  open?: string[]
  // Counts requests to show the cursor (`editor.open`, a selection's declaration): the
  // editor scrolls to the cursor when this changes, never for the cursor it reports itself.
  reveal?: integer
  // The text selected in the editor, if short.
  selected_text?: string | null
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `editor.update` — Report the editor

The interface reports the editor's open files, active file, cursor and selected text; `select` (a click in the code) selects the object declared there. _(for the interface)_

```ts
input: {
  // The file shown.
  active?: string | null
  // Where the cursor is.
  cursor?: {
    // 1-based, in characters.
    column: integer
    // 1-based.
    line: integer
    // The file.
    path: string
  } | null
  // Open files (tabs), in order.
  open?: string[]
  // Counts requests to show the cursor (`editor.open`, a selection's declaration): the
  // editor scrolls to the cursor when this changes, never for the cursor it reports itself.
  reveal?: integer
  // The person clicked in the code: select the innermost object declared there (and bring it
  // into view on the board).
  select?: boolean
  // The text selected in the editor, if short.
  selected_text?: string | null
}
result: {}
```

### view

#### `view.snapshot` — Image of the canvas

A PNG of what the canvas shows now (or of a board region in mm), as a data URL, when an agent needs pixels; `view.state` describes the screen more cheaply. _(reads only, web app only)_

```ts
input: {
  // A board region `[x0, y0, x1, y1]` (mm); default what the canvas shows.
  region?: number[] | null
  // Width in pixels (default the canvas's, at most 2048).
  width?: integer | null
}
result: {
  // Pixels.
  height: integer
  // `data:image/png;base64,…`
  image: string
  // Pixels.
  width: integer
}
```

#### `view.models` — Library 3D models

Where library 3D models come from and how loading them went: the person's KiCad 3D library first — the one the server of `volt view` offers, or a folder the person chooses (`folder: true` asks them to pick one; needs their click), its STEP files converted in this browser and cached, nothing uploaded — else the hosted component library (library.volt0.ai: KiCad's 3D models converted ahead of time, CC-BY-SA 4.0; only the models the board needs). Turn models on or off with `view.library`. _(web app only)_

```ts
input: {
  // Forget the converted models kept in this browser.
  clear_cache?: boolean | null
  // Ask the person to choose a folder holding a KiCad 3D library (`<Library>.3dshapes/…`).
  folder?: boolean | null
}
result: any
```

#### `view.explode` — Exploded layers

Spread the board's layers apart to see each one (`on` omitted toggles): in 2D the copper layers lie side by side like sheets on a table, each labelled (picking and the selection work on every sheet); in 3D the layers lift apart, parts riding on the outer ones and vias as columns through the gaps. Animated; `view.state` → `view.exploded`.

Shortcut: `E`.

```ts
input: {
  // Layers apart or together (omitted: the other way round).
  on?: boolean | null
}
result: {
  // Whether the layers are apart now.
  on: boolean
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `view.rulers` — Rulers

Show or hide rulers along the top and left edges of the 2D board (board millimetres at the current zoom, the pointer's position marked); `on` omitted toggles. `view.state` → `view.rulers`.

Shortcut: `Alt+R`.

```ts
input: {
  // Rulers shown or hidden (omitted: the other way round).
  on?: boolean | null
}
result: {
  // Whether the rulers show now.
  on: boolean
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `view.library` — Library 3D models on or off

Show the parts in 3D as their library 3D models (KiCad's 3D library: the person's, else the hosted one) where there is one, instead of the procedural ones (on by default); `on` omitted toggles. Where the models come from: `view.models`.

```ts
input: {
  // On or off (omitted: the other way round).
  on?: boolean | null
}
result: {
  // Whether library models are shown now.
  on: boolean
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```

#### `view.stats` — Renderer statistics

The renderer's backend (WebGPU or WebGL2) and frame times (CPU, and GPU where the browser measures it), draws and primitives: how fast the board draws here. _(reads only, web app only)_

```ts
input: {}
result: any
```

### workspace

#### `workspace.projects` — Projects

The projects in the browser's storage (`projects`: their names), the open one (`open`) and whether the browser keeps the storage for good (`persistent`). _(reads only, web app only)_

```ts
input: {}
result: any
```

#### `workspace.open` — Open a project

Open a project from the browser's storage by name (its address is `/p/<name>`). _(web app only)_

```ts
input: {
  // The project's name in the browser's storage (`workspace.projects` lists them).
  name: string
}
result: any
```

#### `workspace.demos` — Demo boards

The demo boards anyone can open: slug, title, what each shows, its size, license and source, and its address (`https://app.volt0.ai/demo/<slug>`). _(reads only, web app only)_

```ts
input: {}
result: any
```

#### `workspace.demo` — Open a demo

Open a demo board by slug (`workspace.demos` lists them; the address becomes `/demo/<slug>`). Where this browser saved a copy of it before, the copy opens (with its changes; `original: true` opens the demo as it comes). A demo is not stored until its first change (an edit, routing), which saves it as a new project in this browser, named after the demo, and the address becomes `/p/<name>`. Its source and license come with it (`SOURCE.md`). _(web app only)_

```ts
input: {
  // The demo as it comes, even where this browser saved a copy of it (default: the copy).
  original?: boolean | null
  // The demo's slug, e.g. `blinker` (`workspace.demos` lists them).
  slug: string
}
result: any
```

#### `workspace.link` — Link to this

The address of what is open, to reload it or give it to someone: `/demo/<slug>` opens the same demo anywhere; `/p/<name>` opens the project only in this browser (projects stay on this machine: to share one, `export.repository` and send the zip). With `view` (default) the address also keeps 2D or 3D and the selection (`?view=3d&select=comp:U1`). _(reads only, web app only)_

```ts
input: {
  // Keep the view in the address (2D or 3D, the selection); default true.
  view?: boolean | null
}
result: any
```

#### `workspace.new` — New project in the browser

Create a project in the browser's storage and open it. _(web app only)_

```ts
input: {
  // The project's name in the browser's storage (`workspace.projects` lists them).
  name: string
}
result: any
```

#### `workspace.rename` — Rename a project

Rename a project in the browser's storage. _(web app only)_

```ts
input: {
  // The project's name now.
  name: string
  // The new name (must be free).
  to: string
}
result: any
```

#### `workspace.duplicate` — Duplicate a project

Copy a project in the browser's storage under a new name. _(web app only)_

```ts
input: {
  // The project's name now.
  name: string
  // The new name (must be free).
  to: string
}
result: any
```

#### `workspace.delete` — Delete a project

Delete a project from the browser's storage, with its files and history; it cannot be undone (`export.repository` first keeps a copy). The open project cannot be deleted: open another first. _(destructive, web app only)_

```ts
input: {
  // The project's name in the browser's storage (`workspace.projects` lists them).
  name: string
}
result: any
```

#### `workspace.mount` — Open a folder

Ask the person to pick a folder on disk (a board repository): the app then reads and writes the files there, and follows changes made outside. Needs the person's click; an agent's call shows them a prompt. _(web app only)_

Shortcut: `Mod+O`.

```ts
input: {}
result: any
```

#### `workspace.upload` — Open a file

Ask the person to pick a KiCad board or a zipped board repository to open. _(web app only)_

```ts
input: {}
result: any
```

#### `workspace.load` — New project from files

Create a project in the browser's storage from files you give (the text files of a board repository, or a KiCad board) and open it: what `workspace.upload` does with a file the person picks, without asking them. Returns the workspace (and the import job for a KiCad board). _(web app only)_

```ts
input: {
  // The text files of a board repository (`design.volt` required), paths relative to its root.
  files?: {
    // Path relative to the repository's root, e.g. `design.volt`.
    path: string
    // The file's text.
    text: string
  }[] | null
  // Or a KiCad board, imported into the new project.
  kicad?: {
    // The `.kicad_dru` text.
    dru?: string | null
    // The `.kicad_pcb` text.
    pcb: string
    // The `.kicad_pro` text.
    pro?: string | null
  } | null
  // The project's name (made unique); default: the board's.
  name?: string | null
}
result: any
```

#### `workspace.close` — Back to the projects

Close the open project and show the projects (the start page). _(web app only)_

```ts
input: {}
result: any
```

#### `workspace.reopen` — Reopen the last folder

Open again the folder on disk opened last (the browser may ask the person to allow it again). _(web app only)_

```ts
input: {}
result: any
```

### view

#### `view.zoom` — Zoom

Zoom the canvas: `by` a factor (2 = in, 0.5 = out), `to` a percentage (100 = 1:1 on screen), or `fit` the `board` or the `selection` into the part of the window the panels leave free. Returns the zoom. _(web app only)_

```ts
input: {
  // Multiply the zoom by this factor.
  by?: number | null
  // Fit `board` or `selection`.
  fit?: "board" | "selection" | null
  // Set the zoom to this percentage.
  to?: number | null
}
result: any
```

#### `view.look` — Look at the board in 3D

Turn the 3D camera to a preset and frame the board: `iso` (the default view, from above at an angle), `top`, `bottom` (from below), `side`. Switches the board to 3D. _(web app only)_

```ts
input: {
  // The direction to look from (default `iso`).
  preset?: "iso" | "top" | "bottom" | "side" | null
}
result: any
```

#### `view.tool` — Choose a tool

The canvas's tool for the person's pointer: `move` (select and drag), `hand` (pan), `measure`, `comment` (the person pins a comment to the board). _(web app only)_

```ts
input: {
  // The tool.
  tool: "move" | "hand" | "measure" | string
}
result: any
```

### workspace

#### `workspace.download` — Download an output

Download an output of an export (`export.repository` as a zip, `export.kicad`, …). _(web app only)_

```ts
input: {
  // The id an export returned.
  output: string
}
result: any
```

### fab

#### `fab.quote` — Quote the board

A price for making the board from our fabrication partner: the board's Gerber and drill archive (`export.fab { only: "gerbers" }`; a board that is not complete needs `force`) goes to the partner, which reviews it (size, layers, a preview) and prices it for a quantity, with lead times. `zip` (CLI: a path relative to the project) or `pick` (web app: the person chooses a file) quote other Gerber files. Ordering is not available yet. Fails with the error code `awaiting_partner_permission` while the partner has not opened its API to Volt Zero. _(reads only, over the network)_

```ts
input: {
  // Destination country (ISO code) for shipping and lead times.
  country?: string | null
  // Quote a board whose check is not `complete` (unrouted nets, errors).
  force?: boolean | null
  // Copper layers (default: what the partner reads from the files).
  layers?: integer | null
  // The partner's own PCB parameters (colour, finish, …), passed through as they are.
  pcbParam?: {} | null
  // Web app: ask the person to choose a Gerber zip instead of the board's.
  pick?: boolean | null
  // Boards to make (default 5).
  quantity?: integer | null
  // Board thickness, mm (default 1.6).
  thickness?: number | null
  // A Gerber zip to quote instead of the board's (CLI: a path relative to the project).
  zip?: string | null
}
result: {
  // The total's parts.
  costs: {
    // In `currency`.
    amount: number
    // What it is for (the board, shipping, …).
    name: string
  }[]
  // ISO currency code, e.g. `USD`.
  currency: string
  // The partner's id of the uploaded files (an order refers to it).
  fileKey: string
  // Lead time options.
  leadTimes: {
    // Days (or the partner's text).
    days: any
    // The option (e.g. standard, express).
    name: string
  }[]
  // The options the partner priced.
  pcbParam: any
  // The partner's review of the files.
  review: {
    // Copper layers.
    layers?: integer | null
    // mm
    length?: number | null
    // Preview images (addresses).
    preview: string[]
    // mm
    width?: number | null
  }
  // The order's total as quoted.
  total?: number | null
}
```

#### `fab.parts` — Search the partner's parts

Search the fabrication partner's parts catalog (stock, price breaks, basic/extended library) by words, MPN or part number (`C25804`). For parts in the design and their pins use `parts.search`. _(reads only, over the network)_

```ts
input: {
  // Only parts in stock.
  inStock?: boolean | null
  // Only this library: `basic`, `extended` or `preferred`.
  library?: "basic" | "extended" | "preferred" | null
  // At most this many parts (the partner's default and maximum apply).
  limit?: integer | null
  // Skip this many (for the next page).
  offset?: integer | null
  // Words, an MPN or a part number.
  query?: string
}
result: {
  // This page's parts.
  items: ({
    category?: string | null
    // The partner's part number.
    code: string
    // The datasheet's address.
    datasheet?: string | null
    description?: string | null
    // `basic`, `extended`, `preferred`.
    library?: string | null
    manufacturer?: string | null
    // Manufacturer part number.
    mpn?: string | null
    package?: string | null
    // Parameters by name (e.g. `Resistance`: `10kΩ`).
    params: { [key: string]: string }
    // Price breaks.
    prices: ({
      // From this quantity.
      from: integer
      // USD per piece.
      price: number
      // Up to this quantity (`null`: and more).
      to?: integer | null
    })[]
    // Pieces in stock.
    stock?: integer | null
  })[]
  // Parts matching in all.
  total: integer
}
```

### comments

#### `comments.list` — Comments

The comments on the board with their threads: where each is (`at`, a point in mm; `area`, a box; `on`, the object it is about, e.g. `comp:U1`), who wrote it, its status (`open`, `resolved`, `dismissed`) and the replies. `status` filters (default: all). The person's comments are requests: read the open ones, fix the board, then `comments.reply` with what you did and `status: "resolved"`. _(reads only)_

```ts
input: {
  // Only comments with this status.
  status?: "open" | "resolved" | "dismissed" | null
}
result: {
  // The comments matching the filter, oldest first.
  comments: ({
    // The area it is about: [x0, y0, x1, y1] (mm).
    area?: number[] | null
    // Where its pin is (mm). For a comment on an object that has moved since, the object's
    // centre now.
    at?: number[] | null
    // Who wrote it: `user` (the person), `agent:<name>`, …
    author: string
    // Its id (`comments.reply`, `comments.status`, `comments.remove` take it).
    id: integer
    // The object it is about (`comp:U1`, `net:GND`, …), if any.
    on?: string | null
    // The thread, oldest first.
    replies?: {
      // `user` (the person), `agent:<name>`, …
      author: string
      // What it says.
      text: string
      // When (UTC, `2026-10-15T10:20:00Z`).
      time?: string
    }[]
    // `open`, `resolved` or `dismissed`.
    status?: "open" | "resolved" | "dismissed"
    // What it says.
    text: string
    // When (UTC, `2026-10-15T10:20:00Z`).
    time?: string
  })[]
  // Comments still open (of all, whatever the filter).
  open: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // Comments in all.
  total: integer
}
```

#### `comments.add` — Add a comment

Pin a comment to the board: on an object (`on`: `comp:U1`, `pad:U1.3`, `net:GND`, `track:3`, …), at a point (`at`, mm) or on an area (`area`: [x0, y0, x1, y1], mm); with `on` alone it sits at the object's centre. Saved in `comments.volt` (comments are not undoable: they are a conversation, not the design); the journal gets a `comment.added` event.

```ts
input: {
  // An area it is about: [x0, y0, x1, y1] (mm).
  area?: number[] | null
  // Where to pin it (mm, the board's coordinates).
  at?: number[] | null
  // The object it is about: `comp:U1`, `pad:U1.3`, `net:GND`, `track:3`, `via:5`, `zone:2`,
  // `block:power`.
  on?: string | null
  // What the comment says.
  text: string
}
result: {
  // The area it is about: [x0, y0, x1, y1] (mm).
  area?: number[] | null
  // Where its pin is (mm). For a comment on an object that has moved since, the object's
  // centre now.
  at?: number[] | null
  // Who wrote it: `user` (the person), `agent:<name>`, …
  author: string
  // Its id (`comments.reply`, `comments.status`, `comments.remove` take it).
  id: integer
  // The object it is about (`comp:U1`, `net:GND`, …), if any.
  on?: string | null
  // The thread, oldest first.
  replies?: {
    // `user` (the person), `agent:<name>`, …
    author: string
    // What it says.
    text: string
    // When (UTC, `2026-10-15T10:20:00Z`).
    time?: string
  }[]
  // `open`, `resolved` or `dismissed`.
  status?: "open" | "resolved" | "dismissed"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // What it says.
  text: string
  // When (UTC, `2026-10-15T10:20:00Z`).
  time?: string
}
```

#### `comments.reply` — Reply to a comment

Reply in a comment's thread; `status` changes its status in the same call (e.g. `resolved` after fixing what it asks).

```ts
input: {
  // The comment's id (`comments.list`).
  id: integer
  // Change the status too (e.g. `resolved` after doing what it asks).
  status?: "open" | "resolved" | "dismissed" | null
  // The reply.
  text: string
}
result: {
  // The area it is about: [x0, y0, x1, y1] (mm).
  area?: number[] | null
  // Where its pin is (mm). For a comment on an object that has moved since, the object's
  // centre now.
  at?: number[] | null
  // Who wrote it: `user` (the person), `agent:<name>`, …
  author: string
  // Its id (`comments.reply`, `comments.status`, `comments.remove` take it).
  id: integer
  // The object it is about (`comp:U1`, `net:GND`, …), if any.
  on?: string | null
  // The thread, oldest first.
  replies?: {
    // `user` (the person), `agent:<name>`, …
    author: string
    // What it says.
    text: string
    // When (UTC, `2026-10-15T10:20:00Z`).
    time?: string
  }[]
  // `open`, `resolved` or `dismissed`.
  status?: "open" | "resolved" | "dismissed"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // What it says.
  text: string
  // When (UTC, `2026-10-15T10:20:00Z`).
  time?: string
}
```

#### `comments.status` — Set a comment's status

Mark a comment `open`, `resolved` (done) or `dismissed` (not done, with a reason in a reply).

```ts
input: {
  // The comment's id (`comments.list`).
  id: integer
  // `open`, `resolved` (done) or `dismissed` (not done).
  status: "open" | "resolved" | "dismissed"
}
result: {
  // The area it is about: [x0, y0, x1, y1] (mm).
  area?: number[] | null
  // Where its pin is (mm). For a comment on an object that has moved since, the object's
  // centre now.
  at?: number[] | null
  // Who wrote it: `user` (the person), `agent:<name>`, …
  author: string
  // Its id (`comments.reply`, `comments.status`, `comments.remove` take it).
  id: integer
  // The object it is about (`comp:U1`, `net:GND`, …), if any.
  on?: string | null
  // The thread, oldest first.
  replies?: {
    // `user` (the person), `agent:<name>`, …
    author: string
    // What it says.
    text: string
    // When (UTC, `2026-10-15T10:20:00Z`).
    time?: string
  }[]
  // `open`, `resolved` or `dismissed`.
  status?: "open" | "resolved" | "dismissed"
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
  // What it says.
  text: string
  // When (UTC, `2026-10-15T10:20:00Z`).
  time?: string
}
```

#### `comments.remove` — Delete a comment

Delete a comment and its thread from `comments.volt`; it cannot be undone (`comments.status` `dismissed` keeps it). _(destructive)_

```ts
input: {
  // The comment's id (`comments.list`).
  id: integer
}
result: {
  // The id of the comment deleted.
  removed: integer
  // What happened, in plain words: the numbers that matter and the natural next step (generated from the result; never replaces its fields).
  summary?: string
}
```


## References

- The Volt language: `/llms-full.txt` (section “Volt”), or `help { topic: "volt" }`.
- The board format: `/llms-full.txt` (section “Board format”), or `help { topic: "format" }`.
