Skip to content

The parts you can check.

Four blocks of reference material: what a click resolves to, what preflight measures against, where an AI agent fits, and six load-bearing decisions, each with a file in the repository you can open.

What a click resolves to.

A vector editor sees

  • paths
  • glyph outlines
  • rectangles
  • clip groups

Tavotto sees

  • Title “Reaction kinetics” axes_0.title
  • Legend axes_0.legend
  • Curve axes_0.lines_2
  • Tick labels axes_0.xticks
  • Annotation arrow axes_0.texts_0.arrow

One click, resolved to the object that drew it.

Hit-testing follows drawn geometry: a curve by distance to its rendered path, a filled region by the same winding rule the renderer used to fill it. Not bounding boxes.

Publication rules are physical.

Column widths in millimetres, font floors in points, raster floors in dots per inch. Tavotto measures the composed page against a versioned profile that engine and interface both read; a journal with different widths is a one-line override, recorded in the proof report.

80 / 150 mm
Single- and double-column widths in the default profile
8.5 pt
Strict font floor, judged at final physical size; 8 pt absolute
300 dpi
Minimum for rasterised output
0.5-1.5 pt
Line-width steps the frame and curves are checked against

A panel scaled to 60%: judged at 9 pt × 0.6 = 5.4 pt. Fails.

Blocking
Stops the export until you confirm in writing.
Warning
Always shown, never silently dropped.
Not verifiable
What genuinely cannot be checked, like text inside an imported bitmap. Needs a human.
Suggestion
Never decides anything for you.

One rendering truth.

Every export starts from the same PDF composition: the PDF keeps real text, and PNG is a rasterisation of that exact PDF. The two formats cannot drift apart.

Where an AI agent fits

Coding agents produce good first-pass Matplotlib. What remains is visual: the legend two lines too tall, a label sitting on a data point. Tavotto is that visual layer, and the script your agent wrote stays the source. Hand a figure over from any terminal:

tavotto open figures/Fig1_kinetics.pdf

Opens the figure’s library as a project and hands the figure to the window that is already open, if there is one.

Codex plugin

A skill, a local MCP server and an embedded canvas built from the same frontend the desktop app runs. Parts of the Codex Desktop integration are not yet verified inside a real Codex Desktop; the plugin README says which.

Under the figure.

Six load-bearing decisions, each verifiable in the repository.

Rendering

Local. Script execution, rendering, composition and export run on your machine; figures and scripts are never uploaded.

docs/privacy.md
Network

Two self-initiated requests: a daily release check, and usage statistics only after explicit opt-in. Independent switches. The telemetry schema has no field that can carry a filename, a path or figure text.

docs/analytics/telemetry-events.md
Replay verification

CI holds live session, full replay, fresh process, and write-back-then-reopen to the same manifest, element by element.

tests/test_equivalence_matrix.py
Golden vectors

Patch canonicalisation is implemented in Python and in Rust, held byte-identical by one shared vector file. Preflight likewise, in Python and TypeScript.

tests/golden/
Bundled runtime

Desktop installers ship a pinned CPython with the scientific stack; every platform locks the exact same package versions.

packaging/runtime-lock.json
Release tests

Packaged apps must boot, render, export and exit clean before release. Nightly, the Windows installer is installed, smoked, upgraded and uninstalled, under a CJK username.

.github/workflows/nightly.yml

Open source under AGPL-3.0; the reasoning lives in the repository’s ADRs. docs/adr