M4a tasks 14 and 15: glue written, calls tabled, after task 14's session wrote nothing

Task 14's session read the crate to learn the APIs and planned `connect` in prose until it was
cut off. `connect`, `Gateway::new` and `catch_up` are now written; the task lists every call with
its signature and makes the copy and the failing test the first actions. Task 15's comment is
rewritten one step per item. Both checked to fail, then pass when filled from their comments.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 21:50:18 -07:00
co-authored by Claude Opus 5.5
parent d1d8531495
commit 564875f0a0
6 changed files with 203 additions and 64 deletions
+50 -16
View File
@@ -33,21 +33,54 @@ The loop that ties tasks 03 to 13 together (spec sections 8 and 9):
skeletons `crates/gatewayd/src/serve/mod.rs` and `crates/gatewayd/src/serve/handle.rs`
- Modify: `crates/gatewayd/src/lib.rs` (`pub mod serve;`), `docs/implementer-log.md`
## Before anything else
Your **first two actions** are steps 1 and 2 below: copy the files, and see the tests fail. Do not
read the other modules of `gatewayd` or `proto` first. Everything the functions you write call is
in the table below, with its signature, and the comment above each `todo!()` gives the code to
write, down to the expressions. Write each function as its comment says, run
`cargo check -p gatewayd`, and go on to the next. Do not weigh other ways to write it.
## The skeletons
`serve/mod.rs`, written: the pointers, `INTERRUPTED`, `Log`, `Stop` (`Auth`, `State`, `Start`,
`Asked`) and its `Display`, `Tuning` and its default, the `Gateway` struct, and the two functions
that are glue: **`run`** (the connect-or-back-off loop) and **`Gateway::event_loop`** (finish turns,
send typing, wait for an event, handle it). Read both first: they call everything you write.
To fill: `From<StateError> for Stop`, `backoff`, `sleep_unless`, `connect`, `Gateway::new`,
`Gateway::connected`.
`Asked`) and its `Display`, `Tuning` and its default, the `Gateway` struct, and the glue: **`run`**
(the connect-or-back-off loop), **`connect`** (`users/me`, the WebSocket, the wait for `hello`),
**`Gateway::new`** and **`Gateway::event_loop`** (finish turns, send typing, wait for an event,
handle it). To fill: `From<StateError> for Stop`, `backoff`, `sleep_unless`, `Gateway::connected`.
`serve/handle.rs`, all to fill: `now_ms`, `post`, `tracked`, `handle_post`, `start`, `finished`,
`typing`, `catch_up`.
`serve/handle.rs`, written: the glue **`catch_up`** (which channels). To fill: `now_ms`, `post`,
`tracked`, `finished`, `typing`, `start`, `handle_post`, `catch_up_channel`.
Each `todo!()` has its steps above it, with the exact log lines. `run` takes the token already
loaded and a `stop` flag, so the tests need no secrets and can end it; task 15's `main` passes a
flag that is never set.
Twelve functions, none more than about twenty lines. `run` takes the token already loaded and a
`stop` flag, so the tests need no secrets and can end it; task 15's `main` passes a flag that is
never set.
## What the functions call
| Call | Signature (from the earlier tasks) |
|---|---|
| `self.client.create_post` | `(&self, channel: &str, root: &str, message: &str) -> Result<Post, MmError>` |
| `self.client.posts_since` | `(&self, channel: &str, since: i64) -> Result<Since, MmError>`; `Since { posts: Vec<Post>, full: bool }` |
| `self.state.seen` / `knows_thread` | `(&self, id: &str) -> bool` |
| `self.state.handled` | `(&mut self, post_id: &str, channel: &str, create_at: i64) -> Result<(), StateError>` |
| `self.state.since` | `(&self, channel: &str) -> Option<i64>` |
| `self.state.mark` | `(&mut self, channel: &str, at: i64) -> Result<(), StateError>` |
| `self.state.join_thread` | `(&mut self, root: &str) -> Result<(), StateError>` |
| `self.state.start_turn` | `(&mut self, turn: InFlight) -> Result<(), StateError>`; `InFlight { session, channel, root }`, all `String` |
| `self.state.end_turn` | `(&mut self, session: &str) -> Result<(), StateError>` |
| `self.state.take_in_flight` | `(&mut self) -> Result<Vec<InFlight>, StateError>` |
| `self.router.route` | `(&self, post: &Post, channel_type: &str, known: &dyn Fn(&str) -> bool) -> Route` |
| `Router::new` | `(me_id: &str, me_name: &str, users: &[String], channels: &[String]) -> Router` |
| `self.queues.push` | `(&mut self, message: Message) -> Pushed` |
| `self.queues.finish` | `(&mut self, session: &SessionId) -> Option<Batch>` |
| `self.queues.threads` | `(&self) -> Vec<Thread>`; `Thread { channel, root }` |
| `deliver` | `(poster: &dyn Poster, socket: &Path, batch: &Batch, log: &dyn Fn(&str))`; `Client` is a `Poster` |
| `typing` | `(seq: u64, channel: &str, parent: &str) -> String` |
| `ws.send_text` | `(&mut self, text: &str) -> Result<(), WsError>` |
| `self.log` | `Arc<dyn Fn(&str) + Send + Sync>`: call it as `(self.log)(&line)` |
`?` turns a `StateError` into a `Stop` through the `From` you write first.
## Steps
@@ -58,11 +91,11 @@ flag that is never set.
`cp docs/plans/M4a/files/crates/gatewayd/tests/support/fake_mm.rs docs/plans/M4a/files/crates/gatewayd/tests/support/gateway.rs crates/gatewayd/tests/support/`.
Add `pub mod serve;` to `lib.rs`.
- [ ] **2. See it fail.** `cargo test -p gatewayd --no-fail-fast --test serve --test serve_restart`.
Expected: it compiles; `serve` 6 fail; `serve_restart` 6 fail and 1 passes (a damaged state file
stops `run` before anything you write is called).
- [ ] **3. Fill `mod.rs` first** (`from`, `backoff`, `sleep_unless`, `Gateway::new`, `connect`,
`connected`), **then `handle.rs`** (`now_ms`, `post`, `tracked`, `finished`, `typing`, `start`,
`handle_post`, `catch_up`). `cargo check -p gatewayd` after each function.
Expected: it compiles; `serve` 6 fail; `serve_restart` 5 fail and 2 pass (a damaged state file
and a refused token stop `run` before anything you write is called).
- [ ] **3. Fill `mod.rs` first** (`from`, `backoff`, `sleep_unless`, `connected`), **then
`handle.rs`** (`now_ms`, `post`, `tracked`, `finished`, `typing`, `start`, `handle_post`,
`catch_up_channel`). `cargo check -p gatewayd` after each function.
- [ ] **4. See it pass.** `cargo test -p gatewayd --test serve --test serve_restart`, five times.
Expected: 6 and 7 passed each time, in about 2 s.
- [ ] **5. Run the gate.** `cargo fmt --all`, then `make gate`. Expected last line: `gate: ok`.
@@ -76,4 +109,5 @@ flag that is never set.
- A test passes only sometimes, or a test takes 5 s or more (that is a wait that timed out, not a
pass).
- `run` or `event_loop` seems to need a change. They are given; report instead.
- A written function (`run`, `connect`, `Gateway::new`, `event_loop`, `catch_up`) seems to need a
change. They are given; report instead.