M4b spec: gatewayd strips a leading mention of its own bot; bare commands are the operator's care
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,10 @@ path (`docs/specs/2026-09-18-m3a-decision-path.md`, section 6). Owner's decision
|
||||
changing the outline: **no reactions**, answers are commands only; an `[approvals] users` list
|
||||
that falls back to `allow.users` when empty; approvals are posted in the asking thread, channel
|
||||
threads included (redacting secrets from tool arguments is later work, with the tools); a
|
||||
`gatewayd` restart marks its old approval posts dead and `brokerd` posts them again.
|
||||
`gatewayd` restart marks its old approval posts dead and `brokerd` posts them again; and
|
||||
`gatewayd` strips a leading mention of its own bot from every message before it is handled, so
|
||||
`@boxmaker0 !approve 42` is a command and `@boxmaker0 summarise the log` reaches the model as
|
||||
`summarise the log`.
|
||||
|
||||
Facts this rests on, checked on 2026-09-24 against Mattermost's source and API reference at the
|
||||
server's version, v11.11.0:
|
||||
@@ -64,7 +67,7 @@ of grants, never reaches `admin.sock`, and cannot create, list or widen anything
|
||||
| `gatewayd` | `approvals` | New: the pending approvals it has posted, the commands that answer them, the text of the posts. Pure. |
|
||||
| `gatewayd` | `gateway` | New: the listener and one thread per `brokerd` connection. |
|
||||
| `gatewayd` | `mm::rest` | `patch_post`, `username`. |
|
||||
| `gatewayd` | `sessions` | `!approve` and `!deny` go to `approvals` instead of the M4a notice. |
|
||||
| `gatewayd` | `sessions` | A leading mention of this bot is stripped first (section 7); `!approve` and `!deny` go to `approvals` instead of the M4a notice. |
|
||||
| `gatewayd` | `state` | `approvals`: the posts of each open approval, for the restart. |
|
||||
| `gatewayd` | `deliver`, `serve` | Typing paused while a turn waits for approval; the M4a "waiting for approval" line only when `brokerd` did not send it (section 7). |
|
||||
| `bxctl` | `cli` | `deny` as another name for `refuse`. |
|
||||
@@ -210,6 +213,15 @@ not retried.
|
||||
|
||||
## 7. `gatewayd`: answers, typing, and the old notice
|
||||
|
||||
**The leading mention.** Before commands are looked for, and before a message is queued for
|
||||
`loopd`, `gatewayd` strips a leading mention of its own bot: `@<username>` at the very start of the
|
||||
message, matched as M4a names are (case-insensitive, not followed by a name character), then any
|
||||
spaces after it. If nothing would remain, the message is left as it was. This happens after M4a
|
||||
has decided the post is for this Boxmaker (the mention still counts as naming it), and in every
|
||||
kind of channel. So in a thread shared by two Boxmakers, `@boxmaker0 !approve 42` is a command for
|
||||
`boxmaker0` and, since it names another user, is ignored by the other. A mention anywhere else in
|
||||
the message stays.
|
||||
|
||||
**Answers.** M4a's routing is unchanged up to commands: only allowlisted users are heard, and in
|
||||
channels only in this Boxmaker's threads. A command `!approve <id>` or `!deny <id> [reason]`
|
||||
(exactly: one space, the id in decimal, then for `!deny` optionally one space and a reason of at
|
||||
@@ -256,9 +268,10 @@ their connections close, so `gatewayd` marks the posts "no longer pending".
|
||||
- Arguments are posted in full, in the thread, which in a channel is visible to its members,
|
||||
other agents included. Redacting secrets is deferred to the tools work (owner's decision).
|
||||
- Several Boxmakers in one channel have their own `brokerd`s, whose approval ids can coincide. A
|
||||
command counts only in the thread where this `gatewayd` posted that id; two Boxmakers with the
|
||||
same id pending in the same thread would both take it. This needs both named in one thread and
|
||||
both asking at once; it is accepted, and noted in the runbook.
|
||||
command counts only in the thread where this `gatewayd` posted that id. Where two Boxmakers
|
||||
share a thread, the operator addresses the command, `@boxmaker0 !approve 42`, and only that bot
|
||||
takes it; a bare `!approve 42` would be taken by every Boxmaker with approval 42 pending in that
|
||||
thread. The owner's decision: that is the operator's care, not something the design prevents.
|
||||
- A local process of the owner's user can connect to `gateway.sock` and have the bot post a fake
|
||||
approval; its answer goes back to that process only. The socket is 0600 in a 0700 directory;
|
||||
M7 keeps it out of `loopd`'s and the tools' reach.
|
||||
@@ -281,6 +294,9 @@ Offline, as tasks with given tests:
|
||||
(`Denied`); answered in `bxctl` meanwhile (the courier closes, frame 4 never sent); expired;
|
||||
the fake closes before answering (the courier posts again); unreachable at first, reachable 5 s
|
||||
later; a bad frame 3 (another id, bad `by`) answers nothing.
|
||||
- **The leading mention**, pure: stripped in any case of letters and before a command; kept when
|
||||
nothing would remain, when another name comes first, for a longer name (`@boxmaker0x`), and in
|
||||
the middle of a message; a post naming another bot first is still ignored.
|
||||
- **`gatewayd` approvals**, pure: every rule of section 7 in both directions; the text of one part,
|
||||
of 3 parts with the hash, and of the refusal to show; fences around backtick runs; escaping of
|
||||
controls, bidi overrides and zero-width characters; every outcome line.
|
||||
|
||||
Reference in New Issue
Block a user