Plan M4a: gatewayd in 15 tasks, with skeletons and given tests

Each task's tests were run against a reference at its end state; the end states were replayed
from master in order with the gate at each step (650 to 762 tests); each skeleton compiles
against its tests and fails them. The reference is kept off this machine. Lessons T27 (every
wait in a test has a limit) and T28 (mutate the reference before hand-over) come from this work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 19:05:44 -07:00
co-authored by Claude Opus 5.5
parent f38dc8d474
commit 0339dc13b2
69 changed files with 7790 additions and 3 deletions
@@ -0,0 +1,78 @@
//! What the event loop does with a post, a finished turn, and the time between: routing, starting
//! turns, typing, and catching up after a gap.
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::deliver::{LOOP_DOWN, deliver};
use crate::mm::{Post, typing};
use crate::serve::{Gateway, Stop};
use crate::sessions::{BUSY, Batch, Ignored, Pushed, Route};
use crate::state::InFlight;
use crate::ws::WsError;
use crate::ws::conn::Ws;
/// Now, in Mattermost's milliseconds.
fn now_ms() -> i64 {
// Milliseconds since the Unix epoch as i64, with try_from (i64::MAX if it does not fit).
todo!()
}
impl Gateway {
/// Post in a thread; a failure is logged, not fatal.
pub(super) fn post(&self, channel: &str, root: &str, text: &str) {
// `self.client.create_post`; an error is logged, "gatewayd: cannot post in <channel>
// (thread <root>): <error>".
todo!()
}
/// Is this channel one whose posts `gatewayd` keeps track of?
fn tracked(&self, channel: &str, channel_type: &str) -> bool {
// channel_type "D", or the channel is in allow.channels.
todo!()
}
/// One new post, live or caught up.
pub(super) fn handle_post(&mut self, post: &Post, channel_type: &str) -> Result<(), Stop> {
// 1. Not tracked, or seen -> nothing. 2. `state.handled(...)?` before anything else. 3.
// Route it: NotAllowed -> log "gatewayd: ignored post <id> from <user>: not allowed"
// (never the message); other Ignore -> nothing; Reply -> post it; Queue -> join the
// thread when joins_thread, then push: Start -> `start`, Waiting -> nothing, Full ->
// post BUSY.
todo!()
}
/// Record the turn as in flight and run it on its own thread.
fn start(&mut self, batch: Batch) -> Result<(), Stop> {
// `state.start_turn` (session, channel, root). Spawn with std::thread::Builder: `deliver`
// with the client, the loop socket, the batch and the log, then send the session on
// done_tx. If spawning fails: log "gatewayd: cannot start a thread for <session>: <e>",
// post LOOP_DOWN in the thread, and send the session on done_tx.
todo!()
}
/// Turns that ended: out of flight, and the next batch of each session started.
pub(super) fn finished(&mut self) -> Result<(), Stop> {
// For each session on done_rx (try_recv, never blocking): `end_turn`, then `queues.finish`;
// a batch it returns is started.
todo!()
}
/// Show this bot as typing in every thread with a turn running.
pub(super) fn typing(&mut self, ws: &mut Ws) -> Result<(), WsError> {
// For each of `queues.threads()`: seq += 1, send `typing(seq, channel, root)` as text.
todo!()
}
/// Answer what arrived while `gatewayd` was away: the direct channel with each allowed user,
/// and each allowed channel. A channel seen for the first time starts from now.
pub(super) fn catch_up(&mut self) -> Result<(), Stop> {
// Channels: the direct channel with each allowed user ("D"; an error is logged "gatewayd:
// no direct channel with <user>: <e>" and skipped), then each allowed channel ("O"). For
// each: no mark -> `mark(channel, now_ms())` and skip; else `posts_since(channel, mark)`
// (an error is logged "gatewayd: cannot catch up <channel>: <e>" and skipped); when full,
// log "gatewayd: <channel>: too many posts to catch up; some may be missed"; `handle_post`
// each post in order.
todo!()
}
}