Catch breaking interface changes before landing
tovio semantic diff.
Written By Dustin
Last updated 22 minutes ago
tovio semantic difftovio semantic diff <a> <b>Compares the symbol graphs of two commits and flags interface changes, including breaking ones. With no arguments it diffs your commit against its first parent — the natural "what did my last commit break?" question.
Why it catches what review misses
A textual diff shows a one-line change. It does not tell you that the line removed a parameter from a public function forty callers depend on. A semantic diff does, because it compares interfaces rather than characters.
Because the underlying stitch drops file-local symbols, every name the diff reports is an exported one. A private helper you renamed will not appear — which is the point.
What counts as breaking
Two things, and only two:
- an exported symbol was removed;
- an exported symbol changed — its signature, its supertypes, or both.
Additions are never breaking. Nor are dependency or test-coverage shifts, which the diff reports in their own clearly-marked advisory sections so they cannot be mistaken for an interface break.
Run it on agent work
This is the highest-value habit in the collection. A model that has confidently "cleaned up" a signature produces a small, tidy-looking diff and a large breaking change. semantic diff is the check that catches it.
tovio log --entity agenttovio semantic diffIn the editor
VS Code has a Semantic view with a Run Interface Diff action; each changed symbol is a row you can copy the moniker from. The JetBrains plugin renders the same result as a semantic panel in its Insights tool window.
It reports; it does not gate
semantic diff exits 0 whether or not it found a breaking change — the verdict is in the output, not the exit code. Script against --json and read the breaking field rather than the exit status.
The one case where it exits non-zero is unrelated to the verdict: on a partial clone, when you name a commit whose symbol index was never fetched and the origin is unreachable. It surfaces that honestly rather than rendering a misleading "everything was added" diff. tovio fetch --complete fills the gap.
Enforcing it
A protected lane can require the check, so a breaking change is refused at the land gate rather than noticed later. That is opt-in and off unless someone turns it on.