Skip to content

docs: scope the reference-idempotence promise to what holds - #13

Merged
b2m9 merged 2 commits into
mainfrom
docs/scope-reference-idempotence
Aug 13, 2026
Merged

docs: scope the reference-idempotence promise to what holds#13
b2m9 merged 2 commits into
mainfrom
docs/scope-reference-idempotence

Conversation

@b2m9

@b2m9 b2m9 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Item 2 of the four flagged design choices. The verdict there was keep the behavior, fix the claim — so this is documentation, one test comment, and one law. No behavior change.

The defect

The Semantics list contradicted itself in consecutive bullets:

:195 — replaced values count as unchanged only when they are the very same reference
:196 — Every valid delta is deterministic and idempotent: re-applying the same delta returns the previous result by reference, so a store can drop duplicate frames with one equality check

Only the first is unconditional. The second holds for the same delta object — but websocket reducers are the headline use case in the intro, and they decode every frame afresh.

Where the line actually falls

A replacement is unchanged only under Object.is. Ordinary JSON parsing satisfies that for primitives and never for objects or arrays. Measured:

re-decoded frame carries same reference?
scalars only
keyed list
replace path → string / number / null
replace path → object / array
unkeyed array

All are still toEqual; only the reference churns.

My first attempt drew the line at "carries a replacement," which review caught as wrong — a replace path holding a primitive costs nothing. The line falls at what decoding allocates.

Changed

Three sites, because the claim lived in three places and the third is the one that would regenerate it:

  • README Semantics bullet — states the Object.is rule, then the ordinary-JSON consequence, then the dedup guidance scoped to when it actually works.
  • test/laws.test.ts — the comment inside "re-applying the same delta returns the same reference" carried the overclaim as the law's own justification. It now says the law applies the same delta object twice and points at the boundary.
  • AGENTS.md — the "deterministic and idempotent" constraint had the same unconditional wording, plus an explicit "Do not promise more than that."

Plus a law, a re-decoded frame keeps the reference only for Object.is-equal replacements, pinning three cases: merged values re-decoded are toBe; an unkeyed array and a configured replace path are not.toBe but toEqual; a primitive at a replace path is toBe. Both replacement routes are covered so neither can start comparing by value unnoticed.

Review

Two rounds with GPT-5.6-sol. Round one caught the "carries a replacement" overcorrection; round two grounded the rule in Object.is rather than allocation, added the object-valued replace case, and replaced a cast of invalid data into Delta<Shape> with a dedicated typed merger.

118 tests, vp run check green.


Summary by cubic

Scopes the reference-idempotence claim to what actually holds and pins the boundary with a law; no behavior change. Prevents overstating dedup-by-reference for re-decoded frames (e.g., websocket reducers).

  • README: states Object.is as the equality rule for wholesale replacements, so primitive replacements are unchanged without shared refs; scopes idempotence to re-applying the same delta object and notes re-decoded frames keep refs only when all replacements are Object.is-equal (primitives, not objects/arrays). Dedup by a single equality check works only while no replacements are objects/arrays.
  • AGENTS.md: aligns the constraint with the same scope; explicitly says not to promise more.
  • test/laws.test.ts: updates the justification in the same-delta-object law; adds a law that fixes the boundary: merged values remain toBe after re-decode; unkeyed arrays and object-valued replace paths are not.toBe but toEqual; primitive replace remains toBe.

Written for commit 1ff0ce5. Summary will update on new commits.

Review in cubic

The Semantics list contradicted itself in consecutive lines: one bullet
said replaced values count as unchanged only when they are the very same
reference, the next promised that re-applying any valid delta returns
the previous result by reference, so a store could drop duplicate frames
with one equality check.

Only the first is unconditional. Websocket reducers are the headline use
case and they decode every frame afresh, and a replacement is unchanged
only under Object.is. Ordinary JSON parsing satisfies that for
primitives but allocates a fresh object or array every time, so a frame
carrying an unkeyed array, a non-plain object or an object-valued
`replace` subtree yields an equal result with a new reference. A
`replace` path holding a string or a number costs nothing, which is why
the line falls at what decoding allocates rather than at replacement.

The claim also lived as the justification inside the law that appears to
prove it, and as a constraint in AGENTS.md, which is where a future
writer would go to restate it. All three now say the same thing, and a
law pins both sides of the boundary through an unkeyed array and a
configured replace path, so neither route can start comparing by value
unnoticed.

No behavior change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Re-trigger cubic

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
b2m9 merged commit bc36811 into main Aug 13, 2026
3 checks passed
@b2m9
b2m9 deleted the docs/scope-reference-idempotence branch August 13, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant