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>
4.4 KiB
M4a task 11: which posts become turns, and the queue per session
Branch: m4a (run git switch m4a; git status --short must be empty, otherwise stop)
Commit subject: gatewayd: sessions, routing posts to sessions, commands and the queue
Goal
The decisions, as pure code: no network, no files. For each new post, in this order (spec section 7):
- Ignore it if it is the bot's own, or a system message (
kindnot empty). - Ignore it, silently, unless the author is in
allow.users. - Is it for this Boxmaker? A direct message (
"D"): always. A channel or group message ("O","P","G") whose id is inallow.channels: if it names this bot, or if it is a reply in a thread this Boxmaker already has a session for and names nobody else. Anything else: ignore.@channel,@hereand@allname nobody: every agent would answer them. - The session: its root is
root_id, or the post's own id when that is empty; the session id ismm-<root>. A top-level post is a new session (resume: false); a reply resumes it. - Commands: a message starting with
!.!!…is not a command: one!is removed and the rest goes on as a message.!approve …and!deny …are answered withM4B_COMMAND; any other!withUNKNOWN_COMMAND. A command never reachesloopd. - Otherwise the message joins its session's queue.
"Names" means @<username> in the message, case-insensitive, where the name is the longest run of
a-z, 0-9, ., - and _ after the @, without trailing dots. The spec's examples, with this
bot called boxmaker-straylight and another agent called Hermes in the channel (the first test
checks every row):
| Post | For Boxmaker? |
|---|---|
@boxmaker-straylight summarise the audit log (top level) |
yes: a new session rooted here |
a reply in that thread: and the older files? |
yes: its thread, nobody else named |
a reply in that thread: @hermes what do you think? |
no |
@boxmaker-straylight @hermes compare notes |
yes (and Hermes answers too) |
@boxmaker-straylightx hello |
no: a different name |
@channel standup in five |
no |
The queue. One turn at a time per session. A message for a session with no turn running
starts one at once. Messages that arrive while it runs wait; when it ends, all waiting messages
go together as the next turn, joined with a blank line ("\n\n"), in the order they came. A
session with limit messages already waiting drops the next one, and the caller answers BUSY.
Files
- Copy:
crates/gatewayd/tests/sessions.rs, and the skeletoncrates/gatewayd/src/sessions.rs - Modify:
crates/gatewayd/src/lib.rs(pub mod sessions;),docs/implementer-log.md
The skeleton
Written: the three answer texts (M4B_COMMAND, UNKNOWN_COMMAND, BUSY), EVERYONE, Ignored
(Own, System, NotAllowed, NotForUs), Thread { channel, root }, Message { session, thread, resume, text, joins_thread }, Route (Ignore, Reply { thread, text }, Queue),
Router, Batch { session, thread, resume, text }, Pushed (Start(Batch), Waiting,
Full(Thread)), Pending, Queues.
To fill: named, Router::new, Router::for_us, Router::route, Queues::new, push,
finish, running, threads. route is a straight line of early returns in the order above; the
known function it is given answers "does this Boxmaker have a session for this thread root?"
(task 14 answers it from the state file). SessionId has no Ord, so Queues keys a HashMap.
Steps
- 1. Copy.
git switch m4a, thencp docs/plans/M4a/files/crates/gatewayd/tests/sessions.rs crates/gatewayd/tests/ && cp docs/plans/M4a/files/crates/gatewayd/src/sessions.rs crates/gatewayd/src/. Addpub mod sessions;tolib.rs. - 2. See it fail.
cargo test -p gatewayd --test sessions. Expected: it compiles and 8 tests fail. - 3. Fill
named, then theRouter, then theQueues,cargo check -p gatewaydafter each function. - 4. See it pass.
cargo test -p gatewayd --test sessions. Expected: 8 passed. - 5. Run the gate.
cargo fmt --all, thenmake gate. Expected last line:gate: ok. - 6. Log and commit.
git add crates/gatewayd docs/implementer-log.md Cargo.lock && git commit
Done when
cargo test -p gatewayd --test sessionspasses;make gateprintsgate: ok.
Stop and report if
- A row of the table above seems to need a different order of the steps.