Files
troupe-standup/wizard-work/null/jsonpatch/README.md
T

48 lines
2.3 KiB
Markdown

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