A corrected message reaches a channel session as a second message, so a session acts on text its sender has withdrawn #125
Labels
No labels
blocked
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
waiting-on-julian
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jlxq0/matrix-mcp#125
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Live defect, not a missing feature. Split out of #124, which covers the general shape.
What a session sees today
Julian sends a message, notices a mistake and corrects it in his client. His client sends an
m.room.messagewhosecontent.bodyis* <corrected text>, whosecontent["m.new_content"]["body"]is the corrected text, and whosecontent["m.relates_to"]is{"rel_type": "m.replace", "event_id": "<the original>"}.An
m.replaceis anm.room.message, so it reachespush_messagelike any other.carried_of_livematches on msgtype andm.textis carried, and the body delivered isev.content.body()— the*-prefixed fallback. The meta is built atsrc/channel.rs:1424-1437and carriesroom,sender,event, and nothing about the relation.What the session observes is two near-identical instructions moments apart, the second prefixed with an asterisk, with nothing saying the second supersedes the first, and it has usually already acted on the first. The replay path is the same: both events are in the timeline,
carried_ofcarries both, so on the next attach a session reads the original and the correction as two separate messages in order.The presentation is the worst available one: it reads as the sender repeating themselves rather than replacing what they said. And the failure is silent in the direction that produces action — nothing is missing, so nothing looks wrong, and the session has no prompt to go and check.
Why this is worse than the reply gap
A dropped reply relation makes a message hard to interpret. A dropped
m.replacemakes a session act on text its sender has already withdrawn, and the withdrawal is what it cannot see. Correcting a message is ordinary use of a Matrix client, so this does not require anything unusual to trigger.Fix
Carry the relation. At minimum the push should mark the event as a replacement and name the event it replaces, so a session can recognise the pair. Beyond that there is a design decision this issue should settle:
replaces="<event_id>", leaving the session to reconcile, orm.new_content.bodyrather than the*fallback, since the fallback exists for clients that cannot render edits and a channel session is not one.The second is better prose and loses the asterisk convention a reader might otherwise use to notice. Whichever, the relation must be in the meta: a session that cannot see the relation cannot reconcile anything.
The replay choice, and what a reader observes under each
Decide this rather than inherit it. The sentence to decide against is what a session sees after reattaching.
The third costs one attribute and pays neither of the other two costs. It also generalises past replay: on the live path an edit arriving as an unmarked new message is the same defect whether or not anyone reattached, so one marking serves both surfaces.
Verification
A test asserting the meta of a pushed
m.replace, with a control that must go red: a plainm.textwith no relation must not carry the attribute. If the delivered body changes tom.new_content, a second control that an edit whosem.new_contentis missing or malformed falls back rather than delivering an empty body.Related
#124 — the general case: the channel push is a lossy projection of the timeline and every relation it drops is invisible rather than degraded.