9288fda037
NEXT_STEPS item 6 called for the remaining streamed-install edge cases to be covered in the peer-cli matrix. Add S43-S47 for already-installed rejection, corrupt archive rollback, sender disconnect, receiver cancel, and sorted multi-archive streaming. The receiver-cancel scenario needs the harness to drive the same runtime path as the GUI, so `lanspread-peer-cli` now accepts a narrow `cancel-download` command that forwards to `PeerCommand::CancelDownload`. A parser test covers the new JSONL command shape. Add `fixture-multi/cnctw`, a tiny two-archive RAR fixture. S47 uses it to prove streamed installs process root `.eti` archives in sorted order and commit only extracted `local/` payloads, not the root archives or `version.ini` sentinel. Test Plan: - just fmt - python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py - python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S43 S44 S45 S46 S47 --build-image - just test - just clippy - git diff --check - git diff --cached --check Refs: NEXT_STEPS.md item 6
65 lines
3.1 KiB
Markdown
65 lines
3.1 KiB
Markdown
# Streamed Install Next Steps
|
||
|
||
I’d treat the prototype as proof of the hard part: “can we stream
|
||
archive-derived install bytes into `local/` without making the receiver a
|
||
source?” Yes. Next I’d harden the pieces that decide whether this is
|
||
product-ready.
|
||
|
||
1. **Done — Move from CLI-only to real app integration**
|
||
|
||
The GUI now has an explicit “Low disk install” action in the game detail
|
||
modal for remote-only games. The Tauri backend queues that path through
|
||
`stream_install_game`, injects the shared external `unrar` stream provider,
|
||
and hands fetched file details to `StreamInstallGame` instead of the normal
|
||
download command.
|
||
|
||
2. **Done — Replace per-file `unrar p` with a final archive provider**
|
||
|
||
The shared external `unrar` stream provider now runs `unrar lt` once for the
|
||
archive metadata and one sequential `unrar p` pass per archive for payload
|
||
bytes. It frames directories, file starts, file chunks, and file ends from
|
||
the technical listing, so CLI and GUI callers use one purpose-built provider
|
||
instead of a per-file extraction loop.
|
||
|
||
3. **Done — Handle solid archives deliberately**
|
||
|
||
The provider exposes the RAR `solid` flag in `ArchiveBegin` and always uses
|
||
one sequential payload pass per archive, which is the safe path for solid
|
||
archives. S41 now verifies a real solid RAR fixture through the Docker
|
||
peer-cli flow, including local-only final state, absent root archive/sentinel,
|
||
byte count, and extracted payload SHA-256 hashes.
|
||
|
||
4. **Done — Decide the integrity model**
|
||
|
||
Streamed installs intentionally verify against sender archive metadata for
|
||
now: each file must match the RAR-advertised size and CRC32. That catches
|
||
transport corruption, truncation, and provider bugs, but does not claim
|
||
malicious-peer protection. Trusted content remains a separate catalog schema
|
||
step: add catalog-owned archive or extracted-file SHA-256 hashes, then verify
|
||
those at the receiver before commit.
|
||
|
||
5. **Done — Upgrade retry/resume semantics**
|
||
|
||
Streamed install attempts now use the same majority-validated peer set as
|
||
normal downloads, and each failed attempt rolls back its staging transaction
|
||
before trying the next peer. S42 pins the policy: retry the whole stream from
|
||
another validated peer, keep no partial files across attempts, and do not add
|
||
byte-offset resume until there is a strong reason.
|
||
|
||
6. **Done — Expand scenario coverage**
|
||
|
||
S43-S47 cover the remaining streamed-install edges: already-installed
|
||
rejection, corrupt archive rollback, sender disconnect mid-stream, receiver
|
||
cancel mid-stream, and multi-archive `.eti` roots streamed in sorted order.
|
||
The peer-cli harness now exposes `cancel-download` so cancellation scenarios
|
||
exercise the same runtime path as the GUI.
|
||
|
||
7. **Clean product semantics**
|
||
|
||
Decide how the UI labels this state. It is installed but not downloaded, so
|
||
“Local only” is technically correct, but users may need a clear affordance
|
||
like “Installed, not shareable”.
|
||
|
||
My recommended next slice: make the provider abstraction final-ish, then
|
||
implement a real one-pass provider. Everything else builds cleanly on that.
|