M2.1, PR A of 4: Matrix threads in the transport (architecture L5: each work item's conversation is a thread).
Changes
Transport.post(text, *, thread=None) -> event_id. A top-level post's ID can root a thread. A threaded post carries m.relates_to: {rel_type: m.thread, event_id: root}, plus the standard reply fallback, so clients without thread support still show it as a reply.
InboundMessage gains event_id and thread, the root, or None at top level.
A message in a thread gives its root.
A plain reply from a client without threads joins the thread of the message it replies to, when the transport knows it: one it sent, or one it has seen. It remembers the last 2,000 events.
A rich reply's quoted lines (> <@x> …) are stripped from the text: they aren't what the sender said.
InMemoryTransport plays threads: post returns IDs, and receive(..., thread=).
Unchanged:send_message and friends stay for the old loop, now built on post. The old loop ignores threads.
Tests
Unit:
a threaded post's relation;
a threaded message's root;
plain replies joining threads, with the quote stripped;
a reply to an unknown message staying at top level;
a reply to the operator's own threaded message;
the in-memory fake.
Live, test_matrix_live: the homeserver accepts the relation and lists the reply under its root, through the v1 relations API.
M2.1, **PR A of 4: Matrix threads in the transport** (architecture L5: each work item's conversation is a thread).
## Changes
- **`Transport.post(text, *, thread=None) -> event_id`.** A top-level post's ID can root a thread. A threaded post carries `m.relates_to: {rel_type: m.thread, event_id: root}`, plus the standard reply fallback, so clients without thread support still show it as a reply.
- **`InboundMessage`** gains `event_id` and `thread`, the root, or None at top level.
- **A message in a thread** gives its root.
- **A plain reply** from a client without threads joins the thread of the message it replies to, when the transport knows it: one it sent, or one it has seen. It remembers the last 2,000 events.
- **A rich reply's quoted lines** (`> <@x> …`) are stripped from the text: they aren't what the sender said.
- **`InMemoryTransport`** plays threads: `post` returns IDs, and `receive(..., thread=)`.
- **Unchanged:** `send_message` and friends stay for the old loop, now built on `post`. The old loop ignores threads.
## Tests
- **Unit:**
- a threaded post's relation;
- a threaded message's root;
- plain replies joining threads, with the quote stripped;
- a reply to an unknown message staying at top level;
- a reply to the operator's own threaded message;
- the in-memory fake.
- **Live, `test_matrix_live`:** the homeserver accepts the relation and lists the reply under its root, through the v1 relations API.
559 unit and 20 integration tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Transport.post(text, thread=...) returns the event ID, which can root a
thread; a threaded post carries m.thread with the reply fallback for
clients without threads. Inbound messages carry their event ID and thread
root: a message in a thread gives its root, a plain reply joins the
thread of the message it replies to when known, and the quoted lines of a
rich reply are no longer part of the text. The in-memory transport plays
threads for tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
M2.1, PR A of 4: Matrix threads in the transport (architecture L5: each work item's conversation is a thread).
Changes
Transport.post(text, *, thread=None) -> event_id. A top-level post's ID can root a thread. A threaded post carriesm.relates_to: {rel_type: m.thread, event_id: root}, plus the standard reply fallback, so clients without thread support still show it as a reply.InboundMessagegainsevent_idandthread, the root, or None at top level.> <@x> …) are stripped from the text: they aren't what the sender said.InMemoryTransportplays threads:postreturns IDs, andreceive(..., thread=).send_messageand friends stay for the old loop, now built onpost. The old loop ignores threads.Tests
test_matrix_live: the homeserver accepts the relation and lists the reply under its root, through the v1 relations API.559 unit and 20 integration tests pass.
🤖 Generated with Claude Code
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE