refactor: prune unconsumed peer lifecycle events

An emit-vs-listen audit of the event surface showed the GUI is
state-as-source-of-truth: useGames renders the complete `games-list`
snapshot (full library + active_operations) and reconstructs status from
it, not from a stream of granular events. Several PeerEvents were emitted
but had no consumer at all -- no frontend `listen()` and no peer-cli
scenario assertion -- so they were pure dead weight that made the backend
look event-driven when it no longer is.

This prunes that dead surface in two parts.

1. Remove three PeerEvent variants with no consumer: InstallGameBegin,
   UninstallGameBegin, and RemoveDownloadedGameBegin. The operation-start
   transition is still observable via ActiveOperationsChanged (the
   snapshot already carries the Installing/Updating/Uninstalling/
   RemovingDownload kind), so nothing is lost. This drops their emit
   sites in handlers.rs, the begin-event assertions in the peer's
   lifecycle unit tests (the asserted sequence is now
   ActiveOperationsChanged(kind) -> LocalLibraryChanged ->
   ActiveOperationsChanged([]) -> *Finished), the peer-cli JSONL
   mappings (install-begin/uninstall-begin/remove-download-begin) plus
   the now-orphaned install_operation_name helper and InstallOperation
   import, and the matching Tauri handler arms.

2. Drop Tauri webview emits that no frontend listener consumed:
   peer-local-ready, game-download-begin, game-download-pre,
   game-download-finished, game-uninstall-finished, and
   peer-connected/-disconnected/-discovered/-lost. The log lines and all
   real side effects are kept (handle_got_game_files still forwards
   PeerCommand::DownloadGameFiles). The orphaned emit_peer_addr_event and
   handle_download_finished helpers and the now-unused SocketAddr import
   are removed. peer-runtime-failed is kept pending a decision on
   surfacing runtime failures in the GUI.

