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>
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: thecreate_atof 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 toloopdand 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 skeletoncrates/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, thencp 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/. Addpub mod state;tolib.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 gatewaydafter 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, 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 statepasses;make gateprintsgate: ok.