Working with agents and automation¶
botrail was built to be driven by code, and that makes it a good substrate for an agent: the cell is text, every result is JSON, the bake is deterministic, and nothing needs a GUI. This page is the loop an agent (or a CI job, or you in a shell) runs, and where each piece lives.
The loop¶
- Write the cell — a Python file using the API, or a
.botrailproject written against the JSON Schema. - Check it —
botrail check cell.pyloads it, lints it, counts what is in it, and prints JSON. Exit code 1 means an error-severity finding. - Bake it —
botrail simulate cell.py --scenarios --report r.jsonruns the sequences (and the whole scenario matrix) and prints the cell report: cycle times, step spans, clearance, I/O counts, scenario results, BOM totals, footprint. - Read the numbers, change the cell, go to 2. The bake is bit-identical for the same input, so a number that moved was moved by the edit — there is nothing to average, nothing to re-run.
- Hand it over —
botrail export cell.py --out deliverables/ --allwrites the document set with the report's digests.
botrail check cell.py # {"ok": true, "counts": {...}, "findings": []}
botrail simulate cell.py --scenarios --report r.json # the report as JSON on stdout too
botrail export cell.py --out deliverables/ --all # project, python, bom, io, topology, layout, usd, script, report
The same loop from Python is the API these commands call —
bt.Scene.load_project, scene.io_report(), scene.simulate_scenarios(),
scene.cell_report(), the export_* methods — so an agent that prefers to
stay in Python loses nothing.
What to read, in what order¶
For an agent learning the API, the shortest path through the docs is:
- Your first cell — scene, obstacles, sequence, bake, the vocabulary.
- Sequences and Sensors and devices — the process layer: steps, actions, transitions, what the environment does.
- Timeline assertions — how a bake is read
(
step_span,signal,min_clearance) and asserted on. - Parts and the BOM, Standard parts, The I/O map, Layout sheet and cell report — the engineering documents and how each is derived.
- The API reference — every method's docstring;
the same text
help(bt.Scene)shows. - Selecting parts —
scene.requirements()/botrail check: what each BOM line must be able to do and whether its part can, andbt.catalog.search_for(row)for the candidates to pick from.
The repository's examples/ are complete, runnable cells (the docs'
tutorials walk through them), and python/tests/ shows what is asserted
about each feature — both are good few-shot material.
Conventions that make the output machine-friendly¶
- JSON everywhere.
check,simulate(without--markdown),exportandschemaprint JSON; every report object hasto_json(); the cell report keeps one shape (nullfor what was not measured, never a missing key);Bom.rows,CellReport.cyclesand friends are plain dicts and lists. - Names, not indices. Everything is addressed by the name you gave it — obstacles, frames, sensors, devices, sequences, steps, parts. Findings quote those names.
- Errors are
ValueErrorwith a sentence, and the CLI turns them into{"ok": false, "error": "..."}with exit code 2 (could not load) or 1 (loaded, but findings or a failed bake). - Determinism. Same input, same timeline, same report — a diff between
two runs is a diff between two cells.
exporthashes what it writes so the diff can be taken at the file level too. - The project schema.
botrail schema(orbt.project_schema()) is generated from the Rust types the loader reads; a.botrailthat validates is a.botrailthat loads, and its descriptions are the docstrings.
Studies¶
When the question is which layout rather than this layout, the same
loop runs over a grid: bt.sweep bakes a cell
authored as a function of its parameters at every point and returns a table
(rows in grid order, failed variants as rows with the reason);
bt.optimize searches the grid — exhaustively or
by coordinate descent — for the best feasible point under constraints on
the metrics. Both are deterministic and return every evaluated row, so an
agent can read the whole search, not just its answer
(Parameter sweeps).
Selecting parts¶
botrail does not choose parts, and neither should an agent from memory. The
loop is: read scene.requirements().to_json() (what every BOM line must be
able to do, and why); for a line that is unidentified or short, call
bt.catalog.search_for(row) and pick from what comes back; write the pick
with product.identify(scene, target); run scene.check() (or botrail
check) until no spec_short remains. A line with no candidates stays an
unidentified_part finding whose needs ... text is the question to hand
to a person or a vendor — never a model number invented to make the check
pass. set_part(catalog=...) accepts any id, but only a catalog product's
numbers (or numbers typed on set_part) count as "what the part says"; a
name alone stays unknown. See Selecting parts.
What botrail does not do here¶
It does not design the cell for you. There is no layout generator and no
"design agent" inside the package — those live outside (a script, an agent,
a person) and use botrail as their hands and their verifier: author, check,
bake, read, repeat; optimize searches a space you wrote down. That is
deliberate: the package stays a deterministic engine with a text interface,
which is exactly what makes it composable with whatever does the thinking.