Skip to content

[AIT-30] LiveObjects Path-based API spec#427

Open
VeskeR wants to merge 9 commits into
integration/liveobjects-path-based-apifrom
AIT-30/liveobjects-path-based-api-spec
Open

[AIT-30] LiveObjects Path-based API spec#427
VeskeR wants to merge 9 commits into
integration/liveobjects-path-based-apifrom
AIT-30/liveobjects-path-based-api-spec

Conversation

@VeskeR
Copy link
Copy Markdown
Contributor

@VeskeR VeskeR commented Feb 24, 2026

Note: This PR is based on #470; please review that one first.

Resolves AIT-30.

@VeskeR VeskeR force-pushed the AIT-30/liveobjects-path-based-api-spec branch from 13dee45 to 1e518c6 Compare February 24, 2026 13:46
@VeskeR VeskeR force-pushed the AIT-30/liveobjects-path-based-api-spec branch from 1fa3eeb to 46261f4 Compare February 24, 2026 15:52
@VeskeR VeskeR force-pushed the AIT-313/protocol-v6-state-message branch 3 times, most recently from 49f0364 to 47a9d51 Compare February 27, 2026 15:52
Base automatically changed from AIT-313/protocol-v6-state-message to main February 27, 2026 15:53
@ttypic ttypic force-pushed the AIT-30/liveobjects-path-based-api-spec branch from 46261f4 to 3608895 Compare March 9, 2026 10:54
@github-actions github-actions Bot temporarily deployed to staging/pull/427 March 9, 2026 10:55 Inactive
@lawrence-forooghian lawrence-forooghian force-pushed the AIT-30/liveobjects-path-based-api-spec branch from 3608895 to b4ad764 Compare May 12, 2026 18:41
@github-actions github-actions Bot temporarily deployed to staging/pull/427 May 12, 2026 18:41 Inactive
@lawrence-forooghian lawrence-forooghian added live-objects Related to LiveObjects functionality. labels May 12, 2026
@lawrence-forooghian lawrence-forooghian changed the base branch from main to rename-channel-objects-to-object May 12, 2026 19:37
@lawrence-forooghian lawrence-forooghian force-pushed the rename-channel-objects-to-object branch 2 times, most recently from 7738c92 to fa2a54e Compare May 12, 2026 19:45
Comment thread specifications/objects-features.md Outdated

### LiveCounterValueType

