refactor(bot): split fsm.rs into fsm/{state,status,machine,flow,encoding}#48
Merged
BlindMaster24 merged 2 commits intomainfrom Apr 24, 2026
Merged
refactor(bot): split fsm.rs into fsm/{state,status,machine,flow,encoding}#48BlindMaster24 merged 2 commits intomainfrom
BlindMaster24 merged 2 commits intomainfrom
Conversation
…ing}
bot/fsm.rs had grown to 559 lines holding five distinct concerns
behind a single file:
* lifecycle enums (DialogStatus, DialogTimeoutPolicy) with their wire
encode/decode helpers;
* the persistent DialogState record with its netstring-based encode()
/ decode() plus builder methods;
* the self-contained DialogFlow declarative step sequence;
* the DialogMachine mutating facade over &mut dyn StateStore (lifecycle,
timeout enforcement, metadata, flow navigation); and
* shared low-level plumbing (netstring codec, wall-clock helpers,
session-id generator, internal metadata keys, and the encoding
version constant).
Split along those natural boundaries per the AGENTS.md guideline
("split files when a module grows beyond ~400-600 lines or mixes
multiple responsibilities"):
* fsm/mod.rs - module wiring + public re-exports.
* fsm/status.rs - DialogStatus, DialogTimeoutPolicy, encode/decode.
* fsm/flow.rs - DialogFlow (self-contained).
* fsm/encoding.rs - netstring codec, now_unix_ms / duration_to_millis,
generate_session_id, DIALOG_ENCODING_VERSION, INTERNAL_SESSION_KEY,
INTERNAL_TIMEOUT_POLICY_KEY. Everything here is pub(super) so the
encoding helpers stay invisible outside the fsm/ module tree.
* fsm/state.rs - DialogState + encode/decode + metadata helpers.
* fsm/machine.rs - DialogMachine lifecycle/timeout/flow operations.
External call sites are unchanged. bot/mod.rs kept its re-export:
pub use fsm::{DialogFlow, DialogMachine, DialogState, DialogStatus,
DialogTimeoutPolicy};
So bot::{DialogFlow, DialogMachine, DialogState, DialogStatus,
DialogTimeoutPolicy} continue to resolve bit-for-bit. The external
users (lib.rs, bot/context.rs with 26 references) compile unchanged.
Structural only, no semantic change:
* Public items keep their paths (bot::*) and their public
visibilities.
* DialogState fields remain pub (the struct was pub + #[non_exhaustive]
before).
* Internal helpers (netstring, parse_netstring, now_unix_ms,
duration_to_millis, generate_session_id) downgraded from private-
to-single-file to pub(super) so siblings inside fsm/ can call them;
they were already invisible outside the file, and remain invisible
outside the module tree.
* DIALOG_ENCODING_VERSION, INTERNAL_SESSION_KEY, and
INTERNAL_TIMEOUT_POLICY_KEY lived as private module-level constants
in fsm.rs; now pub(super) in fsm/encoding.rs so state.rs and
machine.rs can reference them. Same effective privacy.
* Method bodies in all five impls (DialogState, DialogStatus,
DialogTimeoutPolicy, DialogFlow, DialogMachine) are byte-for-byte
identical to the pre-split file.
* Doc comments added on public items where missing_docs = "deny"
started flagging them in their new homes (no behavioral effect).
Local verification:
* cargo fmt --all --check clean
* cargo clippy --workspace --all-targets --all-features -- -D warnings clean
* cargo test --workspace --all-features -> 287 passed / 0 failed
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Devin Review flagged: AGENTS.md 'Current Module Baseline' still described src/bot/fsm.rs as a single file. Update the entry to describe the new src/bot/fsm/ directory-first layout, matching the pattern used for bot/router and client.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bot/fsm.rshad grown to 559 lines holding five distinct concernsbehind a single file:
DialogStatus,DialogTimeoutPolicy) with theirwire encode/decode helpers;
DialogStaterecord with its netstring-basedencode()/decode()plus builder methods;DialogFlowdeclarative step sequence;DialogMachinemutating façade over&mut dyn StateStore(lifecycle, timeout enforcement, metadata, flow navigation); and
session-id generator, internal metadata keys, and the encoding
version constant).
Split along those natural boundaries per the AGENTS.md guideline
("split files when a module grows beyond ~400-600 lines or mixes
multiple responsibilities"):
fsm/mod.rs- module wiring + public re-exports.fsm/status.rs-DialogStatus,DialogTimeoutPolicy, encode/decode.fsm/flow.rs-DialogFlow(self-contained).fsm/encoding.rs- netstring codec,now_unix_ms/duration_to_millis,generate_session_id,DIALOG_ENCODING_VERSION,INTERNAL_SESSION_KEY,INTERNAL_TIMEOUT_POLICY_KEY. Everything hereis
pub(super)so the encoding helpers stay invisible outside thefsm/module tree.fsm/state.rs-DialogState+ encode/decode + metadata helpers.fsm/machine.rs-DialogMachinelifecycle/timeout/flow operations.External call sites are unchanged
bot/mod.rskept its re-export:So
bot::{DialogFlow, DialogMachine, DialogState, DialogStatus, DialogTimeoutPolicy}continue to resolve bit-for-bit. The externalusers (
lib.rs,bot/context.rswith 26 references) compile unchanged.Structural only, no semantic change
bot::*) and their publicvisibilities.
DialogStatefields remainpub(the struct waspub+#[non_exhaustive]before).netstring,parse_netstring,now_unix_ms,duration_to_millis,generate_session_id) downgraded fromprivate-to-single-file to
pub(super)so siblings insidefsm/can call them; they were already invisible outside the file, and
remain invisible outside the module tree.
DIALOG_ENCODING_VERSION,INTERNAL_SESSION_KEY, andINTERNAL_TIMEOUT_POLICY_KEYlived as private module-levelconstants in
fsm.rs; nowpub(super)infsm/encoding.rssostate.rsandmachine.rscan reference them. Same effectiveprivacy.
DialogState,DialogStatus,DialogTimeoutPolicy,DialogFlow,DialogMachine) arebyte-for-byte identical to the pre-split file.
missing_docs = "deny"started flagging them in their new homes. No behavioral effect.
Net diff:
fsm.rsdeleted (559 lines), six new files created(
mod.rs~35,status.rs~55,flow.rs~100,encoding.rs~75,state.rs~250,machine.rs~215). Git detectsfsm.rs -> fsm/state.rsas the primary rename.Local verification:
cargo fmt --all --checkcleancargo clippy --workspace --all-targets --all-features -- -D warningscleancargo test --workspace --all-features-> 287 passed / 0 failedReview & Testing Checklist for Human
fsm/mod.rsre-exports:bot::DialogFlow,bot::DialogMachine,bot::DialogState,bot::DialogStatus,bot::DialogTimeoutPolicymust all still resolve fromdownstream crates.
bot/context.rs(26 references) is thebiggest internal user and compiles unchanged.
constants and helpers that were previously private to a single
file are now
pub(super)inside thefsm/module tree. Thestruct
DialogStatekeeps itspubfields as before; theDialogStatus/DialogTimeoutPolicyencode/decodehelpersare
pub(super)(previously private; they are only called bystate.rs).DialogState::decoderetainsthe
dialog|stepfallback for values that predateDIALOG_ENCODING_VERSION = "v2". This matters for stores thatwere populated by an older bot build.
Notes
This is the next PR in the P0 structural refactor queue after #44
(dedupe
can_issue_logged_in_command), #45 (extractpoll_command_completion), #46 (splitclient/bus.rs), and #47(split
bot/storage.rs, currently open).Next up:
client/hooks/builders.rs(574 lines),types/entities/media_common.rs(458 lines), and theclient/backend.rs(1334 lines) +client/backend_mock.rs(1101lines) pair. Then the P1 API tweaks (jitter,
StreamTypes,SdkErrorCode,TimeoutKind,SecretString, indexed dispatch) allwith their own tests. Finally a big #58 test-fill PR driven by
scripts/audit_teamtalk_coverage.py.Branches from clean
main; all the refactor PRs are independentagainst
mainso they can be merged in any order. If any touch thesame file I'll rebase the later one after its predecessor lands.
The pre-existing
semverCI gate is expected to remain red;release-plzhandles the eventual version bump. Every other check isexpected to pass.
Link to Devin session: https://app.devin.ai/sessions/71fdd6196cb74723a2e277bb81993a9c
Requested by: @BlindMaster24