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,96 @@
|
||||
# M4b task 14: the serve loop posts approvals and marks them
|
||||
|
||||
**Branch:** `m4b` (run `git switch m4b`; `git status --short` must be empty, otherwise stop)
|
||||
**Commit subject:** `gatewayd: serve, posting approvals, marking them, and the listener`
|
||||
|
||||
## Goal
|
||||
|
||||
The serve loop does what `gateway.sock` connections ask (M4b spec, sections 6 and 8):
|
||||
|
||||
- **Show**: place the approval (its session's turn in flight: that thread; otherwise the first
|
||||
approver's direct messages, as a new thread), post its parts in order, record the posts in the
|
||||
state file, register it for answers (task 11), and reply with the post ids, or why it could not
|
||||
be posted.
|
||||
- **Ended**: forget it, and add how it ended to its last post, naming who answered.
|
||||
- At the **first connection after a start**, the approval posts the last run left behind are
|
||||
marked: their last post is replaced by its title and the restart line.
|
||||
- `run` opens `gateway.sock` before connecting; a failure stops `gatewayd` at start
|
||||
(`Stop::Start`, pointer `gatewayd-start-failed`).
|
||||
|
||||
## Files
|
||||
|
||||
- Copy: the skeleton `crates/gatewayd/src/serve/approvals.rs`,
|
||||
`crates/gatewayd/tests/serve_approvals.rs`, `crates/gatewayd/tests/support/brokerd_client.rs`,
|
||||
and a **new version** of the given `crates/gatewayd/tests/support/fake_mm.rs` (it gains post
|
||||
edits, user names, and refusing posts)
|
||||
- Modify: `crates/gatewayd/src/serve/mod.rs`, `docs/implementer-log.md`
|
||||
|
||||
## The skeleton
|
||||
|
||||
Written: `Open`, **`gateway_requests`** (drain the channel, never blocking) and **`show`** (read
|
||||
both first). To fill, each with its steps in its comment: `place`, `ended`, `mark_left_behind`,
|
||||
`mark`.
|
||||
|
||||
## `serve/mod.rs`, exactly
|
||||
|
||||
1. `mod approvals;` before `mod handle;`. Imports: `use std::collections::HashMap;`,
|
||||
`use crate::approvals::Registry;`, `use crate::gateway::{self, ToLoop};`.
|
||||
2. Three fields at the end of `struct Gateway`:
|
||||
|
||||
```rust
|
||||
/// The approvals posted here, shared with the turns (for the notice) and judged by commands.
|
||||
registry: Arc<Registry>,
|
||||
/// What `gateway.sock` connections ask of the loop.
|
||||
gateway_rx: Receiver<ToLoop>,
|
||||
/// The last post of each approval posted here and not yet ended.
|
||||
open: HashMap<u64, approvals::Open>,
|
||||
```
|
||||
|
||||
3. `Gateway::new` gains a last parameter `gateway_rx: Receiver<ToLoop>`, and sets
|
||||
`registry: Arc::new(Registry::new()), gateway_rx, open: HashMap::new(),`.
|
||||
4. In `run`, right before `let client = Arc::new(Client::new(…));`:
|
||||
|
||||
```rust
|
||||
let listener = match gateway::listen(&config.gateway_socket()) {
|
||||
Ok(listener) => listener,
|
||||
Err(why) => return Stop::Start(why),
|
||||
};
|
||||
let (gateway_tx, gateway_rx) = mpsc::channel();
|
||||
if let Err(e) = gateway::serve(
|
||||
listener,
|
||||
gateway_tx,
|
||||
gateway::Timing::default(),
|
||||
Arc::clone(&log),
|
||||
) {
|
||||
return Stop::Start(format!("cannot start the gateway.sock thread: {e}"));
|
||||
}
|
||||
```
|
||||
|
||||
and `Gateway::new(config, client, state, log)` becomes
|
||||
`Gateway::new(config, client, state, log, gateway_rx)`.
|
||||
5. In `connected`, inside `if !self.restarted { … }`, after the `take_in_flight` loop:
|
||||
`self.mark_left_behind()?;`
|
||||
6. In `event_loop`, after `self.finished()?;`: `self.gateway_requests()?;`
|
||||
|
||||
## Steps
|
||||
|
||||
- [ ] **1. Copy.** `git switch m4b`, then
|
||||
`cp docs/plans/M4b/files/crates/gatewayd/src/serve/approvals.rs crates/gatewayd/src/serve/ && cp docs/plans/M4b/files/crates/gatewayd/tests/serve_approvals.rs crates/gatewayd/tests/ && cp docs/plans/M4b/files/crates/gatewayd/tests/support/brokerd_client.rs docs/plans/M4b/files/crates/gatewayd/tests/support/fake_mm.rs crates/gatewayd/tests/support/`
|
||||
- [ ] **2. Make the six `serve/mod.rs` changes.** `cargo build -p gatewayd` must succeed.
|
||||
- [ ] **3. See it fail.** `cargo test -p gatewayd --test serve_approvals`. Expected: 4 fail, 1
|
||||
passes (the socket is there while `gatewayd` runs).
|
||||
- [ ] **4. Fill `mark`, `place`, `ended`, `mark_left_behind`**, `cargo check -p gatewayd` after
|
||||
each.
|
||||
- [ ] **5. See it pass.** `cargo test -p gatewayd --test serve_approvals`, three times: 5 passed.
|
||||
Then `cargo test -p gatewayd`: every suite passes.
|
||||
- [ ] **6. Run the gate.** `cargo fmt --all`, then `make gate`. Expected last line: `gate: ok`.
|
||||
- [ ] **7. Log and commit.** `git add crates/gatewayd docs/implementer-log.md Cargo.lock && git commit`
|
||||
|
||||
## Done when
|
||||
|
||||
- `cargo test -p gatewayd` passes; `make gate` prints `gate: ok`.
|
||||
|
||||
## Stop and report if
|
||||
|
||||
- A comment above a `todo!()` does not fit its function, or contradicts a test: quote it.
|
||||
- `show` or `gateway_requests` seems to need a change: they are given.
|
||||
Reference in New Issue
Block a user