From 84cec0e4c9bb70d317f46ea4c807a44f2117f2da Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Tue, 26 May 2026 20:43:15 +0100 Subject: [PATCH] Add TM8b summaryVersion on MessageAnnotations Successive annotation summaries on the same referenced message need an identifier that is independent of the referenced message's `serial`, `timestamp`, and `version` (which all refer to the message being annotated, not to the summary). Without this, consumers cannot order two summaries of the same message, or tell when a summary itself was produced. Introduces `MessageAnnotations.summaryVersion` (TM8b) and a new `SummaryVersion` class with `serial` (TM8b1) and `timestamp` (TM8b2). TM8b3 spells out that the SDK populates it from the wire when present (including when `summary` is empty, since the version advances on every summary publish) and must not synthesise a default when absent. Co-Authored-By: Claude Opus 4.7 (1M context) --- specifications/api-docstrings.md | 10 ++++++++++ specifications/features.md | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/specifications/api-docstrings.md b/specifications/api-docstrings.md index f0d9594b1..87e540656 100644 --- a/specifications/api-docstrings.md +++ b/specifications/api-docstrings.md @@ -914,6 +914,16 @@ Contains annotations that have been made to a message. | Method / Property | Parameter | Returns | Spec | Description | |---|---|---|---|---| | summary: Dict? ||| TM8a | An object whose keys are annotation types, and the values are aggregated summaries for that annotation type. | +| summaryVersion: SummaryVersion? ||| TM8b | Identifies the version of the summary itself, distinct from the enclosing Message's `serial`, `timestamp`, and `version` fields (which all refer to the referenced message). Set on `MESSAGE_SUMMARY` events; absent on other actions. | + +## class SummaryVersion + +Identifies a particular version of an annotation summary on a message. Allows ordering successive summaries on the same referenced message, and reasoning about when the summary itself was produced (as distinct from when the referenced message was published). + +| Method / Property | Parameter | Returns | Spec | Description | +|---|---|---|---|---| +| serial: String ||| TM8b1 | An opaque timeserial that identifies this version of the summary. | +| timestamp: Time ||| TM8b2 | Time in milliseconds since epoch at which this version of the summary was produced. Equal to the time component encoded in `serial`. | ## class PresenceMessage diff --git a/specifications/features.md b/specifications/features.md index e30f80ef1..1c436b6f3 100644 --- a/specifications/features.md +++ b/specifications/features.md @@ -1250,6 +1250,10 @@ The core SDK provides an API for wrapper SDKs to supply Ably with analytics info - `(TM8)` The attributes available in a `MessageAnnotations` object are: - `(TM8a)` `summary` `Dict` - an object whose keys are annotation types, and the values are aggregated summaries for that annotation type. A missing `summary` field on the wire indicates an empty summary, equivalent to an object with no keys. The SDK must initialize this object if not present. - `(TM8a1)` A given annotation type has a fixed summary structure, and the currently-used structured are documented for the user's use in api-docstrings. But the sdk MUST be able to cope with structures and aggregation types that have it does not yet know about or have explicit support for, hence the loose (JsonObject) type. + - `(TM8b)` `summaryVersion` - an object identifying the version of the summary itself. It is distinct from the enclosing `Message`'s `serial`, `timestamp`, and `version` fields, which all continue to refer to the referenced message (i.e. the message that the summary is of). The fields are: + - `(TM8b1)` `serial` - an opaque timeserial string identifying this version of the summary. + - `(TM8b2)` `timestamp` - time in milliseconds since epoch at which this version of the summary was produced. This is the time component encoded in the [TM8b1](#TM8b1) `serial`, so the two cannot drift. + - `(TM8b3)` The SDK MUST populate `summaryVersion` from the value received on the wire when one is present, regardless of whether [TM8a](#TM8a) `summary` is empty (the version advances on every summary publish, including ones that produce an empty `summary`, so consumers can order successive summaries on the same referenced message). When `summaryVersion` is absent on the wire the SDK MUST leave it unset (e.g. `null`); the SDK MUST NOT synthesise a default value for it. #### DeltaExtras @@ -2916,6 +2920,11 @@ Each type, method, and attribute is labelled with the name of one or more clause class MessageAnnotations: summary: Dict? // TM8a + summaryVersion: SummaryVersion? // TM8b + + class SummaryVersion: + serial: string // TM8b1 + timestamp: number // TM8b2 class MessageVersion: serial: string? // TM2s1