SHAPE

SHIFT

Back to homepage

Universal Semantic Merging

Most merge tools begin with text. They compare two edited files against a base file, look for overlapping lines, and ask a person to resolve anything that lands in the same place.

That is useful, but it is also a very small view of what changed. Two edits can touch the same line while changing different facts. Two edits can touch different lines while breaking the same symbol, type, import, or runtime contract.

Semantic merge treats the source as a graph of meaning first and a string of text second. It asks which symbols, imports, declarations, type contracts, call sites, ownership regions, and evidence gates changed. Text is still the thing we write back to disk, but it is not the only thing the merge decision sees.

A text merge sees both branches landing on the same line and escalates to a human.

Example: Same-Line Imports

A line-based merge sees the same original line changed twice. A semantic merge can switch between the base, each branch, and the admitted output while keeping attention on the changed symbols.

Line Diff View
 import { readUser } from "./api";   export function run() {   return readUser(); }
Base fileThe original file has one import specifier and one call site.
The text merge view only knows that line 1 was replaced by two different strings.
Same Line, Different Facts
1import { readUser } from "./api";2 3export function run() {4  return readUser();5}
Base factreadUser is the existing import and call-site dependency.
A semantic diff highlights the changed symbols and the admission reason, not only the line that changed.
The conflict disappears when the line is interpreted as an import set with two independent additions.

The important output is not only the merged text. The system can also produce an admission record.

{
  "kind": "import-specifier-addition",
  "module": "./api",
  "base": ["readUser"],
  "leftAdds": ["writeUser"],
  "rightAdds": ["deleteUser"],
  "decision": "merge"
}

That record is what makes the merge inspectable. It says why this was safe: neither branch removed the base import, neither branch introduced a duplicate local binding, and the output keeps all three specifiers.

Example: Typed Rebase

The harder case is when one branch changes a contract and the other branch builds on the old shape.

A useful semantic merge does not blindly concatenate those edits. It needs to understand that name became fullName, rebase the new helper through that contract change, and then run a type gate before admitting the output.

Line Diff View
 export interface User {   id: string;   name: string; }   export function greet(user: User) {   return `Hello ${user.name}`; }
Base fileThe original public contract exposes name and greet reads it directly.
The text merge view can show the overlap, but it cannot prove the semantic adaptation.
Typed Rebase Through A Rename
1export interface User {2  id: string;3  name: string;4}5 6export function greet(user: User) {7  return `Hello ${user.name}`;8}
Base contractThe public field is name, and greet reads that field directly.
A semantic diff can show the helper addition, the public rename, and the admitted output as one inspectable merge claim.
A typed rebase is not just a text patch. The adapted output has to pass a compiler or diagnostics gate before it can be admitted.

This is the difference between a merge result and a merge claim. The result is the source text above. The claim is narrower: this particular adaptation is acceptable because the symbol rename was detected, the helper was rewritten through that rename, and the project still type-checks.

What We Gain

Semantic merge reduces false conflicts, but that is not the whole point. The larger gain is that the tool can separate mechanical overlap from meaningful overlap.

If two agents add independent imports, merge them. If two agents edit the same public type in incompatible ways, block. If a branch moves a symbol and another branch imports it, ask for module-resolution evidence. If a helper can be rebased through a typed rename, do it only with diagnostics.

That gives collaboration a better shape. Agents can work in parallel without treating every shared line as danger, and humans can spend attention on the cases where meaning is actually uncertain.