docs: correct nine claims the code does not deliver - #14
Merged
Conversation
A sweep for statements stronger than the implementation, prompted by the same defect as the reference-idempotence bullet, found nine across the API, Deletes, Errors, Semantics and Guardrails sections. Cost is not delta-proportional. Unmentioned subtrees are still never traversed, which is the guarantee worth keeping, but every recursively merged plain object whose result changes is shallow-copied across its own fields, changed ancestors included: changing one existing field of a five-hundred-field object reads five hundred and one. Keyed reconciliation indexes every delta item, scans every base item and builds a candidate list. AGENTS.md carried the claim as a constraint, now named for what it actually forbids. Replacement is not entirely uninspected. At `items[]` reconciliation reads the item's identity and `$delete` before the boundary applies, because that is what decides which item is replaced. 'keyfold does not inspect untrusted data' was false, and 'never validates' would have been false too: it runs limited protocol checks during reconciliation. It validates no application schema, which is the claim that holds. A cyclic input does not simply overflow; only a cycle the fold recurses through does. One in an unmentioned branch, at a replace path, or in an unkeyed array passes through, now pinned by a test. A keyBy policy engages only when the delta at that path is an array. A plain object there merges with a plain-object base rather than replacing it, and reconciliation validates base items as well as delta items. KeyfoldMergeError does not cover every contract violation. Undetected input passes, and a recursed cycle raises a native RangeError. A tombstone carrying an unsafe key does not throw; unsafe keys are ignored there as everywhere. DELETE_TOKEN remains ordinary data inside a replacement or an unkeyed array, which the Deletes section already said. 'Matching items merge in place' read as mutation. No behavior change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b2m9
added a commit
that referenced
this pull request
Aug 13, 2026
The bullet above the reference-idempotence one said replaced values count as unchanged only when they are the very same reference. That is the consequence, not the rule: equality is Object.is, so an equal primitive at a replace path is unchanged without any shared reference. Both bullets now rest on the same premise. Found while sweeping the rest of the file for claims stronger than the code delivers, which is #14. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b2m9
added a commit
that referenced
this pull request
Aug 13, 2026
Both claims corrected in #14 were ones I first stated backwards while reading the code, and neither had a test that would have caught it. A keyBy policy engages only when the delta at that path is an array. I had written that a non-array replaces the list; it does not. It follows ordinary merge semantics, so a plain object folds onto a plain-object base and returns it by reference when nothing changes, and replaces only when the base is something else. An `items[]` replacement is verbatim only after reconciliation has decided which item it applies to. I had written that the value goes in without inspection; a missing identity, a duplicate, or a malformed tombstone all throw first, and a tombstone still removes its item rather than being swapped in. The existing tests varied the base shape at a keyed path and covered the happy path for item replacement, so neither boundary was pinned from the delta side. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A follow-on from #13. Reviewing the reference-idempotence overclaim surfaced the same defect class elsewhere, so I swept the whole README and AGENTS.md. No behavior change — documentation, one comment, one test.
Nine statements were stronger than the implementation. Each was verified against running code before and after the edit.
replacepath "swaps in whatever the delta supplies without inspecting it"items[], reconciliation validates the item before the boundary applieskeyfolddoes not inspect untrusted data"keyBypolicy on the wrong shape "usually throws"{a:1}+{b:2}→{a:1,b:2}); only an array reaches reconciliationKeyfoldMergeErrorcovers "base or delta data [that] violates the contract"RangeErrorDELETE_TOKEN"can no longer be stored as ordinary field data"The AGENTS.md constraint was titled "Work remains delta-proportional" — the same false claim, in the file a future writer would consult to restate it. It is now named for what it actually forbids: never recursively traverse an unmentioned subtree.
Test
One new test pins the cyclic-input limit at all four positions, since the README now makes a specific promise about it: a cycle in an unmentioned branch, at a
replacepath, and inside an unkeyed array all pass through untouched, while one the fold recurses through raisesRangeError.Review
Three rounds with GPT-5.6-sol, and it caught me overcorrecting in each one — "carries a replacement", then "never validates", then "unsafe keys are ignored everywhere". Precise statements about these semantics are genuinely hard to write, which is the honest argument for pinning them with tests rather than prose wherever possible.
Not included: the replacement-equality premise at README:197, which belongs with #13 since that PR rewrites the adjacent line.
118 tests,
vp run checkgreen.Summary by cubic
Corrects nine documentation claims that overstated
keyfoldsemantics and renames an agent constraint; behavior is unchanged. Adds a test pinning the documented cycle limit.README.mdandAGENTS.md; one new test intest/merge.test.ts; no runtime changes.items[], reconciliation still reads identity and tombstone form before the boundary applies.keyBypolicy engages only when the delta at that path is an array; a non-array delta merges; when the base is an array, base items are validated too.KeyfoldMergeErrorcovers only detected keyed-list protocol violations; other invalid inputs may pass; a cycle the fold recurses through raises nativeRangeError.DELETE_TOKENremains data inside replacements and unkeyed arrays.RangeError.Written for commit df997fd. Summary will update on new commits.