# jsonpatch — RFC 6901 JSON Pointer + RFC 6902 JSON Patch (stdlib only) Two files, no third-party imports: | file | role | |---|---| | `jsonptr_patch.py` | the engine: pointer syntax/eval + the six patch operations + CLI | | `test_jsonptr_patch.py` | the verification battery (6 stages, exit code = verdict) | | `_vendor_json-patch-tests_*.json` | the official upstream test suite, vendored + sha256-pinned | ## What it does * `decode_pointer` / `unescape_token` / `escape_token` / `pointer_to_string` / `resolve` implement RFC 6901 §3/§4 (empty pointer = whole document, `~1` → `/`, `~0` → `~`, leading-zero array indices rejected, illegal escapes rejected). * `apply_patch(doc, patch)` implements RFC 6902 §4 `add`, `remove`, `replace`, `move`, `copy`, `test` and returns a **new** document — the caller's document is never mutated. * `json_equal` is RFC 6902 `test` equality: `1 == 1.0`, `true != 1`, `null == null`, object member order irrelevant, array order significant. Semantics that the official suite forced (they are the interesting part): * `add` **never** creates missing intermediate objects — `add /a/b` against `{}` is an error (`spec_tests.json` "A.12. Adding to a Non-existent Target"). A first draft of this engine created parents on the fly and passed its own Appendix-A cases; the vendored suite caught it. * array `add`: index `== len` is legal, `> len` is an error, `-` appends. * `move` into a strict descendant of `from` is an error; `move /a → /a` is a no-op. * a patch is atomic: if any op fails, nothing is applied. ## Run it ```sh python3 test_jsonptr_patch.py # full battery, incl. mutation teeth (≈15 s) python3 test_jsonptr_patch.py --no-teeth # skip stage 6 python3 jsonptr_patch.py --selftest # engine's own Appendix-A + 6901 cases python3 jsonptr_patch.py --doc doc.json --patch patch.json ``` CLI exit codes: `0` success, `1` RFC 6901/6902 error, `2` usage error. ## Verification See `EVIDENCE.txt` for the measured numbers and the exact commands that produced them. The battery exits non-zero if any of the following regresses: the vendored suite's sha256, any of the 108 official cases, the 54 selftest cases, the atomicity/purity checks, the seeded fuzz invariants, or if any of the 8 source mutants survives.