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.4 KiB
M4a task 13: a turn on loop.sock, and its answer in the thread
Branch: m4a (run git switch m4a; git status --short must be empty, otherwise stop)
Commit subject: gatewayd: deliver, turns on loop.sock and answers posted in their thread
Goal
A batch of messages (task 11) becomes one turn on loopd's socket, exactly as bxctl chat sends
one (read crates/bxctl/src/chat.rs, run_turn): one frame {"kind": "turn", "body": {"session", "content", "resume"}} with id 1, then events until a final turn_done or error. What comes back
is posted in the thread (spec section 8):
- No thinking and no status reach Mattermost. The one event that is shown:
approval_pending, posted once aswaiting for approval <id>: approve or deny it withbxctl(Mattermost approvals arrive in M4b)(the skeleton'sapproval_text). turn_done→ its content, split into posts of at most 16,000 characters (a Mattermost post holds 16,383), at the last newline before the limit, or at the limit when there is none, in order. An empty answer is posted as(the answer was empty): Mattermost refuses an empty post.- An
errorframe →Error: <code>: <detail>, where the code is the snake_case name (no_such_session), and the detail carriesloopd's runbook pointer when it has one. loop.sockcannot be reached, or closes early, or sends a frame that does not belong →LOOP_DOWN, and the messages are not sent again:loopdmay have run them.- A reply in a thread
loopddoes not know (no_such_sessionwithresume: true) is sent once more withresume: false, creating the session, asbxctl chat --sessiondoes. Only then.
Files
- Copy:
crates/gatewayd/tests/deliver.rs,crates/gatewayd/tests/support/fake_loop.rs, and the skeletoncrates/gatewayd/src/deliver.rs - Modify:
crates/gatewayd/src/lib.rs(pub mod deliver;),docs/implementer-log.md
The skeleton
Written: MAX_POST (16,000), LOOP_DOWN, EMPTY_ANSWER, the trait Poster (somewhere to post:
Mattermost's Client, or a test's record) and its impl for Client, Outcome (Answer,
Refused, LoopDown(why)), approval_text.
To fill: error_text, split_answer, one_turn, run_turn, deliver. split_answer counts
characters, not bytes, and never cuts inside one: find the byte index of the 16,000th character
with char_indices().nth(MAX_POST).
Steps
- 1. Copy.
git switch m4a, thencp docs/plans/M4a/files/crates/gatewayd/tests/deliver.rs crates/gatewayd/tests/ && cp docs/plans/M4a/files/crates/gatewayd/tests/support/fake_loop.rs crates/gatewayd/tests/support/ && cp docs/plans/M4a/files/crates/gatewayd/src/deliver.rs crates/gatewayd/src/. Addpub mod deliver;tolib.rs. - 2. See it fail.
cargo test -p gatewayd --test deliver. Expected: it compiles and 9 tests fail. - 3. Fill
error_text,split_answer,one_turn,run_turn,deliver,cargo check -p gatewaydafter each. - 4. See it pass.
cargo test -p gatewayd --test deliver. Expected: 9 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 deliverpasses;make gateprintsgate: ok.
Stop and report if
- A test seems to need a turn sent twice in any case other than the one above.