A `LiveCounterValueType` is an immutable blueprint for creating a new `LiveCounter` object. It stores the desired initial count value and is consumed when passed to a mutation method such as `LiveMap#set` ([RTLM20](#RTLM20)) or as an entry value in `LiveMap.create` ([RTLMV3](#RTLMV3)).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

an immutable blueprint

I kind of prefer LiveCounterBlueprint — it indicates that it's not a LiveCounter but something else, whereas ValueType doesn't communicate very much (it's… what, a LiveCounter that is a value type?)

Copy link
Copy Markdown
Collaborator

@lawrence-forooghian lawrence-forooghian May 18, 2026

Choose a reason for hiding this comment

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

or LiveCounterTemplate ("template" is a bit overloaded in some languages, but "blueprint" is perhaps a bit flowery)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I asked Claude to have a think

My top picks if I had to shortlist five:

  1. LiveCounterBlueprint — clear metaphor, distinctive, distinct from anything
    in software vocab.
  2. LiveCounterSeed — short, evocative, fits the "grows into" semantic.
  3. LiveCounterRecipe — pleasant metaphor, captures "follow these instructions
    to produce".
  4. LiveCounterIntent — most honest about the actual semantic; mirrors RTLCV1.
  5. LiveCounterTemplate — most conventional; baggage but familiar.

Of these, Blueprint and Seed feel the most distinctive and unambiguous to me.
Seed has the bonus of being short.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Isn't it what we call "CreateParams" in other SDKs (Chat AFAIK)? LiveCounterCreateParams is recognizable by everyone I guess.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah that works too

- `(RTPO5)` `PathObject#get` function:
- `(RTPO5a)` Expects the following arguments:
- `(RTPO5a1)` `key` `String` - the key to navigate to
- `(RTPO5b)` If `key` is not of type `String`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40003, indicating that the key must be a `String`
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think I've expressed it before but I think that these sorts of spec points are starting to pollute the spec a bit (there's quite a lot of them); it feels like there could, somewhere, be a single generic spec point saying that if a language without compiler-guaranteed argument types wishes to perform validation of the input types then if the validation fails it should throw an error with statusCode 400 and code 40003.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That said, we do seem to have a distinction between 40003 ("invalid parameter value") and 40013 ("Invalid message data or encoding") but to me it feels like they both represent the same class of error when thrown client-side (user passed an invalid argument to an SDK method)

Comment thread specifications/objects-features.md Outdated
- `(RTLMV4c)` If any of the values in the internal `entries` are not of an expected type, the library should throw an `ErrorInfo` error with `statusCode` 400 and `code` 40013, indicating that such data type is unsupported
- `(RTLMV4d)` Build entries for the `MapCreate` object. For each key-value pair in the internal `entries` (if present), create an `ObjectsMapEntry` for the value:
- `(RTLMV4d1)` If the value is of type `LiveCounterValueType`, consume it per [RTLCV4](#RTLCV4) to generate a `COUNTER_CREATE` `ObjectMessage`. Collect the generated `ObjectMessage` and set `ObjectsMapEntry.data.objectId` to the `objectId` from the `ObjectMessage`
- `(RTLMV4d2)` If the value is of type `LiveMapValueType`, recursively consume it per [RTLMV4](#RTLMV4) to generate `ObjectMessages`. Collect all generated `ObjectMessages` and set `ObjectsMapEntry.data.objectId` to the `objectId` from the outermost `MAP_CREATE` `ObjectMessage`
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment thread specifications/objects-features.md Outdated
- `(RTO22b)` `CHANNEL` - an operation received over a Realtime channel
- `(RTO24)` Internal `PathObjectSubscriptionRegister` - manages path-based subscriptions for `PathObject#subscribe` ([RTPO19](#RTPO19))
- `(RTO24a)` The `RealtimeObject` instance maintains a single `PathObjectSubscriptionRegister` that manages all path-based subscriptions for the channel
- `(RTO24b)` When a `LiveObject` in the `ObjectsPool` emits a `LiveObjectUpdate` (per [RTLO4b4](#RTLO4b4)), the `PathObjectSubscriptionRegister` must determine which subscriptions should be notified:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Presumably also we want to exclude updates with noOp set to true? would be good if we could keep the "don't do anything on noOp events" logic in a single place instead of having to repeat it here though

lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that a subscription never
emits two events for the same LiveMapUpdate. One particular case in
which this would otherwise happen is the scenario when, after the user
subscribes for path ["map"] which contains a map, this map then emits an
update for some key "myKey", which without this exclusion mechanism
would emit two events to that subscription: one for "map" and one for
"map.key".

If that _is_ the only reason that this mechanism exists, then it seems
like it might conceptually simpler to gather all the paths that we
consider a given LiveObjectUpdate to have touched and then for each
subscription look at these paths and pick zero or one of these paths to
emit an event for, with a shortest-match tiebreaker rule. This makes
the "one event per subscription" rule clearer, and I _think_ is simpler
to specify.

So I'm proposing this change as a way of generating discussion. Perhaps
I've misunderstood the intent of `bubbles`, or perhaps there are ways in
which it might be extended in the future that this approach doesn't
cover, or perhaps what I'm proposing is not in other people's opinions
conceptually simpler. Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that a subscription never
emits two events for the same LiveMapUpdate. One particular case in
which this would otherwise happen is the scenario when, after the user
subscribes for path ["map"] which contains a map, this map then emits an
update for some key "myKey", which without this exclusion mechanism
would emit two events to that subscription: one for "map" and one for
"map.key".

If that _is_ the only reason that this mechanism exists, then it seems
like it might be conceptually simpler to gather all the paths that we
consider a given LiveObjectUpdate to have touched and then for each
subscription look at these paths and pick zero or one of these paths to
emit an event for, with a shortest-match tiebreaker rule. This makes the
"one event per subscription" rule clearer, and I _think_ is simpler to
specify.

So I'm proposing this change as a way of generating discussion. Perhaps
I've misunderstood the intent of `bubbles` (which, given that the tests
still pass, would suggest a test gap), or perhaps this proposed approach
doesn't allow for additional dispatch mechanisms that we might need in
the future, or perhaps others don't find what I'm proposing conceptually
simpler. Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that a subscription never
emits two events for the same LiveMapUpdate. One particular case in
which this would otherwise happen is the scenario when, after the user
subscribes for path ["map"] which contains a map, this map then emits an
update for some key "myKey", which without this exclusion mechanism
would emit two events to that subscription: one for "map" and one for
"map.key".

If that _is_ the only reason that this mechanism exists, then it seems
like it might be conceptually simpler to gather all the paths that we
consider a given LiveObjectUpdate to have touched and then for each
subscription look at these paths and pick zero or one of these paths to
emit an event for, with a shortest-match tiebreaker rule. This makes the
"one event per subscription" rule clearer, and I _think_ is simpler to
specify.

So I'm proposing this change as a way of generating discussion. Perhaps
I've misunderstood the intent of `bubbles` (which, given that the tests
still pass, would suggest a test gap), or perhaps this proposed approach
doesn't allow for additional dispatch mechanisms that we might need in
the future, or perhaps others don't find what I'm proposing conceptually
simpler. Alternatively, perhaps `bubbles` just needs better
documentation that explains its motivation. Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing conceptually simpler (in which case, `bubbles`
needs better documentation that explains its motivation). Thoughts,
please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing conceptually simpler (in which case, `bubbles`
needs better documentation that explains its motivation). Thoughts,
please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing clearer (in which case, `bubbles` needs better
documentation that explains its motivation). Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 19, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing clearer (in which case, `bubbles` needs better
documentation that explains its motivation). Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 20, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing clearer (in which case, `bubbles` needs better
documentation that explains its motivation). Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 20, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing clearer (in which case, `bubbles` needs better
documentation that explains its motivation). Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 20, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-root

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing clearer (in which case, `bubbles` needs better
documentation that explains its motivation). Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-js that referenced this pull request May 20, 2026
After noticing that the path-based API spec PR [1] didn't include
map-entry events nor the bubbling-exclusion mechanism that's implemented
in ably-js, I wanted to get a better understanding of this exclusion
mechanism and why it exists.

From what I can tell, it exists to make sure that if there's a map at
path "myMap", and this map emits a LiveMapUpdate having key "myKey",
then a subscription that covers the path "myMap" will only receive one
event (for "myMap"), as opposed to two (for "myMap" and "myMap.myKey").

If that _is_ the only reason that this mechanism exists, then I don't
think it's very obvious currently; the intended behaviour isn't
documented. I think the implementation could be clearer if it makes the
rules explicit:

- for a given LiveObjectUpdate, a given subscription receives at most
  one event per path-to-object

- in the case where a subscription covers both the "myMap" and
  "myMap.myKey" paths, "myMap" wins

That's what I've tried to do here.

Perhaps I've misunderstood the intent of `bubbles` (which, given that
the tests still pass, would suggest a test gap), or perhaps others don't
find what I'm proposing clearer (in which case, `bubbles` needs better
documentation that explains its motivation). Thoughts, please.

[1] ably/specification#427

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `LiveCounterUpdate` and `LiveMapUpdate` subtypes already have the
`internal` marker; the parent `LiveObjectUpdate` interface was missing
it. Add it for consistency, reflecting that `LiveObject#subscribe` and
its emitted update objects are not part of the public API (confirmed
against ably-js, where these types live only in the plugin internals
and are not exported from `liveobjects.d.ts`).

Addresses [1].

[1] #427 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot temporarily deployed to staging/pull/427 May 20, 2026 18:15 Inactive
The `entries`, `keys`, and `values` methods on `PathObject` and
`Instance` were previously described as returning iterators
(`Iterator<...>` in the IDL, "iterator yielding..." in the prose).
"Iterator" is not a term we use elsewhere in the spec, and the array
form is easier to reason about and consistent with how `LiveMap#entries`
([RTLM11](#RTLM11)) etc. are already specified. SDKs remain free to use
a platform-idiomatic equivalent in their public surface (e.g. a JS
iterator).

Addresses [1].

[1] #427 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot temporarily deployed to staging/pull/427 May 20, 2026 18:19 Inactive
Several PathObject and Instance methods previously described their
behaviour by restating the underlying LiveMap or LiveCounter semantics
("returns the number of non-tombstoned entries, equivalent to
LiveMap#size") or by chaining through a sibling method ("behaves
identically to PathObject#entries except the array contains only the
keys"). Both forms duplicate or obscure the fact that the SDK is just
delegating to the LiveMap/LiveCounter spec point.

Rewrite each as an explicit delegation, so the LiveMap/LiveCounter
spec point remains the single source of truth for the semantics
(tombstone handling, ordering, etc.):

  - RTPO7b, RTINS4a: PathObject#value / Instance#value for LiveCounter
    delegate to LiveCounter#value (consistency fold-in, no specific
    comment).
  - RTPO9 (PathObject#entries): collapse the previous b/c/d into b
    (delegate to LiveMap#keys and build [key, PathObject] pairs) plus
    c (empty array on failure). LiveMap#keys rather than #entries is
    correct because the PathObject is lazy and does not need resolved
    values.
  - RTPO10, RTPO11 (PathObject#keys / #values): resolve and delegate
    directly to LiveMap#keys instead of chaining through
    PathObject#entries. RTPO11 still wraps each key in a PathObject.
  - RTPO12b, RTINS9a (#size): delegate to LiveMap#size.
  - RTINS6a (Instance#entries): rephrase to lead with "delegates to
    LiveMap#entries" and wrap each value in an Instance.
  - RTINS7, RTINS8 (Instance#keys / #values): delegate directly to
    LiveMap#keys / #values; RTINS8 wraps each value in an Instance.

compact / compactJson (RTPO13/14, RTINS10/11) are not touched here;
they will be addressed separately because they require a new
LiveMap#compact spec point.

Addresses [1], [2], [3] (and reply [4]), [5].

[1] #427 (comment)
[2] #427 (comment)
[3] #427 (comment)
[4] #427 (comment)
[5] #427 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot temporarily deployed to staging/pull/427 May 20, 2026 18:41 Inactive
…lue types

"Consume" suggested a one-shot procedure, which is misleading for an
immutable value type that could in principle be evaluated multiple
times (e.g. passed to several mutation calls). "Evaluate" captures the
deferred-validation semantics of the value type (per RTLCV3c /
RTLMV3c, validation is deferred to this procedure, much like
evaluating a lazy expression) without the one-shot implication.

The unrelated "consuming subscription events as a stream" usages in
RTPO21 and RTINS18 are left alone — that is the standard
consumer-of-stream sense, which doesn't have the same problem.

Addresses [1].

[1] #427 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot temporarily deployed to staging/pull/427 May 20, 2026 19:22 Inactive
"Once `unsubscribe` called" was missing the verb. Reads as "Once
`unsubscribe` is called" now.

Addresses [1].

[1] #427 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot temporarily deployed to staging/pull/427 May 20, 2026 19:48 Inactive
RTINS16d1 previously read "the `Instance` representing the updated
object", which was vague. Reword to "an `Instance` wrapping the
underlying `LiveObject`", reusing the "underlying `LiveObject`"
terminology already used in RTINS16c.

The new wording deliberately uses "an `Instance`" rather than "the
`Instance` on which `#subscribe` was called", so that the spec does
not mandate strict reference identity between the subscribed `Instance`
and the one delivered in the event. ably-js currently reuses the
subscriber's own `Instance` (`instance.ts:218-223` passes `object:
this`), which has the side effect of pinning that `Instance` for the
lifetime of the subscription. Implementations are free to make that
trade-off either way: pin for identity, or allocate a fresh `Instance`
per event so the subscriber's reference can be collected independently.

Addresses [1] and its follow-up [2].

[1] #427 (comment)
[2] #427 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

live-objects Related to LiveObjects functionality.

Development

Successfully merging this pull request may close these issues.

5 participants