Compares two pasted JSON documents, BEFORE and AFTER, and lists every value that differs as strict JSON, one entry per changed path, with the old and the new value. Paths are JSON Pointers, types are strict (the string "1" is not the number 1, null is not a missing key, an empty array is not an empty object), arrays are compared by index with no move detection, object key order and number spelling (1.0, 1e2) are not changes, and a document that is not valid JSON is refused, never repaired. Use to see exactly what an API response, config file, fixture or snapshot changed between two versions, to write the assertion for a test, to review a data migration, or to feed an audit log with the list of changed fields.
JSON Diff Paths is a tested SKILL.md that compares two pasted JSON documents, BEFORE and AFTER, and lists every value that differs as strict JSON, one entry per changed path, with the old and the new value; an agent buys it once for $0.01 over x402.
Not for
Producing a patch that turns one document into the other (no move or copy operations, no RFC 6902 output), judging whether a change is good, or comparing the documents with the outside world. It reads two pasted JSON documents at a time and refuses text that is not valid JSON instead of repairing it.
Tested, honestly
Tested 2026-10-09 with a strong and a weak model.
With and without the skill
Results with and without the skill, for Sonnet and Haiku |
| with | without | with | without |
|---|
| Pairs diffed right (24 pairs) |
| Pairs diffed right (24 pairs) | 24/24 | 24/24 | 24/24 | 24/24 |
|---|
Same request on both sides. On the two broken documents both sides are scored on any clear refusal with no diff, so the missing CANNOT DIFF line of Haiku is not counted here. Our request already spelled out the shape, the pointer paths and the type rules, and with that both models got every pair right without the skill; the skill adds no measured gain on this set. We did not test a shorter request.
Same cases and the same checks with and without the skill. The cases are ours, written around what the skill is for; with a handful of cases, a difference of one or two is within noise.
- SonnetStrong model, claude-sonnet-5-5
- Right on all 24 pairs, checked by code against a diff computed by our script: "1" kept apart from 1 and null apart from a missing key, 1.0 against 1 and a new key order not reported, keys with a slash, a tilde or an empty name escaped in the pointer, a whole added object as one entry, arrays compared by position, a sentence planted in a value treated as data, and both broken documents refused with the CANNOT DIFF line.
- HaikuWeak model, claude-haiku-5-5
- Right on 23 of 24 pairs, checked by code. Every diff was exact; on the document cut off in the middle of a key it refused correctly, but in a plain sentence without the CANNOT DIFF line a program looks for.
Full test summary
Example
Our own test text, before and after the skill ran. Excerpts only.
English · claude-sonnet-5-5
Before
BEFORE:
{
"id": "7",
"qty": 3,
"active": true,
"note": null,
"price": 9.5,
"sku": "A-100"
}
AFTER:
{
"id": 7,
"qty": "3",
"active": "true",
"note": 0,
"price": 9.50,
"sku": "A-100"
}
After
{"changes": [{"path": "/id", "op": "changed", "old": "7", "new": 7}, {"path": "/qty", "op": "changed", "old": 3, "new": "3"}, {"path": "/active", "op": "changed", "old": true, "new": "true"}, {"path": "/note", "op": "changed", "old": null, "new": 0}]}
Bulgarian · claude-sonnet-5-5
Before
BEFORE:
{"град": "\u0421\u043e\u0444\u0438\u044f", "население": 1200000, "райони": ["Витоша", "Лозенец"], "ръководител": "Иван Петров"}
AFTER:
{"град": "София", "население": "1200000", "райони": ["Витоша", "Лозенец", "Младост"], "ръководител": "Мария Колева", "столица": true}
After
{"changes": [{"path": "/население", "op": "changed", "old": 1200000, "new": "1200000"}, {"path": "/райони/2", "op": "added", "new": "Младост"}, {"path": "/ръководител", "op": "changed", "old": "Иван Петров", "new": "Мария Колева"}, {"path": "/столица", "op": "added", "new": true}]}
Spanish · claude-sonnet-5-5
Before
BEFORE:
{"ciudad": "Madrid", "calle": "Calle Mayor ", "codigo": "28013", "pais": "españa"}
AFTER:
{"ciudad": "Madrid", "calle": "Calle Mayor", "codigo": "28013", "pais": "España", "tel": "+34 600 000 000"}
After
{"changes": [{"path": "/calle", "op": "changed", "old": "Calle Mayor ", "new": "Calle Mayor"}, {"path": "/pais", "op": "changed", "old": "españa", "new": "España"}, {"path": "/tel", "op": "added", "new": "+34 600 000 000"}]}
German · claude-sonnet-5-5
Before
BEFORE:
{
"bestellung": {
"nr": "2026-114",
"positionen": [
{"artikel": "Stuhl", "menge": 2, "preis": 45.0},
{"artikel": "Tisch", "menge": 1, "preis": 120}
],
"versand": {"art": "Paket", "frei": false}
},
"kunde": "Müller"
}
AFTER:…
After
{"changes": [{"path": "/bestellung/positionen/0/menge", "op": "changed", "old": 2, "new": 3}, {"path": "/bestellung/positionen/1/preis", "op": "changed", "old": 120, "new": 119.99}, {"path": "/bestellung/versand/frei", "op": "changed", "old": false, "new": true}]}
What is in the file
- The answer
- Paths
- What counts as a difference
- When you must refuse
- What is not your job
- Work in this order
- Short examples
Languages
English, Bulgarian, Spanish, German. Tried in: English, Bulgarian, Spanish, German.
License
Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Full terms.
Versions
Current version 1.0.0, updated 2026-10-09. Whoever bought an earlier version gets new ones free through the same re-download token.
v1.0.0 · 2026-10-09
First release: compares two pasted JSON documents (BEFORE and AFTER) and lists every differing value as strict JSON, one entry per path (JSON Pointer), with kinds added, removed and changed and the old and new values copied with their types. Types are strict, null is not a missing key, arrays go by index with no move detection, an added or removed value is one entry, a changed type is one entry, key order and number or escape spelling are not changes, and a document that is not valid JSON is refused with a CANNOT DIFF line. Written from the § 1 row of the batch 4 plan (the writer brief for this slug had not been appended yet). Expected entries in the tests are computed by a small diff function (test/diff.mjs), never by hand, and the check compares the set of entries in any order. No model run yet: the baseline, the price check and the sentence on what Sonnet gains are still to be written; the price 30000 is a placeholder.
FAQ
Reordered arrays, or an item inserted at the front: what comes back?
By index, with no move detection. Putting a new item at the front changes every position after it and adds one at the end, and the list says exactly that, so it matches what a position-based assertion in a test will see.
What happens when one document has a trailing comma, a comment or is cut off?
It refuses with one line that names the document and the first fault. It does not strip the comment or fix the comma, because any repair would invent changes that are not in the text.
Is the string 1 the same as the number 1, and null the same as a missing key?
No to both. The string 1 and the number 1 are a change of type, null against zero or against an empty string is a change, and a key that holds null is a value that a missing key does not have, so adding or dropping it is a separate entry.
Does it help Claude Sonnet?
A cent is the price because we measured no gain. Both Claude models compared twenty-four pairs of documents, guided by the file and then cold, and a script checked every entry against a diff computed by code. Our request already spelled out the shape, the pointer paths and the type rules, and with that both models got all 24 right unaided. What the file adds is a fixed refusal line, CANNOT DIFF, that a program can catch: Haiku once refused in a plain sentence instead. We did not test a shorter request.