Plan M4b: approvals over Mattermost in 15 tasks
Skeletons for new files only; existing files change by exact edits, so no task overwrites an earlier one's work. Each task's tests were run against a reference built in order from master, with the gate at each step and mutations checked; each skeleton compiles and fails its tests, and every todo!() comment was read beside its signature. The reference is kept off this machine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# M4b implementation plan: approvals over Mattermost
|
||||
|
||||
> **For the implementing model:** do not work from this file. The owner gives you one task file at
|
||||
> a time (`01-…` to `15-…`). This file is the index for the owner and the reviewer.
|
||||
|
||||
**Goal:** an `ask` call is posted in Mattermost, in the thread that asked for it (or, for a session
|
||||
not in Mattermost, in the first approver's direct messages), and an approver answers it there with
|
||||
`!approve <id>` or `!deny <id> [reason]`. The answer is recorded exactly as `bxctl`'s is, naming
|
||||
the approver and the post; the post then shows what came of it. Nobody else can answer, and no
|
||||
post is left looking answerable when it is not.
|
||||
|
||||
**Architecture:** `proto` gains the shared escaping (01) and the four `gateway.sock` messages (02);
|
||||
`bxctl` gains `deny` (03). `brokerd` records the answering post (04), reads `[gateway]` (05), and
|
||||
gets the **courier**, which carries each pending approval to `gatewayd` and its answer back (06).
|
||||
`gatewayd` reads `[approvals]` (07), strips a leading mention of itself and recognises approval
|
||||
commands (08), can edit a post and look up a name (09), renders an approval's posts (10), keeps
|
||||
the approvals it posted and judges commands (11), records their posts in the state file (12),
|
||||
listens on `gateway.sock` (13), posts and marks approvals from its serve loop (14), and wires the
|
||||
commands, the typing pause and the fallback notice (15).
|
||||
|
||||
**Spec:** `docs/specs/2026-09-24-m4b-approvals.md` (approved; P16 applied to the brief). Runbook:
|
||||
task 06 adds `gateway-unreachable`, task 07 widens `gatewayd-start-failed`.
|
||||
|
||||
**No task needs Mattermost, Podman or a network.** Everything runs against fakes: a fake `gatewayd`
|
||||
for `brokerd`, and for `gatewayd` a fake Mattermost, a fake `loopd` and a fake `brokerd` client.
|
||||
|
||||
## Global constraints
|
||||
|
||||
- Everything in `AGENTS.md`, including "Lessons from earlier reviews".
|
||||
- No new dependency.
|
||||
- Branch `m4b`. One task, one fresh OpenCode session, one commit. Run `cargo fmt --all` before the
|
||||
gate. Review happens once, after task 15.
|
||||
- **New files** come as skeletons: types, constants and the glue written, `todo!()` bodies with the
|
||||
code to write in the comment above. **Existing files** are changed by the exact edits in the
|
||||
task, never replaced whole: a whole-file copy would overwrite an earlier task's work.
|
||||
- Some tasks hand over **new versions of given test files**, because the change is in what the
|
||||
test must see (a new field, a new text). Copy them over the old ones as the task says; they are
|
||||
still not to be edited.
|
||||
|
||||
## Tasks
|
||||
|
||||
Every task had a reference implementation, built in this order from master with the gate at every
|
||||
step (tip T26), its given tests run against it, and mutations of it caught by them (tip T28); then
|
||||
the reference was moved off this machine (tip T18). Every skeleton was checked to compile at its
|
||||
task's end state and fail its tests, and every comment above a `todo!()` was read beside its
|
||||
signature (tip T29).
|
||||
|
||||
| # | File | Delivers | Tests | Check |
|
||||
|---|---|---|---|---|
|
||||
| 01 | `01-proto-escape.md` | `proto::escape`, moved from `bxctl` | `proto/tests/escape.rs` | reference; `bxctl` unchanged |
|
||||
| 02 | `02-proto-gateway-wire.md` | the four `gateway.sock` messages | `gateway_wire.rs`, 8 fixtures | reference |
|
||||
| 03 | `03-bxctl-deny.md` | `bxctl deny` | `cli_deny.rs` | reference |
|
||||
| 04 | `04-brokerd-answer-post.md` | answers record the post; `Table::contains` | `ledger_post.rs`; 3 given tests updated | reference |
|
||||
| 05 | `05-brokerd-config-gateway.md` | `[gateway]` | `config_gateway.rs`; `rig.rs` updated | reference |
|
||||
| 06 | `06-brokerd-courier.md` | the courier | `courier.rs`, `support/fake_gateway.rs` | reference; 10 mutations caught; no test hangs |
|
||||
| 07 | `07-gatewayd-config-approvals.md` | `[approvals]` | `config_approvals.rs` | reference |
|
||||
| 08 | `08-gatewayd-mention-commands.md` | the leading mention; `Route::Approval` | `sessions_mention.rs`; `sessions.rs`, `serve.rs` updated | reference; 7 mutations, 5 caught, 2 that cannot change behaviour |
|
||||
| 09 | `09-gatewayd-mm-edit.md` | `patch_post`, `username` | `mm_edit.rs` | reference; the bot's edit checked on the owner's server |
|
||||
| 10 | `10-gatewayd-approval-text.md` | the posts' text | `approvals_text.rs` | reference; 7 mutations caught |
|
||||
| 11 | `11-gatewayd-approvals-registry.md` | the registry, `judge` | `approvals_judge.rs` | reference; 6 mutations caught |
|
||||
| 12 | `12-gatewayd-state-approvals.md` | approval posts in `state.json`; `turn_of` | `state_approvals.rs` | reference |
|
||||
| 13 | `13-gatewayd-gateway-sock.md` | the listener and connection threads | `gateway_sock.rs` | reference; 6 mutations, 5 caught, 1 that cannot change behaviour |
|
||||
| 14 | `14-gatewayd-serve-approvals.md` | posting, marking, the restart, the listener in `run` | `serve_approvals.rs`, `support/brokerd_client.rs`; `fake_mm.rs` updated | reference; 5 mutations caught |
|
||||
| 15 | `15-gatewayd-answers.md` | commands judged, typing paused, the notice | `serve_answers.rs`, `deliver_notice.rs`; `deliver.rs`, `serve.rs`, `support/gateway.rs` updated | reference; 4 mutations caught |
|
||||
|
||||
At the end of task 15: about 845 tests (768 before task 01).
|
||||
|
||||
## Running it
|
||||
|
||||
```sh
|
||||
git switch m4b
|
||||
BOXMAKER_MODEL=straylight/ornith-1.5-35b-a3b tools/run-plan.sh docs/plans/M4b
|
||||
```
|
||||
|
||||
Keep the OpenCode TUI closed while it runs.
|
||||
Reference in New Issue
Block a user