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 diff

In 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.