docs(peer): document transactional install model
Update the peer README and architecture notes to match the landed runtime: version.ini is the download sentinel, local/ is the install predicate, install state is recovered through .lanspread.json intents, and watcher rescans are operation-gated rather than time-debounced. Add IMPL_DECISIONS.md with the implementation-time choices that were not already prescribed by PLAN.md, including the just test recipe, the UI event compatibility bridge, reuse of the existing library index for per-ID rescans, and the split between active operation state and download cancellation tokens. Test Plan: - just fmt - just clippy - just test - just build Refs: PLAN.md
This commit is contained in:
@@ -69,7 +69,10 @@ When a peer is discovered:
|
||||
## When peers broadcast their game list
|
||||
|
||||
- Only on changes, not on a timer.
|
||||
- Use a short debounce (1-2 seconds) to coalesce bursts of filesystem events.
|
||||
- Filesystem events are gated per game ID instead of time-debounced:
|
||||
- an active operation lock drops events for that game;
|
||||
- a rescan already running for the ID sets a rescan-pending flag;
|
||||
- the running rescan loops once more when that flag was set.
|
||||
- Send `LibraryDelta` to known peers; send `LibrarySummary` on new connections.
|
||||
|
||||
## Local game scanning: fast and low cost
|
||||
@@ -78,21 +81,50 @@ When a peer is discovered:
|
||||
|
||||
1. Maintain a persistent on-disk index (per game):
|
||||
- `manifest_hash`, total size, file list (optional), and a fingerprint
|
||||
(version.ini mtime, .eti mtime/size, local install dir presence).
|
||||
(root-level `version.ini` mtime, root-level `.eti` mtime/size, and
|
||||
`local/` directory presence).
|
||||
2. Use filesystem watchers to update only changed games.
|
||||
3. Keep a fallback periodic scan with a long interval (minutes) to recover from
|
||||
missed events.
|
||||
3. Keep a 300-second fallback scan to recover from missed events.
|
||||
|
||||
### Fast-path scanning
|
||||
|
||||
- On startup, list only top-level game directories.
|
||||
- For each game, read a cheap fingerprint:
|
||||
- `.eti` size + mtime
|
||||
- `version.ini` mtime (if installed)
|
||||
- presence of `local/` content
|
||||
- root-level `.eti` file names, sizes, and mtimes
|
||||
- root-level `version.ini` mtime
|
||||
- presence of `local/` as a directory
|
||||
- If fingerprint unchanged, reuse cached size and manifest hash.
|
||||
- Only run a recursive scan for new or changed games.
|
||||
|
||||
## Local State and Recovery
|
||||
|
||||
Downloaded and installed are independent predicates:
|
||||
|
||||
- `downloaded` is true only when `<game_root>/version.ini` exists as a regular
|
||||
file. The sentinel is written last through `.version.ini.tmp` and atomic
|
||||
rename. An interrupted replacement leaves no restored old sentinel because
|
||||
archive bytes may already have changed.
|
||||
- `installed` is true when `<game_root>/local/` is a directory. The contents of
|
||||
`local/` are user-owned and are skipped by manifests, fingerprints, and file
|
||||
serving.
|
||||
|
||||
Reserved per-game paths:
|
||||
|
||||
- `.version.ini.tmp` and `.version.ini.discarded` are download transaction
|
||||
scratch files and are swept during startup recovery.
|
||||
- `.local.installing/` is extraction staging.
|
||||
- `.local.backup/` holds the previous install while an update or uninstall is in
|
||||
flight.
|
||||
- `.lanspread.json` is the atomic per-game intent log.
|
||||
- `.lanspread_owned` inside `.local.*` directories proves Lanspread ownership
|
||||
when the current intent is `None`.
|
||||
|
||||
Recovery reads `.lanspread.json` and combines the recorded intent with the
|
||||
observed `local/`, `.local.installing/`, and `.local.backup/` state. Intent
|
||||
states `Installing`, `Updating`, and `Uninstalling` prove ownership of the
|
||||
corresponding reserved directories even if the marker was not flushed before a
|
||||
crash. With intent `None`, markerless `.local.*` directories are left untouched.
|
||||
|
||||
### Result
|
||||
|
||||
Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
@@ -102,6 +134,10 @@ Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
- Keep `GetGame`/manifest requests, but keyed by `manifest_hash` so repeated
|
||||
calls can be skipped when unchanged.
|
||||
- Downloads remain chunked QUIC streams with the existing integrity checks.
|
||||
- A game is transferable only when its ID is in the catalog, no operation is
|
||||
active for that ID, and the root-level `version.ini` sentinel exists.
|
||||
- `local/` paths are never served, even if a stale or malicious manifest request
|
||||
asks for them.
|
||||
|
||||
## Fault tolerance rules
|
||||
|
||||
|
||||
Reference in New Issue
Block a user