Phase 2: Module directory and pure state¶
Master plan: PLAN-app-split.md.
Planning effort¶
Planned at medium effort. Every step is a move with no behaviour
change, so the review effort goes into checking that nothing but
use, mod and visibility changed -- see the verification script
under Definition of done.
Scope¶
In:
- Turn
ryll/src/app.rsintoryll/src/app/mod.rs. - Move the code that depends neither on egui nor on
RyllApp's fields into five child modules, each carrying its own tests.
Out:
- Anything that touches
RyllApp's fields or egui:process_events, the bug-report flow and connection lifecycle (phase 3), theuipanels (phase 4). - Any edit to the moved code beyond
use,modandpub(super). If a move reveals something worth fixing, record it in the master plan's close-out notes and leave the code alone.
What the survey found¶
Surveyed against develop at 70232a2, after #483 merged.
- Every item the master plan lists still exists, and none depends
on egui or on
RyllApp.SessionInitDecisionandclassify_session_initialized,NotificationSnapshotStore(withNotificationSnapshotEntry),ModalVariant,ReconnectState,ReconnectPolicyandmodal_variant_notification,Statistics,BandwidthTrackerandLatencyTracker, and the free functionsis_primary_surface,push_with_cap,recent_lag_stats,auto_fit_size_acceptable,compute_auto_resize,compute_outgoing_resizeandresolution_notification_dueare all top-level items inapp.rs. The onlyself.references among them are inside their ownimplblocks, and the only mention of egui is a doc comment oncompute_outgoing_resize. - The repository uses
mod.rsstyle (ryll/src/web/mod.rs,shakenfist-spice-renderer/src/channels/mod.rs), soapp/mod.rsmatches the master plan and the codebase. - Several constants are shared between moved code and code that
stays.
STATS_BAR_HEIGHTis used by both resize helpers and byui;MAX_AUTO_FIT_DIMENSIONbyauto_fit_size_acceptableand by the oversized-surface warning inprocess_events;MIN_SESSION_RESPAWN_INTERVALbyclassify_session_initializedand byRyllApp;RECENT_LAG_RING_CAPbypush_with_capand the bug-report snapshot;RESOLUTION_NOTIFY_DEBOUNCEbyui's call toresolution_notification_due. Decision 3 settles where they live. - Two pure items the master plan did not place.
format_expiry_localformats aReconnectPolicyticket expiry for the reconnect modal, so it belongs inreconnect.rsand moves in this phase.event_drops_need_noticeandEVENT_DROP_NOTICEare pure but serveprocess_events, so they go to phase 3'sevents.rs.screenshot_paths,validate_regionanddefault_arrow_cursorstay put for phases 3 and 4 as planned. - The tests are one 1,000-line module at the foot of
app.rs, usinguse super::*. Some exercise egui directly (logic_only_passes_keep_answering_repaint_requests,window_focused_is_current_in_a_logic_only_pass) and must stay inmod.rs; the rest test the items above and move with them. - Master plan open question 4 (blame continuity). Pure-move
commits are followed by
git blame -C -Cwithout help, and git's rename detection followsapp.rstoapp/mod.rsas long as the rename commit changes nothing else. A.git-blame-ignore-revsfile is therefore not needed for this phase; decision 4 records the commit discipline that makes that true.
The master plan's phase 2 section is corrected in this commit to
place format_expiry_local and event_drops_need_notice (finding 4)
and to record the answer to open question 4.
Decisions¶
- One commit per module, after a rename-only commit. Step 2a is
git mvand nothing else, so history follows the file. Each subsequent step moves one module. A reviewer can read each commit as "these lines leftmod.rsand arrived here" without untangling five moves at once. - Visibility is
pub(super)and nothing wider. Moved items are used only byapp/mod.rsand its other children.pub(super)keeps them private to theappmodule, exactly as they are today as private items ofapp.rs. Fields of moved structs thatmod.rsreads directly becomepub(super)too; no accessor methods are added, because that would be an edit, not a move. - A constant lives in the module that gives it meaning.
RECONNECT_*andMAX_RECONNECT_ATTEMPTSgo toreconnect.rs;NOTIFICATION_SNAPSHOT_*tonotification_snapshots.rs;BANDWIDTH_HISTORY_LEN,LATENCY_HISTORY_LEN,FPS_WINDOW_SIZEandRECENT_LAG_RING_CAPtostats.rs;STATS_BAR_HEIGHT,MAX_AUTO_FIT_DIMENSIONandRESOLUTION_NOTIFY_DEBOUNCEtoresize.rs;MIN_SESSION_RESPAWN_INTERVALtosession_init.rs.mod.rsimports what it still uses.STATS_BAR_HEIGHTis the one a reviewer might expect to stay with the UI; it moves because the resize arithmetic is what depends on its exact value, anduimerely lays out a bar of that height. - Tests move with the code they test, and egui tests stay. Each
new module gets
#[cfg(test)] mod tests { use super::*; ... }holding the tests for its items, moved verbatim, with test helpers (err,no_policy,epoch,one_shot_policy,expiring_at,fresh_traffic) moving with the tests that use them. A test that exercises items from two modules stays inmod.rs. - The reconnect state machine stays in ryll (phase 1, finding 6),
so
reconnect.rsis a child ofapp, not a renderer module.
Step plan¶
| Step | Effort | Model | Isolation | Brief for sub-agent |
|---|---|---|---|---|
| 2a | low | sonnet | none | Rename only: git mv ryll/src/app.rs ryll/src/app/mod.rs. No content change. |
| 2b | medium | sonnet | none | Move session_init.rs and notification_snapshots.rs. Detail below. |
| 2c | medium | sonnet | none | Move reconnect.rs. Detail below. |
| 2d | medium | sonnet | none | Move stats.rs and resize.rs. Detail below. |
| 2e | low | sonnet | none | Update live references to the old ryll/src/app.rs path. Added during execution; detail below. |
Every step: build and test with make lint and make test (cargo runs
in the devcontainer; never install Rust on the host), run
pre-commit run --all-files, and run the move-check script from
Definition of done against the step's diff. Do not commit; the
management session reviews and commits each step.
Step 2a: rename¶
git mv ryll/src/app.rs ryll/src/app/mod.rs. mod app; in
ryll/src/main.rs resolves to the new path unchanged. Confirm
git diff --cached -M --stat shows a pure rename (100% similarity).
Commit: "Move app.rs to app/mod.rs."
Step 2b: session_init.rs and notification_snapshots.rs¶
The two smallest modules, done together because each is a single type and its tests.
ryll/src/app/session_init.rs:SessionInitDecision,classify_session_initialized,MIN_SESSION_RESPAWN_INTERVAL, and the teststhe_first_session_is_always_accepted,re_announcing_the_held_session_is_a_duplicate,alternating_session_ids_are_rate_limited,a_genuine_relink_after_the_window_is_acceptedanda_new_id_with_no_prior_acceptance_is_accepted.ryll/src/app/notification_snapshots.rs:NotificationSnapshotEntry,NotificationSnapshotStore,NOTIFICATION_SNAPSHOT_TTL,NOTIFICATION_SNAPSHOT_CAP, thesnapshot_store_*tests and theirfresh_traffichelper.notification_bug_report_type_serialisestests a bugreport type, not the store: leave it inmod.rs.
Add mod session_init; and mod notification_snapshots; to
mod.rs with use lines for what it still references. Keep each
item's doc comment attached to it.
Commit: "Move session-init and snapshot store out of app."
Step 2c: reconnect.rs¶
ModalVariant, ReconnectState, ReconnectPolicy and their
impls, modal_variant_notification, format_expiry_local,
MAX_RECONNECT_ATTEMPTS, RECONNECT_BACKOFF_SECS,
RECONNECT_CLUSTER_RESET, and the reconnect_*, ticket_* and
connection_event_message_format_* tests with their helpers (err,
no_policy, epoch, one_shot_policy, expiring_at). Check each
helper's users before moving it; one used by tests that stay must
stay too.
Commit: "Move the reconnect state machine out of app."
Step 2d: stats.rs and resize.rs¶
ryll/src/app/stats.rs:Statistics,BandwidthTracker,LatencyTracker,push_with_cap,recent_lag_stats,BANDWIDTH_HISTORY_LEN,LATENCY_HISTORY_LEN,FPS_WINDOW_SIZE,RECENT_LAG_RING_CAP, and thelatency_tracker_*,recent_lag_stats_*andpush_with_cap_*tests. Thepub use shakenfist_spice_renderer::ByteCounter;re-export next toStatisticsstays inmod.rsunchanged. The survey found no importer ofcrate::app::ByteCounteroutsideapp.rs, so it is probably vestigial, but removing it is an edit, not a move;stats.rsimportsByteCounterfrom the renderer crate directly.ryll/src/app/resize.rs:is_primary_surface,auto_fit_size_acceptable,compute_auto_resize,compute_outgoing_resize,resolution_notification_due,STATS_BAR_HEIGHT,MAX_AUTO_FIT_DIMENSION,RESOLUTION_NOTIFY_DEBOUNCE, and thecompute_auto_resize_*,compute_outgoing_resize_*,round_trip_*,auto_fit_size_acceptable_*,is_primary_surface_*andresolution_notification_due_*tests.
Commit: "Move stats and resize helpers out of app."
Step 2e: references to the old path¶
Added during execution: the rename in step 2a left 27 references to
ryll/src/app.rs in live documentation and code comments (found
with git grep -n 'app\.rs' -- ':!docs/plans/'). Plan files are
historical records and keep their references.
- Items that moved in this phase name their new file
(
LatencyTrackerinryll/src/app/stats.rs, the reconnect tests inryll/src/app/reconnect.rs, and so on). - Items still in
mod.rsare named by symbol with the module directory (RyllApp::reconnectinryll/src/app/), so phases 3 and 4 do not break the reference again. ARCHITECTURE.md's source tree shows theapp/directory and its modules.
Commit: "Point references at the app/ module."
Risks and mitigations¶
- A "move" that quietly edits code. Mitigation: the move-check script below, run by the management session on every step's diff before committing, plus reading every hunk that is not a pure addition or deletion.
- A test silently dropped in transit. Mitigation: the management
session compares the number of tests
make testreports for theryllbinary before step 2a and after step 2d; they must be equal. - Over-broad visibility. Mitigation:
grep -rn 'pub fn\|pub struct\|pub enum\|pub const' ryll/src/app/after each step must show onlypub(super)on moved items (theByteCounterre-export excepted). STATS_BAR_HEIGHTmoving away from the UI surprises a reader. Mitigation: decision 3 states why; the reviewer checksuiimports it fromresizerather than redefining it.
Definition of done¶
git grep -n 'app\.rs' -- ':!docs/plans/'finds no reference to the old path.ryll/src/app.rsdoes not exist;ryll/src/app/holdsmod.rs,session_init.rs,notification_snapshots.rs,reconnect.rs,stats.rsandresize.rs.- None of the items listed in the step briefs is defined in
app/mod.rs:grep -nE '^(pub(\(super\))? )?(struct|enum|fn|const) (SessionInitDecision|NotificationSnapshotStore|ReconnectState|ReconnectPolicy|ModalVariant|Statistics|BandwidthTracker|LatencyTracker|compute_auto_resize|compute_outgoing_resize|STATS_BAR_HEIGHT)\b' ryll/src/app/mod.rsprints nothing. - The
ryllbinary's test count is unchanged fromdevelop. - The move check passes for every step. It compares the multiset of
non-blank, non-
use/mod/visibility-only lines removed frommod.rswith those added to the new files:
d=$(mktemp -d)
git diff HEAD~1 -U0 -- ryll/src/app/mod.rs | grep '^-[^-]' | sed 's/^-//' \
| sed -E 's/pub\(super\) //' | grep -vE '^\s*(use |mod |$)' | sort > "$d/removed"
git diff HEAD~1 -U0 -- ryll/src/app/ ':!ryll/src/app/mod.rs' | grep '^+[^+]' | sed 's/^+//' \
| sed -E 's/pub\(super\) //' | grep -vE '^\s*(use |mod |$)' | sort > "$d/added"
diff "$d/removed" "$d/added"
The only lines it may report are the new files' #[cfg(test)] mod
tests { wrappers and their closing braces.
* make lint, make test and pre-commit run --all-files pass.
* No behaviour change, so no live-guest test is needed for this
phase; the CI GUI build and unit tests are the evidence.
Back brief¶
Before executing any step of this plan, please back brief the operator as to your understanding of the plan and how the work you intend to do aligns with that plan.