fix(peer): settle current-protocol local state cleanup
The follow-up backlog had drifted into three settled peer/runtime issues: the legacy game-list fallback contradicted the one-wire-version policy, the Tauri shell still re-derived local install state from disk after peer snapshots, and `Availability::Downloading` existed even though active operations are already reported through a separate operation table. Remove the legacy `AnnounceGames` request and fallback service. Discovery now ignores peers that do not advertise the current protocol and a peer id, and library changes are sent through the current delta path only. This keeps the runtime aligned with the documented current-build-only interoperability model. Make peer `LocalGamesUpdated` snapshots authoritative for local fields in the Tauri database. The GUI-side catalog still owns static metadata such as names, sizes, and descriptions, but downloaded, installed, local version, and availability now come from the peer runtime instead of a second whole-library filesystem scan. Snapshot reconciliation also pins the missing-begin and missing-finish lifecycle cases in tests. Collapse availability back to the settled `Ready` and `LocalOnly` states. Aggregation now counts only `Ready` peers as download sources, and the frontend no longer carries a dead `Downloading` enum value. The core peer also exposes the small non-GUI hooks needed by scripted callers: startup options for state and mDNS, a local-ready event, direct connection, peer snapshots, and an explicit post-download install policy. Those hooks reuse the same current protocol path and do not add compatibility shims. Test Plan: - `git diff --check` - `just fmt` - `just clippy` - `just test` Refs: BACKLOG.md, FINDINGS.md, IMPL_DECISIONS.md
This commit is contained in:
+1
-130
@@ -11,136 +11,7 @@ here" cleanups that grow beyond the in-scope change.
|
||||
|
||||
---
|
||||
|
||||
## Legacy peer protocol fallback contradicts the wire policy
|
||||
|
||||
CLAUDE.md / AGENTS.md: *"There is only one wire version — the current one.
|
||||
No legacy peers, no compatibility shims, no fallback paths for older
|
||||
builds."*
|
||||
|
||||
Live legacy paths:
|
||||
|
||||
- `crates/lanspread-peer/src/services/legacy.rs` exists and is called from
|
||||
`discovery.rs:183-188` when the `Hello` handshake fails.
|
||||
- `discovery.rs:134` synthesizes `legacy-{addr}` peer IDs when `proto_ver`
|
||||
is absent from mDNS TXT records.
|
||||
- `discovery.rs:169` treats `proto_ver.is_none()` as handshake-eligible.
|
||||
- `update_and_announce_games` (`handlers.rs:605-624`) branches on
|
||||
`FEATURE_LIBRARY_DELTA` and falls back to `announce_games_to_peer`
|
||||
(sending `Request::AnnounceGames`) for peers that don't advertise the
|
||||
feature.
|
||||
- `Request::AnnounceGames` is still defined in
|
||||
`lanspread-proto/src/lib.rs:75` and handled in
|
||||
`services/stream.rs:116`.
|
||||
|
||||
Functionally inert today — current-build peers don't drop `Hello` — but
|
||||
code and stated policy disagree. Either delete the paths or revert the
|
||||
policy.
|
||||
|
||||
---
|
||||
|
||||
## Tauri keeps a parallel filesystem-derived scan
|
||||
|
||||
The peer now owns the install state machine (per PLAN.md:11), but Tauri
|
||||
still re-derives local install/download state from disk on every event:
|
||||
|
||||
- `refresh_games_list` (`src-tauri/src/lib.rs:489`) fires after every
|
||||
`update_game_db`, `update_local_games_in_db`, and
|
||||
`update_game_directory`. It calls `set_all_uninstalled()` and re-runs
|
||||
`update_game_installation_state` over every bundled-DB entry,
|
||||
re-reading `version.ini`, re-checking `local/`, re-parsing version
|
||||
strings.
|
||||
- `update_local_games_in_db` (`src-tauri/src/lib.rs:667-704`) just merged
|
||||
the peer's authoritative `Game` values into the Tauri-side `GameDB`.
|
||||
Immediately after, `refresh_games_list` re-derives the same fields
|
||||
from disk and overwrites the merged result.
|
||||
- The per-ID rescan optimization in `local_monitor.rs` is completely
|
||||
undone on the Tauri side: every peer event triggers a whole-library
|
||||
disk walk.
|
||||
|
||||
Today both paths reach the same conclusion. The risk is forward-looking:
|
||||
the moment one of the two derivation rules changes (a new `availability`
|
||||
rule, a new sentinel, a new ignore name), the two scanners can disagree
|
||||
silently with no rule for which wins.
|
||||
|
||||
**Fix when convenient:** `refresh_games_list` accepts the peer's `Game`
|
||||
slice and trusts it for local fields. Tauri's bundled DB stays as the
|
||||
source of truth for static metadata (name, description, max_players,
|
||||
thumbnail mapping), but `downloaded`/`installed`/`local_version`/
|
||||
`availability` come from the peer. `update_game_installation_state` and
|
||||
`set_all_uninstalled` go away. The dead log branch at
|
||||
`src-tauri/src/lib.rs:397-402` is obviated naturally by this.
|
||||
|
||||
---
|
||||
|
||||
## `Availability::Downloading` is wire-defined but unreachable
|
||||
|
||||
`crates/lanspread-db/src/db.rs:36-41`. The variant exists and serializes
|
||||
but `build_game_summary` only emits `Ready` or `LocalOnly`.
|
||||
Operation-table gating handles the in-progress case instead.
|
||||
|
||||
`peer_db::get_all_games` has a code path that lets a remote-advertised
|
||||
`Downloading` summary contribute `eti_version` to aggregation. If a
|
||||
future maintainer re-enables emitting `Downloading` from
|
||||
`build_game_summary`, aggregation will treat such peers as
|
||||
not-downloadable but still pull their version info.
|
||||
|
||||
**Decide-and-document task:** either remove the variant (matches the
|
||||
"current wire only" policy) or add a comment in the proto enum naming
|
||||
the contract.
|
||||
|
||||
---
|
||||
|
||||
## `update_game_installation_state` dead log branch
|
||||
|
||||
`src-tauri/src/lib.rs:397-402`:
|
||||
|
||||
```rust
|
||||
if eti_package_exists(&game_path, &game.id) && !downloaded {
|
||||
log::debug!("Game ... has archives but no version.ini sentinel; treating as not downloaded");
|
||||
}
|
||||
```
|
||||
|
||||
Side-effect-only log line. Either delete or wire to a UI affordance
|
||||
("partial download — retry?"). Obviated naturally if/when the Tauri
|
||||
parallel scan goes away.
|
||||
|
||||
---
|
||||
|
||||
## Untested edge: Tauri reconciliation with dropped lifecycle events
|
||||
|
||||
The Rust `reconcile_active_operations` test
|
||||
(`src-tauri/src/lib.rs:1117-1153`) covers map replacement but not the
|
||||
realistic case of an event sequence with a missing `begin` or `finish`.
|
||||
The TS side merges in `App.tsx`. Real failure mode: a missing `finish`
|
||||
followed by a `LocalGamesUpdated` snapshot should clear the spinner,
|
||||
and today's code does — but it's not pinned by a test.
|
||||
|
||||
Add a test if the spinner ever gets stuck in practice.
|
||||
|
||||
---
|
||||
|
||||
## Documentation drift
|
||||
|
||||
`FOLLOW_UP_2.md` still lists two items as "Still open" that have
|
||||
landed:
|
||||
|
||||
- **#10 `save_library_index` non-atomic** — landed in `fdad162`, atomic
|
||||
temp+fsync+rename at `local_games.rs:169-184`.
|
||||
- **#11 Split `download.rs`** — landed in `a251233`, split under
|
||||
`crates/lanspread-peer/src/download/`.
|
||||
|
||||
Either mark the doc complete or delete it. Anyone reading it as status
|
||||
will be misled.
|
||||
|
||||
The collection of plan/follow-up/review docs in the repo root
|
||||
(`PLAN.md`, `PLAN_AVAILABILITY.md`, `PLAN_ATOMIC_INDEX.md`,
|
||||
`PLAN_DOWNLOAD_SPLIT.md`, `FOLLOW_UP_PLAN.md`, `FOLLOW_UP_2.md`,
|
||||
`REVIEW_STEP_1..4.md`, `IMPL_DECISIONS.md`) is also getting noisy. A
|
||||
single retrospective archive folder for completed plans would help; a
|
||||
future PLAN.md should be self-terminating with explicit acceptance
|
||||
criteria so it doesn't spawn this many trailing docs.
|
||||
|
||||
---
|
||||
No open backlog items.
|
||||
|
||||
## How items leave this file
|
||||
|
||||
|
||||
Reference in New Issue
Block a user