M4b spec: approvals from non-approvers in direct messages go to an approver, with who asked and what
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# M4b design: approvals over Mattermost
|
||||
|
||||
Status: approved by the owner, 2026-09-24, with P16 applied to the brief. It builds on M4a
|
||||
Status: approved by the owner, 2026-09-24, with P16 applied to the brief. Changed after the review
|
||||
of the same day (owner's decision): approvals asked by non-approvers in direct or group messages
|
||||
are redirected to an approver with who asked and their message (section 6). It builds on M4a
|
||||
(`docs/specs/2026-09-23-m4a-gateway.md`, section 12 there is the agreed outline) and M3a's approval
|
||||
path (`docs/specs/2026-09-18-m3a-decision-path.md`, section 6). Owner's decisions of 2026-09-24,
|
||||
changing the outline: **no reactions**, answers are commands only; an `[approvals] users` list
|
||||
@@ -161,10 +163,33 @@ One thread per accepted `gateway.sock` connection. It reads frame 1 (bounded by
|
||||
checks the ids in it, and decides **where**:
|
||||
|
||||
- If the session is `mm-<root>` and `state.in_flight` holds a turn of that session, the approval
|
||||
goes into that turn's thread (its channel and root). This covers direct messages and channels.
|
||||
goes into that turn's thread (its channel and root), **unless** the turn was asked by someone
|
||||
who is not an approver in a direct or group message: no approver could answer it there. (In a
|
||||
channel an approver can reply in the thread, so it stays.)
|
||||
- A redirected approval (asked by a non-approver in a direct or group message) goes to the first
|
||||
approver's direct channel as a new thread whose **first post** says who asked, where, and what
|
||||
they asked (review of 2026-09-24, owner's requirement: the approver must know who is asking,
|
||||
their prompt, and what Boxmaker is trying to do):
|
||||
|
||||
````text
|
||||
**Approval 3** was asked by @alex in a direct message with the bot. Their message:
|
||||
```
|
||||
Read the file /srv/notes/q3.txt and summarise it.
|
||||
```
|
||||
````
|
||||
|
||||
The message is the text of the turn that asked (a burst, joined), escaped and fenced like the
|
||||
arguments; it is context, not what is approved, so it is cut at 2,000 characters with
|
||||
`(… <n> more characters)` after the fence. The approval's own posts follow in that thread, and
|
||||
the asking thread gets one line: `waiting for approval <id>: it was sent to an approver`.
|
||||
- Otherwise (`bxctl chat`, a session no longer in flight, later scheduled jobs), it goes to the
|
||||
first approver's direct channel (`POST /channels/direct`), as a new top-level post whose own
|
||||
thread is where it is answered.
|
||||
first approver's direct channel as a new thread whose first post says
|
||||
`**Approval <id>** was asked in session `<session>`, which gatewayd does not follow now (a
|
||||
restart, or a session from outside Mattermost).`, followed by the approval's posts.
|
||||
|
||||
`gatewayd` knows who asked from the posts it queued: for each session it keeps the author of the
|
||||
last message queued, the channel's type, and, when a turn starts, that turn's text; it forgets them
|
||||
when the turn ends. They are not in the state file: after a restart the turn is interrupted anyway.
|
||||
|
||||
### The text
|
||||
|
||||
|
||||
Reference in New Issue
Block a user