Why not re-wire instead: under state-as-source-of-truth, per-event UI
state is exactly the pattern this project abandoned. Live progress
already flows via game-download-progress, and the peer-cli's chunk,
timing-trigger, and transition assertions read events that are retained
(download-begin, download-chunk-finished, the *-finished/*-failed
terminals), so test coverage is unchanged.

Behavior change: none functional. The Tauri backend no longer emits
events nothing listened to; the GUI is unchanged. The peer-cli no longer
emits the three *-begin JSONL events. PeerEvent is a workspace-internal
UI-reporting type, not a wire-protocol type, so there is no protocol or
version impact and all consumers are updated in this commit.

Docs: PEER_CLI_SCENARIOS.md S39 no longer lists install-begin (with a
note that the start transition is visible via active-operations-changed),
and a dated Run Log entry records the removal. The historical 2026-05-18
run-log note is left intact as a dated observation.

Test Plan:
- just test: pass (incl. peer lifecycle event-sequence tests).
- just clippy: pass (-D warnings, all targets).
- just frontend-test: pass (11/11, incl. streamed-install gating/labels).
- just build: pass (release, no bundle).
- Not run: the Docker S39-S47 matrix (run_extended_scenarios.py); those
  scenarios never asserted the removed *-begin events, so coverage is
  unaffected. just fmt's tombi step needs network and was skipped; no
  TOML changed.

Refs: peer event-surface emit-vs-listen audit; no external consumers of
the removed events.
This commit was merged in pull request #28.
This commit is contained in:
2026-06-20 19:54:53 +02:00
parent c25dc7420e
commit 0b8e1e7f92
5 changed files with 24 additions and 178 deletions
+2 -85
View File
@@ -784,13 +784,6 @@ async fn commit_streamed_install(
ctx.active_operations.clone(),
tx_notify_ui.clone(),
);
events::send(
tx_notify_ui,
PeerEvent::InstallGameBegin {
id: id.clone(),
operation: InstallOperation::Installing,
},
);
match transaction.commit().await {
Ok(()) => {
@@ -918,14 +911,6 @@ async fn run_started_install_operation(
tx_notify_ui.clone(),
);
let result = {
events::send(
tx_notify_ui,
PeerEvent::InstallGameBegin {
id: id.clone(),
operation,
},
);
let state_dir = ctx.state_dir.as_ref();
match operation {
InstallOperation::Installing => {
@@ -995,14 +980,7 @@ async fn run_uninstall_operation(ctx: &Ctx, tx_notify_ui: &UnboundedSender<PeerE
ctx.active_operations.clone(),
tx_notify_ui.clone(),
);
let result = {
events::send(
tx_notify_ui,
PeerEvent::UninstallGameBegin { id: id.clone() },
);
install::uninstall(&game_root, ctx.state_dir.as_ref(), &id).await
};
let result = install::uninstall(&game_root, ctx.state_dir.as_ref(), &id).await;
match result {
Ok(()) => {
@@ -1068,14 +1046,7 @@ async fn run_remove_downloaded_operation(
ctx.active_operations.clone(),
tx_notify_ui.clone(),
);
let result = {
events::send(
tx_notify_ui,
PeerEvent::RemoveDownloadedGameBegin { id: id.clone() },
);
install::remove_downloaded(&game_dir, &id).await
};
let result = install::remove_downloaded(&game_dir, &id).await;
match result {
Ok(()) => {
@@ -2032,13 +2003,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Updating),
);
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::InstallGameBegin {
id,
operation: InstallOperation::Updating
} if id == "game"
));
assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!(
recv_event(&mut rx).await,
@@ -2063,13 +2027,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Installing),
);
match recv_event(&mut rx).await {
PeerEvent::InstallGameBegin { id, operation } => {
assert_eq!(id, "game");
assert_eq!(operation, InstallOperation::Installing);
}
_ => panic!("expected InstallGameBegin"),
}
assert_local_update(recv_event(&mut rx).await, true, true);
assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!(
@@ -2124,13 +2081,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Installing),
);
match recv_event(&mut rx).await {
PeerEvent::InstallGameBegin { id, operation } => {
assert_eq!(id, "game");
assert_eq!(operation, InstallOperation::Installing);
}
_ => panic!("expected InstallGameBegin"),
}
assert_local_update(recv_event(&mut rx).await, true, true);
assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!(
@@ -2184,13 +2134,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Updating),
);
match recv_event(&mut rx).await {
PeerEvent::InstallGameBegin { id, operation } => {
assert_eq!(id, "game");
assert_eq!(operation, InstallOperation::Updating);
}
_ => panic!("expected InstallGameBegin"),
}
assert_local_update(recv_event(&mut rx).await, true, true);
assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!(
@@ -2215,13 +2158,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Installing),
);
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::InstallGameBegin {
id,
operation: InstallOperation::Installing
} if id == "game"
));
let game = local_update_game(recv_event(&mut rx).await, true, true);
assert_eq!(game.local_version.as_deref(), Some("20240101"));
assert_active_update(recv_event(&mut rx).await, &[]);
@@ -2238,13 +2174,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Updating),
);
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::InstallGameBegin {
id,
operation: InstallOperation::Updating
} if id == "game"
));
let game = local_update_game(recv_event(&mut rx).await, true, true);
assert_eq!(game.local_version.as_deref(), Some("20250101"));
assert_active_update(recv_event(&mut rx).await, &[]);
@@ -2258,10 +2187,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Uninstalling),
);
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::UninstallGameBegin { id } if id == "game"
));
let game = local_update_game(recv_event(&mut rx).await, false, true);
assert_eq!(game.local_version.as_deref(), Some("20250101"));
assert_active_update(recv_event(&mut rx).await, &[]);
@@ -2289,10 +2214,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Uninstalling),
);
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::UninstallGameBegin { id } if id == "game"
));
assert_local_update(recv_event(&mut rx).await, false, true);
assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!(
@@ -2324,10 +2245,6 @@ mod tests {
recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::RemovingDownload),
);
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::RemoveDownloadedGameBegin { id } if id == "game"
));
let PeerEvent::LocalLibraryChanged { games } = recv_event(&mut rx).await else {
panic!("expected LocalLibraryChanged");
};
-9
View File
@@ -127,23 +127,14 @@ pub enum PeerEvent {
DownloadGameFilesFailed { id: String },
/// All peers with the game have disconnected during download.
DownloadGameFilesAllPeersGone { id: String },
/// Install or update transaction has started for a game.
InstallGameBegin {
id: String,
operation: InstallOperation,
},
/// Install or update transaction has completed successfully.
InstallGameFinished { id: String },
/// Install or update transaction has failed after rollback.
InstallGameFailed { id: String },
/// Uninstall transaction has started for a game.
UninstallGameBegin { id: String },
/// Uninstall transaction has completed successfully.
UninstallGameFinished { id: String },
/// Uninstall transaction has failed after rollback.
UninstallGameFailed { id: String },
/// Downloaded archive removal has started for an uninstalled game.
RemoveDownloadedGameBegin { id: String },
/// Downloaded archive removal has completed successfully.
RemoveDownloadedGameFinished { id: String },
/// Downloaded archive removal has failed before deleting the game root.