6.9 KiB
PR#91: An Agent Marks a Matrix Message with a Reaction
Version: minor
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
Problem
The user announces pull requests of hs.hsadmin.ng for review in the Matrix room "HSAdmin NG". An agent merges the approved ones and must then mark each announcement: ✅️ when the pull request is approved, ☑️ in addition when it is merged. The room is end-to-end encrypted; werkregie has a verified device of the user's account, but only reads.
Non-Goals
- No messages, replies or edits; only a reaction (
m.reaction,rel_type: m.annotation). - No removal of a reaction.
- No reaction to messages of other users.
- No HTTP endpoint for it; the command line only.
- No reaction through the running relay; see "Follow-up PRs".
Specification
Feature: werkregie-dashboard matrix react marks the newest own message that contains a text
Scenario#91.01: an announcement gets the reaction
… so that the room shows which pull request is approved.
- Given the user's newest own message in the room "HSAdmin NG" that contains
/pulls/329, without a reaction ✅️ of the user - When an agent runs
werkregie-dashboard matrix react --room "HSAdmin NG" --contains /pulls/329 ✅️ - Then the message has the reaction ✅️ of the user, and the command prints
REACTEDwith the event IDs
Verified by
- MatrixReactionsTest "the target is the newest own message that contains the text"
- MatrixReactorTest "a message gives its text, an edit none"
- MatrixReactorTest "an encrypted event gives what its cleartext shows, a decrypted one its content"
- MainTest "matrix react takes room, text and key, in any order of the options"
Scenario#91.02: a reaction that is there already stays single
… so that an agent can repeat the command after a failure.
- Given the message has a reaction ✅️ of the user, with or without the variation selector U+FE0F
- When the agent runs the same command again
- Then nothing is sent, and the command prints
PRESENT
Verified by
- MatrixReactionsTest "an own reaction with the key, with or without U+FE0F, is present"
- MatrixReactionsTest "a reaction of another user, with another key or to another message is not present"
- MatrixReactorTest "a reaction gives the message it annotates and its key"
- MatrixReactorTest "a reaction in the outbox counts as the user's, by its event ID once it is sent"
Scenario#91.03: no matching message
… so that a wrong text does not mark another message.
- Given no own message among the last 500 events of the room contains the text, e.g. only
/pulls/3290for/pulls/329 - When the agent runs the command
- Then nothing is sent, and the command prints
NO-MESSAGEand ends with exit code 1
Verified by
- MatrixReactionsTest "without an own message that contains the text there is no target"
- MatrixReactionsTest "each outcome prints one line, and only a missing message ends with exit code 1"
Scenario#91.04: no reaction while the relay syncs
… so that two clients never use the device's store and keys at once.
- Given the relay of the machine holds
sync.lockand runs the Matrix client - When the agent runs the command
- Then nothing is sent, and the command says to stop the relay first and ends with exit code 3
Verified by
Solution
MatrixReactionsdecides, free of the client library: the room by its ID or name, the newest own message whose text contains--contains, not followed by a letter or digit, and whether an own reaction with the key is there already; U+FE0F does not count, since clients differ in it.MatrixReactortakessync.lock, opens the device's client, waits for the first sync, reads the last 500 events of the room backwards, decrypted where the device has the key, and sendsm.reaction. Trixnity encrypts it in an encrypted room. A reaction of an earlier run that still waits in the client's outbox counts as present, so it is not sent twice.- Exit codes: 0 for
REACTEDandPRESENT, 1 forNO-MESSAGE, 2 for a wrong call, 3 for a failure, aswatch-pr. - No token or key is printed: the command prints event IDs only, an error its message.
Why the command takes sync.lock rather than going through the running relay:
the relay holds the lock as long as it runs, and only MatrixSource has its client.
A control socket in MatrixSource would make pitestPartial mutate all of its methods;
MatrixSource.message alone has a Mutation-CRAP of 812, far above the limit 15 of a branch.
So MatrixSource needs its refactoring first, in a PR of its own.
Until then the agent stops the relay for the reactions and starts it again;
the dashboard then misses the relay's report for that time and says so.
A relay takes the lock only with its first fetch, a few seconds after its start;
until then matrix react gets the lock, too.
Checked live in the user's test room, with the relay stopped:
REACTED, then PRESENT for the same call; NO-MESSAGE with exit code 1 for a room found by its name.
A call takes about 50 s, mostly the first sync since the relay last ran.
Release Notes
werkregie-dashboard matrix react --room RAUM --contains TEXT EMOJImarks the newest own message with a reaction; a running relay or local mode stops for it.
Open Questions
None.
Follow-up PRs
- A refactoring of
MatrixSource: each of its methods at a Mutation-CRAP of at most 15, the limit ofpitestPartialfor the classes a branch changes. - Then the relay that holds
sync.locksends the reaction formatrix reactthrough a control socket; the relay no longer stops for it.