Clarify terminology around aggregations (#1424)

I've done my best to remove the word "bundle", because I feel like it causes
more confusion than it provides. Instead I have favoured "aggregated child
events" which I think is clearer.

Some general clarification around these parts of the spec.
This commit is contained in:
Richard van der Hoff 2023-03-21 18:27:24 +00:00 committed by GitHub
parent d6f38f157d
commit d26794ee90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 52 deletions

View file

@ -192,7 +192,7 @@ replacement event.
Note that there can be multiple events with an `m.replace` relationship to a
given event (for example, if an event is edited multiple times). These should
be [aggregated](#aggregations) by the homeserver.
be [aggregated](#aggregations-of-child-events) by the homeserver.
The aggregation format of `m.replace` relationships gives the **most recent**
replacement event, formatted [as normal](#room-event-format).
@ -201,8 +201,9 @@ The most recent event is determined by comparing `origin_server_ts`; if two or
more replacement events have identical `origin_server_ts`, the event with the
lexicographically largest `event_id` is treated as more recent.
This aggregation is bundled under the `unsigned` property as `m.relations` for any
event that is the target of an `m.replace` relationship. For example:
As with any other aggregation of child events, the `m.replace` aggregation is
included under the `m.relations` property in `unsigned` for any event that is
the target of an `m.replace` relationship. For example:
```json
{
@ -288,8 +289,9 @@ subsequent edits, from the visible timeline. In this situation, homeservers
will return an empty `content` for the original event as with any other
redacted event, and as
[above](#server-side-aggregation-of-mreplace-relationships) the replacement
events will not be bundled with the original event. Note that the subsequent edits are
not actually redacted themselves: they simply serve no purpose within the visible timeline.
events will not be included in the aggregation bundled with the original
event. Note that the subsequent edits are not actually redacted themselves:
they simply serve no purpose within the visible timeline.
#### Edits of replies

View file

@ -19,11 +19,12 @@ messages.
##### Server-side aggregation of `m.reference`
The aggregation format of `m.reference` relations consists of a single `chunk` property,
which lists all the events which `m.reference` the event (the parent). Currently,
only a single `event_id` field is present on the events in the `chunk`.
The [aggregation](#aggregations-of-child-events) format of `m.reference`
relations consists of a single `chunk` property, which lists all the events
which `m.reference` the event (the parent). Currently, only a single `event_id`
field is present on the events in the `chunk`.
An example `m.reference` would be:
For example, given an event with the following `m.reference` relationship:
```json
{
@ -38,7 +39,7 @@ An example `m.reference` would be:
}
```
The [bundle](#aggregations) under `m.relations` would appear similar to the following:
The aggregation would appear similar to the following:
```json
{

View file

@ -11,12 +11,14 @@ Clients SHOULD render threads differently to regular messages or replies in the
as by providing some context to what is going on in the thread but keeping the full conversation
history behind a disclosure.
Threads are established using a `rel_type` of `m.thread` and reference the *thread root* (the
first event in a thread). It is not possible to create a thread from an event with a `rel_type`,
which includes not being able to nest threads. All conversation in a thread reference the thread
root instead of the most recent message, unlike rich reply chains.
Threads are established using a `rel_type` of `m.thread` and reference the
*thread root* (the first event in a thread). It is not possible to create a
thread from an event which itself is the child of an event relationship (i.e.,
one with an `m.relates_to` property). It is therefore also not possible to nest
threads. All events in a thread reference the thread root instead of the
most recent message, unlike rich reply chains.
As a worked example, the following represents a thread and how it'd be formed:
As a worked example, the following represents a thread and how it would be formed:
```json
{
@ -128,11 +130,11 @@ clients is used to create a reply within a thread: clients should render the eve
##### Validation of `m.thread` relationships
Servers SHOULD reject client requests which attempt to start a thread off an event with a
`rel_type`. If the client attempts to target an event which already has an `m.thread`,
`m.reference`, or any other `rel_type` then it should receive a HTTP 400 error response
with appropriate error message, as per the [standard error response](#standard-error-response)
structure.
Servers SHOULD reject client requests which attempt to start a thread off an
event with an `m.relates_to` property. If the client attempts to target an event which itself
has an `m.relates_to` property, then it should receive a HTTP 400 error
response with appropriate error message, as per the [standard error
response](#standard-error-response) structure.
{{% boxes/note %}}
A specific error code is not currently available for this case: servers should use `M_UNKNOWN`
@ -141,12 +143,16 @@ alongside the HTTP 400 status code.
##### Server-side aggregation of `m.thread` relationships
Given threads always reference the thread root, an event can have multiple "child" events which
then form the thread itself. These events should be [aggregated](#aggregations) by the server.
Given threads always reference the thread root, an event can have multiple
"child" events which then form the thread itself. These events should be
[aggregated](#aggregations-of-child-events) by the server.
The aggregation for threads includes some information about the user's participation in the thread,
the approximate number of events in the thread (as known to the server), and the most recent event
in the thread (topologically). This is then bundled into the event as `m.thread`:
in the thread (topologically).
As with any other aggregation of child events, the `m.thread` aggregation is
included under the `m.relations` property in `unsigned` for the thread root. For example:
```json
{
@ -165,6 +171,11 @@ in the thread (topologically). This is then bundled into the event as `m.thread`
"content": {
"msgtype": "m.text",
"body": "Woo! Threads!"
},
"unsigned": {
"m.relations": {
// ...
}
}
},
"count": 7,
@ -178,15 +189,17 @@ in the thread (topologically). This is then bundled into the event as `m.thread`
`latest_event` is the most recent event (topologically to the server) in the thread sent by an
un-[ignored user](#ignoring-users).
Note that any bundled aggregations on `latest_event` should also be present. The server should be
careful to avoid loops, though loops are not currently possible due to `m.thread` not being possible
to target an event with a `rel_type` already.
Note that, as in the example above, child events of the `latest_event` should
themselves be aggregated and included under `m.relations` for that event. The
server should be careful to avoid loops, though loops are not currently
possible due to `m.thread` not being permitted to target an event with an
`m.relates_to` property.
`count` is simply the number of events using `m.thread` as a `rel_type` pointing to the target event.
It does not include events sent by [ignored users](#ignoring-users).
`current_user_participated` is `true` when the authenticated user is either:
1. The `sender` of the event receiving the bundle (they sent the thread root).
1. The `sender` of the thread root event.
2. The `sender` of an event which references the thread root with a `rel_type` of `m.thread`.
#### Querying threads in a room