v0.3.0 Open source · MIT · runs in your browser
git diff for knowledge graphs
See what an AI pipeline actually changed in your knowledge graph — merges, renames, moves — and review it like a pull request.
npx @kgdiff/cli a.ttl b.ttl
-
Semantic, not syntactic
A merge is a merge and a rename is a rename — not one node deleted and another one added.
-
One mental map
Both versions share one stable layout. Nothing moves unless it changed.
-
Color is meaning
Five change families and gray for context. Nothing else gets a color.
-
Review is a workflow
Step through changes, replay them, mark them reviewed, export an invertible delta.
Views
Four ways to see a change
One diff, one layout, four lenses. Switch below — or press 1 to 4 inside the viewer.
Both versions in one picture. Removed things stay as dashed ghosts where they were — nothing just vanishes. Before on the left, after on the right, on one shared layout. Drag the divider across the change. The before graph morphs into the after graph, one change at a time: merges travel, edges swing, additions grow. Only what changed, plus one hop of context. The default for graphs with thousands of nodes.
Dataset: dbt lineage refactor
Semantics
Semantic, not syntactic
Text diffs see lines; triple diffs see deleted and inserted statements. kgdiff matches nodes across versions and names what happened — in a visual language where color carries meaning but is never the only cue.
- Added only in after
- Removed only in before
- Content same thing, new content
- Structure same thing, new place
- Identity which thing changed
- Context unchanged, quiet
-
Merge Identity
Duplicates fold into one. Sources stay as ghosts, an arc shows where they went, and their relations are carried over.
-
Split Identity
One term becomes several. kgdiff follows the relations to the new terms — and says so when it is only inferring.
-
Rename Content
Same node, new name. The old label stays visible, struck through, right under the new one.
-
Move Structure
A class changed parents. The old link fades to a ghost; the new one swings into place around a pivot.
-
Retarget Structure
A relation now points somewhere else: one edge that swings — not one deleted and one added.
-
Attribute change Content
Attributes, types, aliases and deprecations — counted on a badge, diffed word by word in the details.
-
Rekey Identity
The id changed, the thing did not. kgdiff matches it by label, neighbours and attributes, so its relations stay unchanged.
-
Suggestion Identity
Not sure two nodes are the same? kgdiff draws a dotted “maybe” link with its confidence and lets you decide.
Command line
In your terminal and CI
The same engine as a CLI: a colored summary for people, --json and --delta for machines, --markdown for pull requests, and a single-file HTML report that works offline.
$ npx @kgdiff/cli before.obo after.obo --match id --hierarchy is_a
One diff, many outputs
--stat- diffstat: nodes and edges added, removed, modified
--name-status- one line per change: A D M R V G S E
--json- the full machine-readable diff
--delta- an invertible patch, for
kgdiff apply --markdown- a PR comment or job summary
-o report.html- a self-contained interactive report
--exit-code- exit 1 when the graphs differ
Fail the job, explain the change
Gate ontology pull requests on review, and put a readable summary where reviewers already are. Labels are escaped: a summary can never mention people, link issues or inject HTML.
- uses: actions/checkout@v7
# Explain what changed in a pull request comment and the
# job summary, attach the interactive report…
- uses: iiwish/kgdiff@v0
with:
files: ontology.ttl
# …and fail the job when the graph changed.
fail-on-changes: true
--markdown
Formats & integrations
Bring your graph
RDF, OBO, or the JSON your graph library already exports — detected automatically and normalized, so the same graph diffs as identical however it is serialized.
RDF
- Turtle
.ttl - N-Triples
.nt - N-Quads
.nq - TriG
.trig - RDF/XML
.rdf .owl - JSON-LD
.jsonld
Labels by language preference, OWL existential restrictions as edges, blank nodes identified by content, annotations folded into edges.
OBO 1.4
- OBO
.obo
is_a, relationship, synonyms and xrefs; replaced_by and alt_id become merge hints.
JSON
- kgdiff
- Cytoscape.js
- d3 / NetworkX
- AntV G6
- graphology
- vis-network
The dialect is detected from the shape; rendering state is dropped with a warning.
One custom element, no framework. Give it two graphs (or a precomputed report) and listen to its review events.
<kgdiff-viewer mode="replay" locale="zh-CN"></kgdiff-viewer>
<script type="module">
import '@kgdiff/viewer';
const viewer = document.querySelector('kgdiff-viewer');
const text = (url) => fetch(url).then((r) => r.text());
await viewer.load({
before: { text: await text('v12.ttl'), filename: 'v12.ttl' },
after: { text: await text('v13.ttl'), filename: 'v13.ttl' },
diff: { hierarchyPredicates: ['rdfs:subClassOf'] },
});
viewer.addEventListener('kgdiff-review', (e) => {
console.log(e.detail.progress); // { reviewed: [...], total: 23 }
});
</script>
Already on AntV G6? Render the same visual language inside your own G6 graph — selection, level of detail and replay included.
import { diff } from '@kgdiff/core';
import { layoutDiff } from '@kgdiff/layout';
import { buildScene } from '@kgdiff/scene';
import { createDiffGraph } from '@kgdiff/g6';
const result = diff(before, after, { hierarchyPredicates: ['is_a'] });
const scene = buildScene(result, layoutDiff(result), { theme: 'light' });
// A G6 v5 Graph with kgdiff's encodings, hover and click-to-select.
const view = await createDiffGraph('#container', scene);
await view.selectHunk(0);
await view.playReplay({ speed: 1 });
A verifiable, invertible patch: apply(A, delta) ≅ B and apply(B, invert(delta)) ≅ A. Every op checks its precondition; it maps cleanly to KGCL and RDF Patch.
{
"format": "kgdiff.delta",
"version": 1,
"edgeIdentity": "structure",
"base": { "name": "mhd", "version": "2026-09-01", "hash": "c53:…", "nodes": 179, "edges": 351 },
"target": { "name": "mhd", "version": "2026-10-01", "hash": "c53:…", "nodes": 184, "edges": 353 },
"ops": [
{ "op": "edge.remove",
"edge": { "source": "MHD:0000019", "target": "MHD:0002003", "predicate": "has_symptom" } },
{ "op": "node.set", "id": "MHD:0000018", "field": "aliases",
"from": ["MI"], "to": ["MI", "heart attack"] },
{ "op": "edge.add",
"edge": { "source": "MHD:0000018", "target": "MHD:0002030", "predicate": "has_symptom" } }
]
}
npx @kgdiff/cli apply v1.json patch.json -o v2.json
Compare
Why kgdiff
Good tools already exist for ontologies and for data lineage. kgdiff is a visual, semantic review layer for any graph. An honest comparison, by category:
| Capability | Text diffgit diff on the files | Ontology diff toolsROBOT diff, Protégé Compare Ontologies, owl2vcs, owlcompare | Lineage diff UIse.g. Recce for dbt | kgdiffv0.1, MIT |
|---|---|---|---|---|
| Semantic changes: merge, split, rename, move | lines added / removed | axiom- or entity-level; some detect renames | models added / removed / modified | named, grouped into hunks |
| Ignores serialization noise (order, prefixes, blank nodes) | reordering shows as change | axiom-level | manifest-level | normalized graph model |
| Graph view on one stable layout | text, tables or trees | lineage DAG | any graph, both versions | |
| Visual replay of the change | step by step, reduced-motion aware | |||
| Review workflow | PR review, line by line | reports | checks and checklists in PRs | hunks, reviewed state, keyboard |
| Invertible patch format | git apply -R | varies by tool | verifiable JSON delta | |
| Any graph format | any text, compared line by line | OWL and OBO ontologies | dbt projects | RDF, OBO, six JSON shapes |
| OWL axiom-level detail | their core strength | graph-level; existential restrictions become edges | ||
| Data-level diff (rows and values) | profiles, row counts, values | out of scope |
- supported
- partly
- not supported
Summarized by category from each project's public documentation (2026); individual tools differ. Spotted a mistake? Please open an issue.
Examples
Five datasets, five kinds of change
Realistic before / after pairs, each with a machine-checked list of what kgdiff must report. Open one in the playground or run it with the CLI.
Diff your own graphs
Drop two files into the playground. Everything runs in your browser — nothing is uploaded.