Objects: explicit no-op marking, enforceable tombstone exception, RTO4b2a reset clarifications + UTS cases - #515
Open
sacOO7 wants to merge 3 commits into
Conversation
…; clarify tombstone and RTO4b2a reset semantics
…ro/empty objects, and empty-root reset
There was a problem hiding this comment.
Pull request overview
This PR refines the LiveObjects/Objects specification around no-op updates and tombstone behavior (making tombstone teardown reliably enforceable), clarifies RTO4b2a reset edge cases, and adds UTS unit cases to cover previously untested branches.
Changes:
- Makes no-op marking explicit (
*.noop = true) for counter/map diffs and adds a normative carve-out so tombstone diffs are not collapsed to no-op. - Clarifies
RTO4b2abehavior for tombstoned entries and the “empty root reset” case. - Adds 7 UTS unit test cases covering empty-diff no-ops, tombstone-on-zero/empty behavior, empty synthetic list publishAndApply behavior, listener teardown, and empty-root reset behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| specifications/objects-features.md | Updates spec wording for explicit no-op marking, tombstone carve-outs, and RTO4b2a reset clarifications |
| uts/objects/unit/realtime_object.md | Adds UTS case for RTO20d4 (empty synthetic list skipping sync wait) |
| uts/objects/unit/objects_pool.md | Adds UTS case for RTO4b2a (empty-root reset emits no update) |
| uts/objects/unit/live_object_subscribe.md | Adds UTS case asserting tombstone update delivery + listener deregistration on already-zero counter |
| uts/objects/unit/internal_live_map.md | Adds UTS cases for tombstone-empty-map update and RTLM22c empty-diff no-op |
| uts/objects/unit/internal_live_counter.md | Adds UTS cases for tombstone-zero-counter update and RTLC14c zero-delta no-op |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| - `(RTO4b1)` All objects except the one with id `root` must be removed from the internal `ObjectsPool` | ||
| - `(RTO4b2)` The data for the `InternalLiveMap` with id `root` must be set to the value described in [RTLM4c](#RTLM4c). Note that the client SDK must not create a new `InternalLiveMap` instance with id `root`; it must only clear the internal data of the existing `InternalLiveMap` with id `root` | ||
| - `(RTO4b2a)` Emit a `LiveMapUpdate` object for the `InternalLiveMap` with ID `root`, with `LiveMapUpdate.update` consisting of entries for the keys that were removed, each set to `removed`, and without populating `LiveMapUpdate.objectMessage` | ||
| - `(RTO4b2a)` Emit a `LiveMapUpdate` object for the `InternalLiveMap` with ID `root`, with `LiveMapUpdate.update` consisting of entries for the keys that were removed, each set to `removed`, and without populating `LiveMapUpdate.objectMessage`. Only the keys of non-tombstoned entries are reported as `removed`, consistent with the non-tombstoned-visibility rule in [RTLM22b](#RTLM22b): entries that were already tombstoned were not part of the user-visible map data, so their removal is not reported. If no keys were removed (that is, the `root` map was already empty), the computed `LiveMapUpdate.update` contains no changed keys and is therefore a no-op per [RTLM22c](#RTLM22c) ([RTLO4b4b](#RTLO4b4b)), so no update is emitted. |
sacOO7
added a commit
to ably/ably-cocoa
that referenced
this pull request
Aug 11, 2026
…on tail (RTO4b4/RTO5c) - Port the seven no-op-package UTS unit cases: RTLC14c/RTLM22c (zero-delta/ empty diffs are no-op updates), RTO20d4 (empty synthetic list skips the RTO20e wait), RTLO5 tombstone-of-zero/empty-object cases (the tombstone update must not be no-op-marked), RTLO4b4c3c teardown for a zero-valued counter, and RTO4b2a (reset of an already-empty root emits no update). Production already conforms at every site; the RTO20d4 port drives the real publishAndApply pipeline via a new optional ObjectsUTSCoreSDK publishHandler. - Extract the shared nosync_completeSync() completion tail used by both the ATTACHED(HAS_OBJECTS=0) path (RTO4b4) and the OBJECT_SYNC completion path (RTO5c), so the two cannot drift; add a native test proving the ATTACHED path resolves parked publishAndApply sync waiters. - Retire the native RTO5a5 twin superseded by the UTS port (bumped to two objects to keep its multi-object coverage); cross-link the counter tombstone-bypass comments to the RTLC14c zero-delta exception. Spec changes: ably/specification#515 Companion ably-js fix: ably/ably-js#2288
sacOO7
added a commit
to ably/ably-java
that referenced
this pull request
Aug 11, 2026
Port the seven no-op-package UTS cases: RTLC14c/RTLM22c (zero-delta/empty diffs are no-op updates, never delivered), RTO20d4 (empty synthetic list skips the RTO20e sync wait), the RTLO5 tombstone-of-zero/empty-object cases and the RTLO4b4c3c zero-valued-counter teardown case (covering BaseRealtimeLiveObject.tombstone()'s NoOp-synthesis branch for the first time), and RTO4b2a (reset of an already-empty root emits no update; verified with a second-pool liveness control via a backward-compatible optional target parameter on the ObjectsPoolTest processAttached helper). Production already conforms at every site; test-only change. Spec changes: ably/specification#515 Companion ably-js fix: ably/ably-js#2288
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.
Note
Stacked on #514 (
objects/get-sync-wait-failure-and-sync-fixes) — review after that PR; only the two commits of this branch are new.Problem
Follow-up to #514, addressing its review feedback and the gaps found while bringing ably-js into conformance with the no-op update clauses:
RTLC14c/RTLM22csaid "return a … update marked as a no-op per RTLO4b4b", while every other no-op-producing clause in the spec (RTLC6e1, RTLC9h, RTLC16d, RTLM6e1, RTLM7h, RTLM16b, …) uses the explicit form "return aLive*Updateobject with*.noopset totrue". Divergent SDK interpretations were possible. (Raised by Copilot in Objects: specify get() sync-wait failure (RTO23c1), malformed sync serial handling (RTO5a6) and related clauses, with UTS unit cases #514 (comment).)RTO4b2a(the ATTACHED/no-objects reset update) was silent on two edges: whether already-tombstoned entries are reported asremoved, and what happens when no keys were removed (an already-empty root). ably-cocoa filters tombstoned entries and suppresses the empty update; ably-java suppresses via its diff; ably-js emitted a spurious{update: {}}.RTO20d4, tombstoning an already-zero/empty object (the branch every SDK special-cases — untested in all three), or the empty-root reset.Changes
specifications/objects-features.mdRTLC14c/RTLM22c(reworded)Live*Updateobject with*.noopset totrue([RTLO4b4b]), as in [RTLC9h] / [RTLM16b]" — matching the spec-wide convention. The tombstone exception is now normative and mechanical: the exception "must not be applied when the diff is computed for a tombstone per [RTLO4e5]: the resulting tombstone update ([RTLO4b4e]) must not be marked as a no-op", so it is delivered and drives the [RTLO4b4c3c] teardown even when the data was already zero/empty.RTO4b2a(clarified)removed(consistent with [RTLM22b]'s non-tombstoned-visibility rule), and if no keys were removed the update has no changed keys and is a no-op per [RTLM22c]/[RTLO4b4b], so nothing is emitted.uts/objects/unit/— 7 new unit casesinternal_live_counter.md:RTLC14c/zero-delta-diff-is-noop-0(identical-state override → no-op, notamount: 0);RTLO5/tombstone-zero-value-counter-emits-update-0(the exception's flip side: tombstoning a zero counter still yields a non-no-op update withtombstone == true,amount == 0).internal_live_map.md:RTLM22c/empty-diff-is-noop-0(identical non-tombstoned entries — only the timeserial differs, which RTLM22b3 does not compare);RTLO5/tombstone-empty-map-emits-update-0(all-entries-tombstoned map).realtime_object.md:RTO20d4/empty-synthetic-list-skips-sync-wait-0(all-null ACK serials while SYNCING → the operation resolves without the sync ever completing, proving the RTO20e wait was skipped).live_object_subscribe.md:RTLO4b4c3c/tombstone-zero-value-counter-tears-down-0(subscriber receives the zero-amount tombstone update and listeners are deregistered).objects_pool.md:RTO4b2a/reset-of-empty-root-emits-no-update-0(with a second-pool liveness control).SDK status
feature/liveobjects-implementationtombstone()NoOp-synthesis)