Files
boxmaker/docs/plans/M4a/12-gatewayd-state.md
T
kyleandClaude Opus 5.5 0339dc13b2 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>
2026-09-23 19:05:44 -07:00

3.0 KiB

M4a task 12: the state file

Branch: m4a (run git switch m4a; git status --short must be empty, otherwise stop) Commit subject: gatewayd: state, what was handled, our threads and turns in flight

Goal

<home>/gateway/state.json lets gatewayd pick up where it left off (spec section 9):

{"channels": {"<channel id>": 1758650000000}, "recent": ["<post id>", ],
 "threads": ["<root post id>", ],
 "in_flight": [{"session": "mm-…", "channel": "<id>", "root": "<id>"}]}
  • channels: the create_at of the last post handled in each channel; catching up starts there. A mark never moves back.
  • recent: the ids of the last 500 posts handled (RECENT_KEPT), so a post seen twice (live and in a catch-up) is handled once.
  • threads: the roots of the threads this Boxmaker takes part in, in channels; the newest 5,000 (THREADS_KEPT).
  • in_flight: turns sent to loopd and not yet answered; after a restart each gets an "interrupted" reply.

It is our format: unknown fields are errors, and every id must be a valid Mattermost id. It is written atomically after every change, in the same six steps as brokerd/src/state.rs's persist (read it). A file that exists but cannot be read, parsed or written stops gatewayd with see docs/runbook.md#gateway-state-damaged: guessing would answer posts twice. Only a missing file is a first start.

Files

  • Copy: crates/gatewayd/tests/state.rs, and the skeleton crates/gatewayd/src/state.rs
  • Modify: crates/gatewayd/src/lib.rs (pub mod state;), docs/implementer-log.md

The skeleton

Written: RECENT_KEPT, THREADS_KEPT, RUNBOOK, StateError (Read(PathBuf, String), Write(PathBuf, io::Error)) and its Display (the path, the problem, and the pointer on its own line), InFlight, the private StateFile, State { path, file }.

To fill: StateFile::problem, State::load, save, persist, and the methods seen, handled, since, channels, mark, knows_thread, join_thread, start_turn, end_turn, take_in_flight. Every method that changes the state saves it before it returns, and returns the save's error.

Steps

  • 1. Copy. git switch m4a, then cp docs/plans/M4a/files/crates/gatewayd/tests/state.rs crates/gatewayd/tests/ && cp docs/plans/M4a/files/crates/gatewayd/src/state.rs crates/gatewayd/src/. Add pub mod state; to lib.rs.
  • 2. See it fail. cargo test -p gatewayd --test state. Expected: it compiles and 4 tests fail.
  • 3. Fill problem, load, persist, save, then the methods, cargo check -p gatewayd after each.
  • 4. See it pass. cargo test -p gatewayd --test state. Expected: 4 passed, in well under a second.
  • 5. Run the gate. cargo fmt --all, then make 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 state passes; make gate prints gate: ok.