Read a function-level diff¶
A text diff tells you which lines moved. A semantic diff tells you what changed — which exported symbols were added, removed, or re-shaped, and whether a public interface broke. This guide shows you how to read one.
Built and default-on — optional and additive
tovio semantic diff is part of the optional, additive semantic layer, which is built and
on by default — everything below works today. The layer never blocks a core operation, so a
plain text tovio diff always works whether or not the index exists.
When to reach for it¶
Use a semantic diff when "12 lines changed" isn't the question you actually have. You want to know:
- Did I break anyone? — whether an exported signature or supertype changed.
- What's the blast radius of this refactor? — symbols touched, not lines touched.
- Is this safe to land? — whether a public interface changed in a breaking way.
For a line-by-line view, keep using plain tovio diff. The semantic diff sits
beside it, not instead of it.
Diff a change semantically¶
tovio semantic diff [A] [B] compares two commits' symbol graphs. Give it commit addresses
(blake3:<hex> or bare hex); with no arguments it compares HEAD's first parent with HEAD — "what did
my latest commit change":
$ tovio semantic diff
semantic diff blake3:3f9c1a2b7d… → blake3:8d41e07c93…
Removed (breaking):
- legacyVerify().
Changed (breaking):
~ verifyToken(). (signature)
Added:
+ assertNotExpired().
→ BREAKING interface change(s) detected (advisory).
Read it top to bottom:
~changed,+added,-removed — the same glyph grammar as the rest of TOVIO, applied to symbols instead of lines. Symbols are named by their descriptor moniker (verifyToken().is a function;Claims#is a type).(breaking)marks a change to an exported symbol — a removed export, or one whose signature or supertype changed (signature,supertype, orsignature + supertype). File-local symbols never appear here.- Advisory means exactly that: the diff reports, it never blocks. The last line is the verdict — breaking changes detected, no interface changes, or additions only.
Two more sections appear when the graph carries the edges: Dependencies changed (a symbol now depends on, or no longer depends on, another) and Test coverage changed (a symbol gained or lost a covering test). Both are advisory context, not part of the breaking verdict.
Turn the verdict into a list
A breaking change tells you what moved. To see who is affected, query the symbol
graph: tovio semantic impact with no arguments seeds itself from this
change's removed and changed symbols and walks the recorded dependents, and
tovio semantic callers '<moniker>' lists the direct callers of one symbol.
Get machine-readable output¶
Every semantic command honors the same --json contract as the rest of the CLI — structured, stable,
and free of any human decoration. This is the surface agents and CI read.
{
"a": "blake3:3f9c1a2b7d4e6f0182a35c9b7e04d1f6a83c25be9701df4368ac52e1b0947dca",
"b": "blake3:8d41e07c93b5a2f7160e4dc8395b7f02ae61c4d90837fb52e6a1c0d47983be25",
"a_indexed": true,
"b_indexed": true,
"breaking": true,
"added": ["assertNotExpired()."],
"removed": ["legacyVerify()."],
"changed": [
{ "moniker": "verifyToken().", "reason": "signature", "removed_supertypes": [] }
],
"dependency_deltas": [],
"coverage_deltas": []
}
a_indexed and b_indexed tell you whether each side actually carried a symbol graph; a side without
one is treated as empty, so its symbols read as absent rather than unchanged.
The machine contract carries no warmth
--json and --quiet output contain no glyphs, color, or celebration — that's a hard rule across
TOVIO, so an agent or pipeline parses exactly the same bytes every time.
When a file has no indexer¶
The index covers Rust, TypeScript/JavaScript, Python, and Go. A file in any other language is
simply absent from the symbol graph: the semantic diff doesn't list it, nothing errors, and
tovio diff still gives you the text view of that file. The same fallback applies to a file the
indexer can't parse or that is too large to index — a single unindexable file never costs the rest of
the graph, let alone the commit.
You always get a diff. You just don't get the symbol-level view for that file.
Make it a gate¶
The diff is advisory on its own. A protected lane can opt in to enforcing it: set
semantic_check = required on the lane's protection entry in the policy manifest, and a land that
would introduce a semantic conflict or a breaking interface change is refused with TVO-SEM-004
(or TVO-SEM-005 when the build carries no semantic layer at all — fail-closed, never waved through).
tovio explain semantic [A] [B] --into <lane> shows the diff and evaluates that gate for a target lane
without landing anything.
How it stays fast¶
The symbol graph updates incrementally as you commit — TOVIO re-indexes only the changed files,
never the whole tree. Each file's facts live in a content-addressed symbol-shard; the commit's
symbol-xref stitches them together and is what the diff compares. Because those are ordinary objects
in your store, they sync and verify like any other object, and because the layer is additive, a slow
or missing index can only ever cost you the enhanced view, never the commit itself.
Where to go next¶
- Query the symbol graph — turn a breaking verdict into the recorded list of callers and dependents.
- Version AI behavior — the same object model, applied to an agent's behavioral surface.
- Everyday guides — the plain
tovio diff,commit, andlandthis sits beside.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure