Files
lanspread/organize/testing/PEER_CLI_SCENARIOS.md
T
ddidderr 4a1b08db98 test(peer-cli): verify protocol 8 transfer lifecycle
Replace legacy metadata and relay expectations with current protocol-8 JSONL
assertions. Scenarios now bind every source to authenticated PeerId and exact
ContentId, prove typed attempt lifecycle order, and fence cancellation,
quarantine, rollback, republishing, and peer-departure outcomes against vacuous
success.

Isolated topologies distinguish direct author pulls from relay and ambient mDNS
substitution. The run log records focused diagnostics and the final fresh-image
S1-S49 acceptance result without presenting Docker-host throughput as a
representative external-LAN measurement.

Test Plan:
- `LANSPREAD_S37_MIN_MIB_PER_S=100 just peer-cli-tests` -- passed S1-S49
- S37 -- passed 2,147,483,656 bytes in 17 chunks at 551.10 MiB/s
- `python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py` -- passed
- `ruff check --select F,E9 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py` -- passed
- `git diff --cached --check` -- passed
2026-08-10 14:00:03 +02:00

753 lines
97 KiB
Markdown

# Peer CLI P2P Scenarios
This matrix tracks the headless peer-to-peer contract exercised through
`lanspread-peer-cli`. It intentionally avoids the GUI and uses direct connect
for deterministic local runs; mDNS/macvlan remains an environment smoke path.
## Scenario Matrix
| ID | Scenario | Setup | Expected result |
| --- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| S1 | Startup scan | Start one peer with `fixture-alpha`. | Peer emits `local-peer-ready` and `local-library-changed`; catalog fixture games are `downloaded=true`, `installed=false`, `availability=Ready`. |
| S2 | Direct connect handshake | Start alpha and bravo, then connect each side to the other's authenticated peer endpoint. | Both peers record one remote peer, no self-peer entry appears, and each direction's raw `GameAvailability` plus `remote-library-view` carries the exact `ContentId` from that side's fixture manifest. |
| S3 | Remote aggregation | Empty client connects to alpha and bravo. | `list-games` shows remote-only games once; shared `ggoo` has `peer_count=2`, unique games have `peer_count=1`. |
| S4 | Single-source download, no install | Empty client connected to bravo downloads `bfbc2` with `install=false`. | The fixture manifest, raw `GameAvailability`, `remote-library-view`, and every verified `download-chunk-finished` agree on the exact `ContentId`; every chunk names bravo's authenticated `PeerId`. Download finishes with `downloaded=true`, `installed=false`; root files exist and `local/` does not. |
| S5 | Auto-install download | Empty client connected to bravo downloads `cnctw` with default install. | Download finishes, install begins and finishes, and local `cnctw` is `downloaded=true`, `installed=true` with `local/fixture-payload.txt`. |
| S6 | Manual install and uninstall | After S4, client sends `install bfbc2`, then `uninstall bfbc2`. | Install marks `bfbc2` installed and creates `local/`; uninstall removes `local/` while preserving downloaded root files. |
| S7 | Duplicate-source catalog download | Empty client connects to alpha and bravo, which both advertise the exact catalog-version `ggoo`, then downloads it. | The receiver uses its local catalog manifest as the sole descriptor/hash authority, completes exactly once with chunks from both eligible peers and no duplicate chunk, and the downloaded package matches the fixture bytes. |
| S8 | Catalog file-shape enforcement and failover | Two peers start with exact catalog-version `ggoo`; after their authenticated IDs are known, the lower-`PeerId` source's `.eti` is made oversized. A fresh second receiver then connects only to the oversized source. | Exact catalog-file admission rejects the oversized archive assigned to the first authenticated source, and only the honest source supplies a successful archive chunk; an exact `version.ini` may validly come from either peer. The successful attempt has no invalid-data warning for this admission/transport rejection. The oversized-only receiver clears verification, emits `download-failed` with reason `verified-catalog-sources-exhausted`, and publishes neither `version.ini` nor `local/`. |
| S9 | Known catalog game with no source | An empty client asks for catalog-known `cod6` while connected to no peers. | The command acknowledges queueing, then emits an attempt-keyed `download-failed` with reason `verified-catalog-sources-exhausted`, without `download-begin` or verification activity. The game never enters active operations and no game root, `version.ini`, or `local/` is created. |
| S10 | Shutdown liveness cleanup | Alpha and bravo are connected, then bravo shuts down. | Within the named 125-second liveness allowance, pinned-liveness failure removes bravo and Alpha converges to an exactly empty authenticated-peer set and remote-library view. The protocol has no explicit departure control message. |
| S11 | Same identity reconnect | Bravo shuts down; Alpha first converges to an exactly empty peer set and remote-library view through bounded liveness, then Bravo restarts with the same state dir and reconnects. The OS-assigned listener port may or may not differ. | Alpha has exactly one bravo peer entry reusing the same peer ID, not a duplicate identity, and sees Bravo's library again at the newly authenticated endpoint generation. |
| S12 | Transfer serving gates | A peer has a non-catalog, missing-sentinel, active-operation, or `local/` path request. | The serving peer declines the transfer request; covered by unit tests where timing is too small for a stable CLI race test. |
| S13 | Exact transferred-file equality | Repeat small and large downloads, then compare every transferred regular file against its source with SHA-256 manifests. | Source and receiver manifests match exactly for each transferred file; no extra or missing files appear in the downloaded game root. |
| S14 | Large multi-peer chunked download | A source advertises a synthetic catalog game whose `.eti` is a sparse file of `4 * CHUNK_SIZE` (four 128 MiB chunks). A second peer downloads it, then a third peer downloads it from both peers. | The third peer's downloaded files match the source by SHA-256; `download-chunk-finished` shows the `.eti` split across exactly both peers, all four chunks accounted for, and the per-peer byte totals balanced within one `CHUNK_SIZE` (a fair 2+2 split; a 3+1 imbalance would trip the check). |
| S15 | Exact-content source selection | Peer A has a stale `version.ini` and publishes no availability. Peer B has a valid but different fixture catalog and `ContentId`. Peer C matches the client's exact catalog content. The client connects to all three and downloads with `install=false`. | Raw views distinguish B's wrong `ContentId` from C's expected `ContentId`; the client's catalog-joined `list-games` reports `peer_count=1`. Every verified chunk carries the expected `ContentId` and C's authenticated `PeerId`; A and B contribute no verified bytes. |
| S16 | Catalog-version fanout with stale peers present | Peer A has a stale version of a game. Peers B and C both advertise the catalog version with matching manifests; the `.eti` is inflated to `2 * CHUNK_SIZE` so it can fan out. | The aggregated row counts only catalog-version ready peers. The `.eti` chunks split across exactly B and C; peer A is not listed as downloadable and contributes no manifest vote or file chunks. |
| S17 | Catalog-byte verification and source quarantine | Two peers start with exact catalog-version `cnc4`; after their authenticated IDs are known, the lower-`PeerId` source's `.eti` receives a same-length byte corruption. A fresh second receiver then connects only to the corrupt source. | The first receiver detects the BLAKE3 mismatch, quarantines the corrupt source for that content ID, emits exactly one invalid-source retry activity after selecting the honest alternate, and only the honest source supplies a successful archive chunk; an exact `version.ini` may validly come from either peer. The all-bad receiver emits no false retry warning, clears verification, emits `download-failed` with reason `verified-catalog-sources-exhausted`, and publishes neither `version.ini` nor `local/`. |
| S18 | Mid-download source drop with redundancy | Client confirms two authenticated peers advertise the exact catalog `ContentId`, orders them by authenticated `PeerId`, then downloads a sparse `16 * CHUNK_SIZE` archive. Once the lower-ID source has four active outbound streams, the harness confirms no terminal event has occurred and force-kills it. | The download survives the active source failure without an invalid-data warning: no `download-failed` occurs, every verified chunk names the exact `ContentId` and one of the two authenticated `PeerId`s, all bytes arrive, and the receiver matches the surviving source. The surviving peer must complete the final even-offset chunk originally assigned to the lower-ID peer, proving retry rather than merely completion of its own initial plan. |
| S19 | Mid-download sole-source drop | Client downloads a sparse `8 * CHUNK_SIZE` archive from one source. Once four outbound streams are positively active and no terminal event has occurred, the source is force-killed. | The interrupted transport emits exactly one attempt-keyed `download-failed` with reason `verified-catalog-sources-exhausted`, after verification activity clears, and no `download-finished`; no invalid-data retry warning appears for a transport loss, no committed target `version.ini` or ready row remains, and the active operation drains so a retry is possible. |
| S20 | Receiver write failure | Client downloads a large game into a constrained `/games` filesystem. | The attempt verifies received chunks, then emits exactly one `download-failed` with reason `operation-failed`; no committed `version.ini` is advertised, and active operation state clears so the peer can retry later. |
| S21 | Add-game propagation | Two connected peers are running; one peer gains a new catalog game root through a completed download or an external drop. | The other peer receives a library update without reconnecting, and `list-games` shows the new remote game under the existing peer. |
| S22 | Remove-game propagation | Two connected peers are running; one peer loses a previously advertised game root. | The other peer receives a library update without dropping the peer, and `list-games` no longer shows that remote game. |
| S23 | Version bump propagation | Two connected peers are running; one peer's ready game root starts with a stale `version.ini`, then changes to the catalog version. | The other peer receives a library update without reconnecting; the stale row is absent before the change, then the catalog-version game appears as downloadable. |
| S24 | Two clients pull from one source | Two empty clients connect to the same source and download the same large game concurrently. | Both downloads finish, both receivers match the source by diff or SHA-256, and the source remains responsive. |
| S25 | One client downloads two games concurrently | One client connected to a source issues two different `download` commands without waiting for the first to finish. | Both operations may run in parallel; both eventually finish, each game reaches the requested install state, and each transferred root matches its source. |
| S26 | Same-game duplicate download rejection | A client starts downloading a game, then issues a second `download` command for the same game while the first operation is active. | The second request is rejected deterministically as an operation-in-progress condition; the first download is not corrupted and still reaches its documented final state. |
| S27 | Self-connect rejection | A peer sends `connect` with its own peer ID and advertised listener address. | The CLI command fails cleanly before dialing, no self-peer entry is created, and the peer remains responsive. |
| S28 | Reconnect generation fencing | The same authenticated peer endpoint is committed again, creating a newer endpoint generation, while a stale removal token from the earlier generation remains outstanding. | The newer generation is strictly greater, the stale removal loses authority, and the current authenticated endpoint remains. This is covered by the deterministic unit test `reconnect_gets_new_generation_and_stale_removal_loses_authority`. |
| S29 | Empty-library peer participates | An observer and an empty peer share the ordinary network. The source starts only on a scenario-internal network; the empty peer is attached there and dials it directly, while the observer remains unable to authenticate the source. | The observer first sees exactly the empty peer with zero games. After that peer downloads `alienswarm`, the observer remains authenticated only to it and receives its exact `ContentId` with `peer_count=1`; the downloader's verified chunks name the isolated source's authenticated `PeerId`. This proves local republishing without transitive library relay. |
| S30 | 5+ peer mesh aggregation | Five peers advertise partially overlapping catalog games with a mix of unique and shared catalog-version games; a sixth client connects to all five. | The client shows one row per game ID, correct catalog-version ready-source `peer_count`, catalog `eti_game_version`, no duplicates, and no self entries. |
| S31 | Bootstrapped peer becomes source in same session | An empty client downloads exact catalog content from one authenticated source, verifies every chunk's `ContentId` and source `PeerId`, then the original source is killed and a fresh third peer connects only to the bootstrapped client. | The third peer's exact authenticated peer set is only the bootstrap, its remote view reports the exact content with `peer_count=1`, every verified chunk names the bootstrap's `PeerId`, and its files match the original source by diff or SHA-256. This proves downloaded files become servable without restart. |
| S32 | Reinstall after uninstall | A downloaded game is installed, uninstalled, then installed again without another download. | `local/` is recreated from preserved root files, no transfer events occur during reinstall, and the game returns to `installed=true`. |
| S33 | Install after external root mutation | A downloaded game root is externally mutated before `install` is issued. | The CLI fixture installer installs from the current root bytes. The resulting `local/fixture-payload.txt` must match the mutated archive bytes exactly. |
| S34 | Many-small-files game without `.eti` | A catalog game root contains `version.ini` plus many small regular files and no archive. | Download with `install=false` transfers every file, chunk events are coherent for small files, and source/receiver manifests match exactly. |
| S35 | Unknown game ID from remote peer | A remote peer's own catalog advertises a game ID and exact `ContentId` absent from the receiver's catalog. | The raw peer snapshot and `remote-library-view` retain the typed availability, while the receiver's catalog-joined `list-games` omits it. Download fails deterministically and creates no local files. |
| S36 | Exact-content singleton beats stale majority | Five peers contain one game; one peer matches the client's catalog `ContentId` and four have stale versions that publish no transferable availability. | Raw snapshots omit the stale roots, `remote-library-view` reports the exact `ContentId` once, and catalog-joined `list-games` has `peer_count=1`. Every verified chunk names that content and the singleton peer's authenticated `PeerId`. |
| S37 | Single-source download throughput | A source peer advertises a Rust-published temporary catalog profile with one sparse `2 GiB` `.eti`; an empty client downloads it with `install=false`. | The exact `ContentId` agrees across the raw snapshot, remote view, and all 17 authenticated verified-chunk events. The attempt emits begin, verification, clear, and exactly one keyed `download-finished` with no failure. The only canonical root-relative paths are sixteen `bf1942.eti` chunks with exact 128 MiB lengths and offsets plus one eight-byte `version.ini` chunk at offset zero. Local state is downloaded, ready, not installed, and drained. The complete receiver root matches the source by SHA-256/diff. Throughput reports internally consistent byte, chunk, duration, MiB/s, and Mbit/s measurements, with `LANSPREAD_S37_MIN_MIB_PER_S` defaulting to the 100 MiB/s normal-LAN gate. |
| S38 | First-play launch-setting stamping | `fixture-persona/css` ships a real RAR `.eti` whose tree buries a CRLF `SmartSteamEmu.ini` with a stub `PersonaName` line under `engine/bin/win64/steam_settings/`, plus a stub `account_name.txt` and `language.txt` under `profiles/local/`. A peer installs `css` (with `--unrar`), then sends `play css` with a username and language, then `play css` again. | Before first play, the marker is absent and the installed bytes are exactly `stubaccount`, `english`, and the four-line CRLF INI with `PersonaName = stubplayer`. The first `play` returns all three write outcomes, stamps the requested values while preserving INI siblings/CRLF, and creates the marker. A second `play` returns `already_applied=true`, rewrites nothing, and leaves externally reset files untouched. |
| S39 | Streamed install without keeping archive payload | Source and empty client run alone on a scenario-internal network. The source has real RAR `.eti` payload entries under `bin/` and `data/`; the receiver uses the container-bundled `unrar` provider and sends `stream-install cnctw`. | One exact event window is `Active Downloading` → attempt-keyed `download-begin``verifying-downloaded-chunks` → two authoritative verified files (`bin` then `data`, exact path/size/offset, catalog `ContentId`, source `PeerId`/address) → activity clear → `Active Installing` → matching `download-finished``Active empty``install-finished`, with no failure. Throughput is exactly 3 MiB/two files. Final state is local-only installed with no root sentinel/archive; payload SHA-256 matches `unrar p`, and the source transfer drains. |
| S40 | Streamed install receiver is not a peer source | After a streamed install, the original source shuts down. An observer starts alone on a fresh internal network, proves an empty roster, then the receiver is attached and dialled at its internal address. | The observer roster becomes and remains exactly the receiver. Its authoritative raw snapshot, post-connect remote view, and catalog join omit local-only `cnctw`. An ordinary download acknowledges queueing, then emits attempt-keyed `download-failed` with reason `verified-catalog-sources-exhausted` and no begin or verification activity, success, active-operation snapshot containing `cnctw`, or game-root mutation. |
| S41 | Solid archive streamed install | Source and client run alone on a scenario-internal network using the solid catalog profile; the named source's `.eti` is verified as a real solid RAR before transfer. | The client roster/raw view carry the exact solid-profile `ContentId`, and every verified file is bound to the named source's authenticated `PeerId`. Streamed install finishes local-only with no root archive/sentinel; byte count equals the extracted entries and payload SHA-256 matches `unrar p`. |
| S42 | Streamed install integrity quarantine and retry | On an internal network, a lower-`PeerId` source serves a valid solid archive whose output mismatches the default catalog; a higher-`PeerId` source serves the exact default package. Both advertise the same exact catalog `ContentId`. | First install observes admission/drain edges on bad then good, emits one invalid-source retry activity only after rollback and alternate selection, and only good supplies authoritative verified files. After uninstall, the same client runtime retains both peers/raw availabilities; a second install observes good admission/drain, zero bad edges, and no invalid-source warning, proving runtime quarantine skip. Both keyed attempts finish with exact 3 MiB, good-source `ContentId`/`PeerId`, hashes, local-only state, and no staging/backup/sentinel/archive residue. |
| S43 | Already-installed streamed install rejection | A client first stream-installs `cnctw`, then attempts `stream-install cnctw` again. | The second attempt emits `download-failed` with reason `operation-failed` and no begin or verification activity, does not emit a new success event, leaves the existing local-only install intact, and clears active operations. |
| S44 | All-bad streamed integrity rollback | Named source and client run alone on an internal network. The source advertises exact default content but serves a verified-solid `cnctw` archive whose extracted output mismatches that catalog. | Exact roster/raw `ContentId` plus source admission/drain edges prove the named bad source was attempted. Catalog verification clears without falsely promising another source, then emits `download-failed` with reason `verified-catalog-sources-exhausted`; no download/install success occurs, active state drains, and no `local/`, staging, archive, or sentinel remains. |
| S45 | Sender disconnect during streamed install | Source and client run alone on an internal network for exact catalog `alienswarm`; after a verified chunk bound to that source `PeerId`/`ContentId`, the source is killed. | All observed chunks remain exact-source authoritative. The transport interruption clears verification and promptly emits `download-failed` with reason `verified-catalog-sources-exhausted`, without an invalid-data warning, before stale-peer liveness can elapse; no success occurs, active state drains, and local/staging state rolls back. |
| S46 | Receiver cancel during streamed install | Source and client run alone on an internal network for exact catalog `alienswarm`; after a verified chunk bound to that source `PeerId`/`ContentId`, the receiver sends `cancel-download`. | All observed chunks remain exact-source authoritative. Cancellation clears verification, drains active state, and rolls back local/staging state; graceful client shutdown then fences the completed JSONL slice, which contains no download/install success or user-visible download failure. |
| S47 | Multi-archive streamed install order | Source and client run alone on an internal network using `fixture-multi/cnctw`, with two root `.eti` archives named to require sorted processing. | The client roster/raw view carry the exact multi-profile `ContentId`, and every verified file is bound to the named source's authenticated `PeerId`. Paths arrive in root archive sort order, both payloads install under `local/`, final state is local-only installed, and no root archive/sentinel is committed. |
| S48 | Call to Play direct-author late join | Alice creates a call; Bob publishes RSVP/chat intents against its generated `CallId`. Alice and Bob remain connected while Charlie starts on a separate internal-only Docker network shared with Alice; Bob joins it only after Charlie's first pull. | Alice retains the exact Bob-inclusive view before and after Charlie's first pull, while Charlie's authenticated peer set is exactly Alice and its replacement contains only Alice's generated event ID. After the direct Bob pull it contains the exact three-event union once. Bob's departure converges to exactly Alice plus the creator event; Alice's departure then converges to an empty peer set and view. |
| S49 | Terminal Call to Play direct-author late join | Alice creates and later starts a call; Bob publishes ready/chat intents. Alice and Bob remain connected while Charlie first pulls Alice across an internal-only Docker network, then Bob joins that network for a direct pull. | Alice retains the exact four-event view throughout the first pull; Charlie is authenticated only to Alice and sees create/start without Bob-owned IDs. Pulling Bob produces the exact four-event terminal view once; creator departure removes the complete call after bounded liveness convergence while Charlie's authenticated peer set remains exactly Bob. |
## Version-Skew Contract
Use S15-S17 to pin down what happens when several peers have the same game ID
but only some match the receiver's exact catalog content:
- The receiver's catalog is authoritative. A remote root whose `version.ini`
does not match the catalog's expected version for that game ID is not
downloadable.
- `list-games` joins raw remote availability against the local manifest. The
game appears once; `peer_count` counts only ready peers with the exact local
`ContentId`.
- The aggregated `eti_game_version` must be the catalog version.
- File paths, sizes, chunk boundaries, and BLAKE3 values come exclusively from
the receiver's bundled catalog. There is no remote descriptor preflight. Stale
or wrong-`ContentId` peers must not supply verified chunks.
- If exactly one peer has the catalog version, that peer is the only transfer
source. If several peers match, chunk fanout uses that set; byte mismatches
quarantine the exact source/content pair and retry elsewhere.
- Capture proof by matching the fixture manifest `ContentId` to raw
`GameAvailability`, `remote-library-view`, and every verified
`download-chunk-finished` event. Chunk proof uses authenticated `PeerId`; the
socket address is diagnostic only.
## Extended Failure And Mutation Contracts
Use S18-S36 to pin down operational behavior that is awkward to prove with the
GUI:
- A failed download must not commit the root `version.ini` sentinel. Partial
payload files may remain, but they must not be advertised as a ready local
game and must not leave an active operation stuck.
- Source failure during a redundant download should retry failed chunks against
another validated source for the same catalog-version file.
- Live local library changes are observable through revision hints followed by
authoritative full snapshots; reconnect is not required for add, remove, or
version-bump cases.
- Same-game operations are single-flight. A duplicate download request while a
game is already active is rejected instead of starting another writer.
- Unknown remote game IDs are filtered by the receiver's current catalog and are
not downloadable.
For a manual run, prefer a catalog game ID already served by the fixture lab,
such as `cnc4`, then create temporary `just peer-cli-run` game roots where some
peers match the catalog version and others deliberately use stale `version.ini`
contents. The existing alpha/bravo/charlie fixtures cover duplicate-source and
shared-game cases; S15-S17 add the focused skew cases.
## First-Play Launch-Setting Contract
Use S38 to pin down how launcher settings are stamped into an installed game:
- Stamping happens on the first `play`, not during install/update. The install
transaction only clears the `games/<id>/launch_settings_applied` marker so the
next play reapplies settings to a freshly (re)created `local/`.
- The first play stamps the username into the first `account_name.txt` and the
first `SmartSteamEmu.ini` `PersonaName` line, and the language into the first
`language.txt`, searching the whole `local/` tree. The matched `PersonaName`
line keeps its existing line ending (`\n` or `\r\n`).
- The marker records only that we _tried_: it is written unconditionally after
the first play, so a game with none of these files is still marked done.
- S38 needs a real archive expanded with `--unrar`; the Docker matrix image now
carries the Linux sidecar for streamed-install coverage, while the peer
crate's `launch_settings` unit tests cover the rewrite, line-ending, and
marker logic deterministically.
## Streamed Install Archive Contract
Use S39-S47 to pin down low-disk streamed installs:
- The stream provider performs one archive metadata pass and one payload pass
per `.eti`, then frames entry boundaries for the receiver.
- Non-solid and solid archives both install into `local/` without committing a
root archive or root `version.ini`, so the receiver is installed but not a
downloadable source.
- RAR size/CRC32 checks are only an early sender-consistency check. The security
boundary is the receiver's catalog-owned extracted path set, sizes, and BLAKE3
file hashes. Scenario SHA-256 comparisons independently demonstrate that the
installed bytes match the intended fixture.
- S41 verifies the fixture is actually solid inside the source container, so
solid handling stays covered by the same Docker harness as the existing
streamed-install scenarios.
- S42 verifies integrity quarantine and retry: a valid archive with extracted
output from the wrong catalog profile rolls back its staging directory, then
the receiver retries the whole stream from an honest matching source. There is
no byte-offset resume contract.
- S43-S47 cover the remaining streamed-install failure and archive-shape edges:
already-installed rejection, all-bad catalog mismatch rollback, sender
disconnect, receiver cancel, and multi-archive root sorting.
## Run Log
### 2026-08-10 - Phase 5 Unfiltered Acceptance
- A fresh-image `LANSPREAD_S37_MIN_MIB_PER_S=100 just peer-cli-tests` run passed
every scenario from S1 through S49. S37 transferred the exact `2,147,483,656`
bytes in 17 verified chunks over `3.716s`, reporting `551.10 MiB/s` and
`4622.93 Mbit/s` against the active 100 MiB/s floor. These are Docker
acceptance measurements on this host and storage, not a representative
external-LAN performance claim. The run included the attempt-keyed
verification/retry/exhaustion traces, authenticated exact-content sources,
structured cancellation and rollback, streamed-install lifecycle, same-runtime
quarantine skip, direct-author Call to Play replacement, and non-vacuous
bounded peer-departure proofs.
- On the final implementation snapshot, `just test` passed all 708 workspace
tests (including 480 peer and 56 Tauri tests), `just clippy` passed,
`just frontend-test` passed 91/91, and `just build` passed the fixture catalog
checks plus the Deno/Vite and release-mode Tauri build. `just build` uses
fixture catalogs and is not the production corpus/bundle gate.
### 2026-08-10 - Phase 5 Typed Transfer Focused Acceptance
- This fresh-image command passed all fourteen scenarios:
```sh
LANSPREAD_S37_MIN_MIB_PER_S=100 just peer-cli-tests \
S8 S9 S17 S18 S19 S20 S37 S39 S40 S42 S43 S44 S45 S46
```
Their operation-local JSONL windows proved one canonical decimal attempt ID,
exact begin/verification/retry/clear/terminal milestone order, no status after
settlement, typed exhaustion versus local operation failure, no invalid-data
warning for transport loss, and silent user cancellation. S42 emitted the
invalid-source warning only for its first bad-to-good rollback; the same
runtime's quarantined-source retry emitted none. S37 transferred
`2,147,483,656` bytes in 17 verified chunks over `3.679s`, reporting
`556.69 MiB/s` and `4669.84 Mbit/s` against the active 100 MiB/s
Docker-on-this-host acceptance floor. This focused run is retained as targeted
evidence; the refreshed unfiltered S1-S49 result is recorded above.
### 2026-08-10 - Phase 4 v8 Unfiltered Acceptance
- A fresh-image `LANSPREAD_S37_MIN_MIB_PER_S=100 just peer-cli-tests` run passed
every scenario from S1 through S49. S37 transferred the exact `2,147,483,656`
bytes in 17 verified chunks over `6.999s`, reporting `292.61 MiB/s` and
`2454.61 Mbit/s` against the active 100 MiB/s floor. These are Docker
acceptance measurements on this host and storage, not an external LAN
performance claim. The same unfiltered run exercised the strengthened
exact-byte, lifecycle, topology, authenticated-source, quarantine, rollback,
and cancellation proofs in S38-S47, plus the direct-author Call to Play and
bounded-departure proofs in S48-S49.
### 2026-08-10 - Phase 4 v8 Focused Surface Acceptance
- A fresh-image `just peer-cli-tests S48` run passed the hardened direct-author
topology. Alice retained the exact Bob-inclusive view while Charlie's
authenticated peer set was exactly Alice and Charlie saw only Alice's creator
event. After Bob joined Charlie's network, Charlie pulled the exact union
directly; bounded liveness convergence then removed Bob's participant slice
and finally Alice's complete call.
- The unchanged-image command `just peer-cli-tests S12 S42 S48 S49` then passed
all four focused scenarios. S12 ran the current v8 transfer-admission unit
suite. S42 assigned the lower authenticated `PeerId` to the catalog-mismatched
source, quarantined it, and completed all `3,145,728` bytes from the exact
honest source. S49 reconstructed terminal creator and participant slices by
direct pulls and removed the complete call when the creator became stale,
while Charlie's authenticated peer set remained exactly Bob.
- A later fresh-image `just peer-cli-tests S18 S19 S31` run passed the hardened
source-failure cases. S18 observed four active outbound streams on the
lower-`PeerId` source before force-killing it; the survivor then supplied all
`2,147,483,648` archive bytes, including the final even-offset chunk
originally assigned to the killed peer, with no failure event and an exact
directory diff. S19 force-killed its sole source with four active streams and
reached `download-failed` with no ready row, sentinel, or active operation.
S31 proved exact `ContentId` and authenticated-source attribution on both the
original download and the bootstrapped peer's same-session re-serve before
matching the original fixture by diff.
- A subsequent fresh-image `just peer-cli-tests S29 S30 S31` run passed the
isolated bootstrap topology. S29 kept its source on an internal network and
attached only the empty peer; the observer remained authenticated exactly to
that formerly-empty peer before and after its download, then saw its exact
`alienswarm` `ContentId` with `peer_count=1`. S30 retained the expected six
aggregate game rows and per-content source counts across five peers. S31
proved an exact bootstrap-only peer set and `peer_count=1`, with each chunk on
the second hop attributed to that bootstrap before the directory diff matched.
- With `LANSPREAD_S37_MIN_MIB_PER_S=100`, the existing frozen image then passed
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S37`. The
exact-content single-source download transferred `2,147,483,656` bytes in 17
verified chunks over `10.138s`, reporting `202.02 MiB/s` and `1694.64 Mbit/s`.
All events carried the catalog `ContentId` and authenticated source `PeerId`;
their canonical root-relative paths comprised sixteen exact `bf1942.eti`
offsets plus `version.ini`, and the complete receiver directory matched the
source by SHA-256/diff. The configured 100 MiB/s floor was active.
- The same frozen image then passed
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S40 S45`.
S40 shut down the original source before starting the observer; the observer
authenticated exactly the local-only receiver, whose raw snapshot and
post-connect remote view both omitted `cnctw`, then observed the queued
ordinary download fail asynchronously without a game-root mutation. S45 killed
its sender after the first verified `alienswarm` stream chunk and observed
prompt `download-failed`, no success, drained active state, and complete
local/staging rollback before stale-peer liveness could elapse.
- The frozen image then passed the Python scenario runner for
`S38 S39 S40 S41 S42 S44 S45 S46 S47`. S38 proved the exact account, language,
and CRLF INI stub bytes before first play. S39 proved the complete ordered
streamed-install lifecycle, including both exact verified chunks (`3,145,728`
bytes total) from the isolated named source. S40 proved an isolated
empty-to-receiver topology and an asynchronous no-source failure with no
active-operation event or local mutation. S41 streamed the named solid source
and reproduced both expected hashes over 118 bytes. On S42's first install,
the mismatched and honest sources each emitted exactly two admission/drain
edges; after uninstall, the same receiver runtime emitted zero further edges
for the quarantined source and exactly two for the honest source while
reproducing the exact `3,145,728` bytes and hashes. S44 proved the sole bad
source was admitted and drained before rollback. S45 bound its first verified
chunk and prompt sender-loss failure to the exact authenticated source and
`ContentId`. S46 used a strict graceful event-loop drain to prove receiver
cancellation emitted no terminal success or failure. S47 bound the sorted
multi-archive install to its isolated named source.
- This was a focused post-cutover acceptance run, not a new unfiltered S1-S49
run. The full run below predates the final v8 Call to Play harness and compact
catalog-index cutovers and is retained only as historical baseline evidence.
### 2026-08-10 - Earlier Catalog Authority S1-S49 Baseline
- Built a fresh `lanspread-peer-cli:dev` image after the streamed-install
durability changes. The focused acceptance command
`LANSPREAD_S37_MIN_MIB_PER_S=100 just peer-cli-tests S8 S17 S37 S42 S44`
passed all five scenarios. S8 and S17 proved ordinary-download failover and
all-bad rollback for oversized and same-length-corrupt catalog content. S42
and S44 proved streamed-install quarantine/retry and all-bad rollback for a
valid archive whose extracted output mismatched the local catalog authority.
- The exact unfiltered command
`LANSPREAD_S37_MIN_MIB_PER_S=100 just peer-cli-tests` then passed S1-S49. This
includes the source-only unknown-game profile in S35, the solid profile in
S41, the multi-archive profile in S47, and the runtime-published temporary
profiles used by the large-file scenarios. These catalogs and manifests are
generated by the Rust fixture publisher; the peer does not derive authority
from runtime package contents.
- Fresh post-catalog-hash S37 evidence from the unfiltered run: a `2.00 GiB`
single-source download completed in `3.651s` at `560.93 MiB/s`
(`4705.43 Mbit/s`) and emitted exactly 17 verified chunks: sixteen 128 MiB
archive chunks plus `version.ini`. Environment: host `pfs-arch`, Linux
`7.1.6-arch1-1` x86_64, Docker network `lanspread`, dynamic profile
`dynamic-s37`, storage path `.lanspread-peer-cli/extended-scenarios` on
`/dev/dm-2` (`btrfs`), date `2026-08-10`. The 100 MiB/s normal-LAN acceptance
floor was active.
- The repository still intentionally contains no fabricated 186-game production
manifest corpus. The production bundle gate therefore remains fail-closed
until the publisher is run against the real package source.
### 2026-07-21 - Call to Play Transport (S48)
- Added JSONL commands to publish local Call to Play intents and inspect full
replacement views.
- S2 passed against the rebuilt image, preserving bidirectional library exchange
after the protocol version bump.
- The Phase 4 scenario now captures core-generated receipt IDs and proves that a
late third peer sees only Alice's author slice until it pulls Bob directly.
### 2026-06-21 - Test-Suite Integrity Audit And Hardening
- An adversarial review of `run_extended_scenarios.py` found assertions that
passed vacuously, raced, or diverged from the spec. A full baseline run
(S1-S47, rebuilt image) passed beforehand, confirming these were test-quality
gaps, not peer regressions. Baseline evidence of the gaps: S14 chunk totals
were `{134217728, 1048576}` (a 2-chunk file whose "balanced within one chunk"
check can never fail), and S16/S18 each served the whole ~120 MiB
`alienswarm.eti` from a single source, so neither fanout (S16) nor
retry-onto-survivor (S18) was actually exercised.
- Fixes applied to the runner (and the matching rows above):
- S18: replaced the dead `assert_no_event` (it reused a `LineWaiter` already
advanced past `download-finished`, so it scanned an empty tail and could
never fire) with `assert_no_event_since` over the whole download window;
switched to a multi-chunk sparse archive (`4 * CHUNK_SIZE`) so both peers
own `.eti` chunks and the test proves the download survives a mid-download
source kill (retry-onto-survivor is the mechanism, exercised when the kill
interrupts an unfinished chunk, but not asserted since the race can't be
forced).
- S7: added chunk-source, both-sources-served, single-`download-finished`, and
no-duplicate-chunk checks (the byte-identical `ggoo` fixtures made the old
diff-only assertion source-agnostic).
- S14: `4 * CHUNK_SIZE` file so the balance check is meaningful (a 3+1 split
would now exceed one chunk); asserts an exact 2+2 split and full byte total.
- S16: inflated `.eti` to `2 * CHUNK_SIZE` so it fans out across both
catalog-version peers (the stock 120 MiB fixture is a single chunk).
- S19: force-kill right after `download-begin` on a multi-chunk file, require
the typed `download-failed` source-exhaustion terminal, assert no
`download-finished` (the old graceful shutdown could let a single-chunk
transfer finish first).
- S26: large sparse source so the first op is reliably still active, and
asserts the active `operation == "Downloading"` (no scenario checked it).
- S37: validates the throughput rate fields (positive, self-consistent
`mbit_per_s/mib_per_s == 8.388608`, `mib_per_s == bytes/duration`), not just
the byte count.
- S35: asserts the source actually advertises `cod2` with its typed
`ContentId` before checking the receiver's local catalog filters it.
- S15: cross-checks raw typed availability for the wrong and expected
`ContentId` values; the catalog-joined list counts only the expected one.
- S2: polls for library convergence and verifies the bidirectional exchange
(bravo sees alpha's 3 games, not just alpha seeing bravo's 4).
- S11: dropped the "listener address must change" assertion (it tested the OS
ephemeral-port allocator and could fail spuriously).
- S12/S28: require the gating unit test to appear as `<name> ... ok` so an
`#[ignore]`d (un-run) test no longer satisfies the check.
- S24/S25: assert the requested `install=false` final state.
- S34: assert exactly 21 coherent chunks (20 files + version.ini), 21 distinct
paths, no duplicates, instead of a `>= 21` floor.
- Historical S27 note: protocol v7 added the
`handshake::tests::inbound_hello_from_self_is_ignored` test for its Hello
self guard. That test and handshake shape were removed by v8; the current
CLI scenario independently proves typed self-connect rejection and this old
test name is not current acceptance evidence.
- Harness: `find_fixture_game` now iterates `sorted(...)`, so the ambiguous
`cnctw` (bravo/multi/solid) resolves deterministically to `fixture-bravo`.
- Accepted as-is (reviewed, deliberately not changed): S20 (disk-full via chunk
`write_all` is equivalent coverage), S21 (inotify across the bind mount is
inherent to the harness), S30 (dup-row/self-peer checks are cheap defensive
guards), and S32/S39/S44 absence checks (cheap regression guards against
committing a root sentinel). S45 remained unchanged at this historical
checkpoint; current acceptance requires the prompt transport `download-failed`
rather than a later stale-peer event. S42 now persists two honest installation
identities first, assigns the mismatching profile to the lower decoded
`PeerId`, and proves that only the higher-ID honest source emits verified
chunks after retry/quarantine.
- Live runs against the rebuilt `lanspread-peer-cli:dev` image: baseline S1-S47
passed; post-fix S1-S47 passed. Post-fix evidence: S14
`{268435456, 268435456}` (balanced 2+2); S16 `.eti` split across B and C
`{134217728, 134217728}`; S18 all `536870912` bytes delivered despite the
source drop (the survivor served the whole archive in that run); S19
deterministic `download-failed`; S37 `874.24 MiB/s`. Gates: `just test` (incl.
the new handshake test), `just clippy` (`-D warnings`), and `just fmt` all
passed.
### 2026-06-20 - Prune Dead Lifecycle Events
- Code under test removed the unconsumed `InstallGameBegin`,
`UninstallGameBegin`, and `RemoveDownloadedGameBegin` `PeerEvent` variants
(and their peer-cli JSONL
`install-begin`/`uninstall-begin`/`remove-download-begin` events), plus the
Tauri webview emits that no frontend listener consumed (`peer-local-ready`,
`game-download-begin`, `game-download-pre`, `game-download-finished`,
`game-uninstall-finished`,
`peer-connected`/`-disconnected`/`-discovered`/`-lost`). `peer-runtime-failed`
was kept pending a UI decision.
- Rationale: the GUI is state-as-source-of-truth (it renders the `games-list`
snapshot), and no scenario asserted these begin events; the install,
uninstall, and removal start transitions stay observable via
`active-operations-changed`.
- Contract update: the S39 row no longer lists `install-begin`. Older run-log
entries below predate the removal and are left intact as historical records.
- Gates: `just test`, `just clippy`, `just frontend-test`, and `just build`
passed. (`just fmt`'s `tombi` step needs network and was skipped; no TOML
changed.) The Docker S39-S47 matrix was not re-run for this cleanup; S39-S47
never asserted the removed begin events, so coverage is unchanged.
### 2026-06-07 - Catalog-Version Matrix Alignment (S1-S47)
- Code under test aligned checked-in fixture `version.ini` sentinels with the
catalog, made `run_extended_scenarios.py` stamp generated fixture games with
catalog versions by default, updated S15-S17/S23/S30/S36/S37 to assert
catalog-authoritative aggregation, and wired S38 into the executable matrix.
- Gates before Docker:
`python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed.
- The rebuilt-image Python runner passed S3, S8, S14-S17, S21-S24, S29-S31, S34,
S36-S37, and S39-S47 with `--build-image`.
- S38 standalone runner:
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S38`
passed, proving the real-RAR `css` fixture installs with the container
`/usr/local/bin/unrar` sidecar and stamps launch settings only once.
- Full matrix runner:
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py` passed
for S1-S47 against the rebuilt `lanspread-peer-cli:dev` image.
- The final full-run highlights included S3 aggregation, S15-S17 catalog-version
skew/fanout/conflict, S23 stale-to-catalog propagation, S30 mesh aggregation,
S36 catalog singleton over stale majority, S37 throughput, S38 first-play
stamping, and S39-S47 streamed-install coverage.
### 2026-06-07 - Streamed Install Edge Coverage (S43-S47)
- Code under test added `cancel-download` to `lanspread-peer-cli`, added the
tiny `fixture-multi/cnctw` two-archive fixture, and added S43-S47 in
`run_extended_scenarios.py`.
- Gates before Docker: `just fmt` and
`python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed.
- The Python runner passed S43-S47 with `--build-image` against the rebuilt
`lanspread-peer-cli:dev` image.
- S43 stream-installed `cnctw`, retried `stream-install cnctw`, observed
`download-failed`, and verified the existing local-only install stayed intact.
- S44 replaced the source `cnctw.eti` with invalid bytes. The receiver emitted
`download-failed`, cleared active operations, and left no `local/`,
`.local.installing`, root archive, or root `version.ini`.
- S45 killed the sole `alienswarm` source after the first streamed chunk. The
receiver ended with `download-failed`, emitted no success, cleared active
operations, and rolled back local/staging state.
- S46 cancelled `alienswarm` on the receiver after the first streamed chunk. The
receiver emitted no success and no user-visible `download-failed`, cleared
active operations, and rolled back local/staging state.
- S47 streamed `fixture-multi/cnctw` and observed chunk paths in sorted root
archive order: `cnctw/.local.installing/order/first.txt`, then
`cnctw/.local.installing/order/second.txt`.
### 2026-06-07 - Streamed Install Whole-Stream Retry (S42)
- Code under test added S42 in `run_extended_scenarios.py`.
- Gates before Docker:
`python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed.
- Runner:
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S42`
passed against the current `lanspread-peer-cli:dev` image.
- S42 started a broken source with `--unrar /missing-unrar` and a good source
with the same catalog-version `cnctw` metadata. The broken source sorted first
(`10.66.0.2:32897`) and the good source second (`10.66.0.3:34092`).
- The broken source contributed zero chunks; the good source completed the fresh
whole-stream attempt with `3145728` streamed file bytes.
- The final client state was `downloaded=false`, `installed=true`,
`availability=LocalOnly`, with no root `version.ini`, no root `cnctw.eti`, and
no `.local.installing` staging directory. Payload SHA-256 hashes matched the
good source's `unrar p` output.
### 2026-06-07 - Solid Streamed Install Coverage (S41)
- Code under test added `fixture-solid/cnctw`, a real solid RAR `.eti`, plus S41
in `run_extended_scenarios.py`.
- Gates before Docker: `just fmt`, `git diff --check`, and
`python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed.
- Runner:
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S41 --build-image`
passed against the rebuilt `lanspread-peer-cli:dev` image.
- S41 verified the source archive with `unrar lt -cfg-` inside the source
container; the archive reported `Details: RAR 5, solid`.
- The streamed install finished with `downloaded=false`, `installed=true`,
`availability=LocalOnly`, no root `version.ini`, and no root `cnctw.eti`.
- The client received `118` streamed file bytes, matching the extracted solid
entries. Payload SHA-256 hashes matched `unrar p` output:
`88764c9a6c9b5b846b4323cf7725cb7fd70766ddd7fba4168332804a839fa193`
(`bin/cnctw-solid-payload.bin`) and
`44afc308269b2381b7c707a056dd8d9d393274108ac4d880237fa6772c861d7a`
(`data/cnctw-solid-assets.dat`).
### 2026-06-07 - Streamed Install Prototype (S39-S40)
- Code under test added `stream-install` to `lanspread-peer-cli`, a peer
`StreamInstallGame` command, streamed install frames over QUIC, and an
injected `unrar lt`/`unrar p` provider for archive-derived bytes.
- Gates before Docker: `just fmt` and
`RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= just test` passed for the
workspace.
- Runner:
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S39 S40 --build-image`
passed against the rebuilt `lanspread-peer-cli:dev` image.
- S39 streamed a catalog-version-adjusted `cnctw` fixture from a real RAR `.eti`
into the receiver's `local/` only. The receiver had `downloaded=false`,
`installed=true`, `availability=LocalOnly`, no root `version.ini`, no root
`.eti`, and payload SHA-256 hashes
`82f4da22dc042166def2a5ee2eca19fc9e52785f99838e86c32167cb342e2588`
(`bin/cnctw-payload.bin`) and
`abf833a06c74ea9f17d505c2684186491898ce906405e0f098f0deac19476b06`
(`data/cnctw-assets.dat`) matching `unrar p`.
- S40 connected an observer only to that streamed-install receiver. The observer
saw the receiver's `cnctw` summary as local-only, remote aggregation hid it as
a downloadable source, and `download cnctw` failed with
`no peers have game cnctw`.
### 2026-05-28 - First-Play Launch-Setting Stamping (S38)
- Code under test moved the `account_name.txt`/`language.txt` overwrite out of
the install transaction and into a single first-play step (shared with the new
`SmartSteamEmu.ini` `PersonaName` rewrite) gated by the
`games/<id>/launch_settings_applied` marker.
- `just test` passed the whole workspace, including the new
`lanspread_peer::launch_settings` unit tests and
`install::transaction::tests::install_resets_launch_settings_marker`.
- S38 host run: built `crates/lanspread-peer-cli/fixtures/fixture-persona/css`
with a stored RAR `.eti` (verified by `unrar t`) burying a CRLF
`SmartSteamEmu.ini` plus stub `account_name.txt`/`language.txt`. A host peer
installed `css` with `--unrar /usr/bin/unrar`, then `play css` stamped the
username into the deep `PersonaName` line (CRLF preserved, sibling lines
intact) and `account_name.txt`, the language into `language.txt`, and created
the marker. A second `play css` returned `already_applied=true` and rewrote
nothing even after the value was reset externally.
### 2026-05-19 - Snapshot Status Fix Docker Matrix Pass
- Code under test included `5c4976d`
(`fix(peer): settle local state before clearing operations`) and `6651f02`
(`fix(ui): derive operation status from snapshots`).
- Gates before the matrix: `just fmt`, `just test`, `just frontend-test`, and
`just build` passed. The peer harness image was rebuilt with
`just peer-cli-image`.
- Runner: `python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed S1-S36 against the rebuilt `lanspread-peer-cli:dev` image.
- Auto-install coverage remained good: S5 downloaded and installed `cnctw`, saw
the fixture payload under `local/`, and the downloaded root diffed cleanly
against `fixture-bravo/cnctw` excluding local metadata.
- Large/exact transfer coverage remained good: S13 small and large downloads
diffed cleanly; S14 split `alienswarm` between two sources with chunk totals
`67,108,864` and `58,721,049` bytes and the final root diffed cleanly.
- Failure and mutation coverage remained good: S17 latest-version conflict, S19
sole-source drop, S20 write failure, S26 duplicate operation, and S35 unknown
catalog filtering all failed safely without advertising bad local state;
S21-S23 propagation, S24-S25 concurrency, S29-S31 bootstrapping, S32
reinstall, S33 mutation install, S34 many-small-files, and S36 latest
singleton all passed.
### 2026-05-18 - Full Automated Docker Matrix Pass
- Historical pre-v8 record: this section describes the then-current image and
does not establish current typed-identity, exact-`ContentId`, or v8 admission
behavior. See the 2026-08-10 focused acceptance entry for current evidence.
- Runner: `python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed S1-S36 against the then-current `lanspread-peer-cli:dev` image.
- S1-S17 rerun highlights: startup, direct connect, aggregation, download,
install/uninstall, duplicate-source, ambiguous metadata, missing game,
shutdown cleanup, identity reconnect, serve gates, exact equality, large
multi-peer chunking, and latest-version selection/conflict all passed. Exact
transfer scenarios used `diff -r`/SHA-256 manifest checks; S14 chunk totals
were `58,721,049` and `67,108,864` bytes, balanced within one `32 MiB` chunk.
- S18-S36 rerun highlights: source-drop, disk-full, live mutation, concurrency,
duplicate-operation rejection, self-connect rejection, empty-peer sourcing,
5-peer aggregation, bootstrapped sourcing, reinstall, external mutation,
many-small-files, unknown catalog filtering, and stale-majority/latest
singleton cases all passed. File-copy scenarios used diff/manifests or `cmp`
for the mutated install payload.
### 2026-05-18 - Extended Scenario Docker Pass
- Historical pre-v8 record: the scenario details below preserve the behavior
tested at that date and are not current v8 authority or admission evidence.
- Runner: `python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed for S18-S36 after rebuilding `lanspread-peer-cli:dev` with
`just peer-cli-image`.
- S18 redundant source drop: one `alienswarm` source was killed after
`download-begin`; the client emitted `download-finished`, no
`download-failed`, and `diff -r`/SHA-256 manifest comparison matched the
surviving source. Recorded large-file chunk bytes from the surviving source:
`58,721,049`.
- S19 sole-source drop: killing the only source after `download-begin` emitted
`download-failed`; the receiver had no committed `alienswarm/version.ini`, no
ready local row, and no active operation left.
- S20 receiver write failure: a client with `/games` constrained to a `32m`
tmpfs emitted `download-failed`; `/games/alienswarm/version.ini` was absent
inside the container and active operations were empty.
- S21-S23 live mutation propagation: a connected peer observed `cod5` added,
`cod5` removed, and `cnc4` bumped from `20250101` to `20260501` without
reconnecting or dropping the peer.
- S24-S25 concurrency: two clients downloaded `alienswarm` from one source at
the same time and both diffed cleanly; one client downloaded `bfbc2` and
`cnctw` concurrently and both roots diffed cleanly.
- S26 duplicate same-game download: the second `alienswarm` download command
returned `operation already in progress for game alienswarm`; the first
download still finished and diffed cleanly.
- S27 self-connect rejection: connecting a peer to its own listener returned
`cannot connect peer to itself ...`; `list-peers` stayed empty and the peer
stayed responsive.
- Historical S28 recorded the then-current address-change invariant; it does not
establish the current v8 proof. Current S28 uses
`peer_db::tests::reconnect_gets_new_generation_and_stale_removal_loses_authority`.
- S29 empty-library peer: an observer first saw the empty peer with zero games;
after that peer downloaded `alienswarm`, the downloaded root diffed cleanly
and the observer's peer snapshot for that same peer contained `alienswarm`.
- S30 5-peer aggregation: a sixth client connected to five peers and aggregated
six game IDs with expected `peer_count` and latest versions, with no duplicate
game rows and no self-peer entry.
- S31 bootstrapped source: after the original source was killed, a third peer
downloaded `alienswarm` from the bootstrapped client and diffed cleanly
against the original fixture.
- S32 reinstall: reinstall after uninstall recreated `local/`, reported
`installed=true`, and produced no transfer chunk events during reinstall.
- S33 external root mutation: after mutating the downloaded `bfbc2.eti` inside
the client container, `install` wrote `local/fixture-payload.txt` that matched
the mutated archive exactly by `cmp`.
- S34 many-small-files transfer: a `bf1942` fixture with 20 small regular files
and no `.eti` downloaded with `install=false`; 21 file chunks were observed
including `version.ini`, and the receiver diffed cleanly against the source.
- S35 unknown game ID: a source advertised `cod2`; the receiver filtered it from
`list-games`, rejected the download, and created no local files.
- S36 latest singleton: four stale-version roots and one then-current root were
present; the client selected the singleton current-version source and
completed the download. This historical result does not establish the later
exact-`ContentId` and authenticated-source contract.
### 2026-05-18 - Full Matrix Manual Docker Pass
- Historical pre-v8 record: command names, protocol behavior, and unit coverage
below belong to that snapshot. They are not evidence that current v8 tests ran
in May; current v8 admission proof is recorded in the 2026-08-10 entry.
- Build/setup: `just peer-cli-image` passed. Local `just peer-cli-build` needed
`RUSTC_WRAPPER=` because the host `kache` wrapper failed with a read-only
filesystem error; `RUSTC_WRAPPER= just peer-cli-build` passed.
- Temporary skew/conflict fixtures were created under the ignored
`.lanspread-peer-cli/full-fixtures/` tree using `rar a -idq -m0` against
`/dev/urandom` payloads and then renaming the archives to `.eti`.
`find .lanspread-peer-cli/full-fixtures -name '*.eti' -exec unrar t -idq {} \;`
passed.
- S1 startup scan: `just peer-cli-alpha` emitted `cli-started`,
`local-library-changed`, and `local-peer-ready`; `alienswarm`, `bf1942`, and
`ggoo` were `downloaded=true`, `installed=false`, `availability=Ready`.
- S2 clean direct connect: with only alpha and bravo running, alpha connected to
bravo at `10.66.0.3:42776`; `wait-peers` returned `peer_count=1`, and
`list-peers` showed exactly one bravo peer with four games.
- S3 clean remote aggregation: an empty `clean-s3-client` saw exactly alpha and
bravo. `list-games` showed `ggoo peer_count=2`; `alienswarm`, `bf1942`,
`bfbc2`, `cnc4`, and `cnctw` each had `peer_count=1`.
- S4 single-source no-install: `full-empty-client` downloaded `bfbc2` from bravo
with `install=false`. Events included `download-begin`, verified chunk
completions, `download-finished`, and local `installed=false`. Host
verification:
`diff -r crates/lanspread-peer-cli/fixtures/fixture-bravo/bfbc2 .lanspread-peer-cli/full-empty-client/games/bfbc2`
passed and `local/` was absent.
- S5 auto-install: `full-empty-client` downloaded `cnctw` with default install.
Events included download finish, `install-begin`, and `install-finished`;
`local/fixture-payload.txt` existed. Host verification diffed the downloaded
files against `fixture-bravo/cnctw` excluding `local/` and `.lanspread.json`.
- S6 manual install/uninstall: after S4, `install bfbc2` created `local/` and
marked `installed=true`; `uninstall bfbc2` removed `local/` and preserved the
downloaded root files. Host verification diffed the preserved files against
`fixture-bravo/bfbc2` excluding `.lanspread.json`.
- S7 duplicate-source download: `full-empty-client` downloaded shared `ggoo`
from alpha/bravo with `install=false`. Chunk events used alpha for
`version.ini` and bravo for `ggoo.eti`; host `diff -r` matched both
`fixture-alpha/ggoo` and `fixture-bravo/ggoo`.
- S8 ambiguous metadata rejection: `full-s8-a` and `full-s8-b` both advertised
`ggoo` version `20260101` but with different `.eti` sizes (`1,048,746` and
`2,097,323` bytes). The client saw `peer_count=2`, then `download ggoo`
emitted `download-failed`; no target `ggoo/version.ini` was committed.
- S9 missing game: `download does-not-exist` emitted `no-peers-have-game` and
returned a command error; `.lanspread-peer-cli/full-empty-client/games` had no
`does-not-exist` directory.
- S10 shutdown cleanup: alpha saw bravo before shutdown with one remote peer and
bravo-only remote games. After bravo `shutdown`, alpha emitted `peer-lost`;
`list-peers` returned `[]` and `list-games` returned an empty remote list.
- S11 same identity reconnect: restarting bravo reused peer ID
`019e347d901e70c19adf5b9fd313fce4` at new address `10.66.0.3:41764`. Alpha
`list-peers` showed exactly one bravo entry at the new address.
- S12 transfer serving gates: this remains covered by unit tests because the CLI
cannot stably race raw transfer requests against non-catalog, missing
sentinel, active-operation, and `local/` path states.
`RUSTC_WRAPPER= just test` passed its then-current serve-gate coverage; this
historical entry intentionally does not attribute later v8 test names to that
run.
- S13 exact transferred-file equality: the S4 small transfer and S14 large
transfer both passed host `diff -r` against the original source game
directories, proving exact file equality beyond event flow.
- S14 large multi-peer chunked download: `full-empty-client` first downloaded
`alienswarm` from alpha and diffed cleanly against `fixture-alpha/alienswarm`.
A fresh `full-s14-client` then saw `alienswarm peer_count=2` and downloaded
from both alpha and `full-empty-client`. Large `.eti` chunk totals were
`67,108,864` bytes from alpha and `58,721,049` bytes from the staged peer,
balanced within one `32 MiB` chunk. Final host `diff -r` against
`fixture-alpha/alienswarm` passed.
- S15 three-way version skew: peers A/B/C advertised `cnc4` versions `20250101`,
`20250201`, and `20250301`. The client saw one row with `peer_count=3` and
`eti_game_version=20250301`; all chunks came only from C at `10.66.0.4:60290`.
Host `diff -r` against C passed.
- S16 latest-version fanout with stale peer present: A advertised stale
`20250101`; B/C both advertised latest `20250301` with a `134,217,906` byte
`.eti`. The client saw `peer_count=3`; chunks came only from B/C (`67,108,873`
and `67,109,042` bytes respectively), with stale A contributing zero. Host
`diff -r` matched both B and C.
- S17 latest-version conflict rejection: A advertised stale `20250101`; B/C both
advertised latest `20250301` but with conflicting `.eti` sizes (`1,048,748`
and `2,097,325` bytes). The client saw `peer_count=3` and latest `20250301`,
then `download cnc4` emitted `download-failed`; no target `cnc4/version.ini`
was committed.
- Gates after manual runs: `just fmt`, `RUSTC_WRAPPER= just test`, and
`RUSTC_WRAPPER= just clippy` passed.
### 2026-05-17 - Exact Transfer And Large Multi-Peer Chunking
- Fixture update: `fixture-alpha/alienswarm/alienswarm.eti` was rebuilt with
`rar a -idq -m0` from three random 40 MiB payload files, then renamed to
`.eti`. Final archive size: `125,829,913` bytes. `unrar t -idq` passed.
- Gates before manual runs: `just fmt`, `just test`, `just peer-cli-build`,
`just clippy`, and `just peer-cli-image` passed.
- S13 small exact transfer: `deep-small-client` downloaded `bfbc2` from
`fixture-bravo` with `install=false`. SHA-256 manifests matched exactly:
`bfbc2/bfbc2.eti`
`f7accef0833f29481acdeaac58261bc4fc23ebb58b7197049024d354f60daabc`;
`bfbc2/version.ini`
`f3d94f70edcebbbc7d8ce38fdf076412fb95114ce1ecf071b26c9c2f93586372`.
- S13 large exact transfer: `deep-stage-b` downloaded `alienswarm` from
`fixture-alpha` with `install=false`. SHA-256 manifests matched exactly:
`alienswarm/alienswarm.eti`
`8a4fb1fd458e731affb175134b7b99efc8d8a5eda80e978ba81f721d01aecc43`;
`alienswarm/notes.txt`
`3832bcb7057a4453981e975d2d2d528bfd9a26671423352f4a8527362d5b9810`;
`alienswarm/version.ini`
`8dfdc51d4dbfb06015b41a85a5f5d47f44144139e4a12db2b17eb040773082a3`.
- S14 multi-peer setup: `deep-stage-c` connected to alpha (`10.66.0.3:53514`)
and `deep-stage-b` (`10.66.0.2:58491`). `list-games` showed `alienswarm` with
`peer_count=2` before the download.
- S14 chunk-source evidence for `alienswarm/alienswarm.eti`: `deep-stage-c`
received chunks from `deep-stage-b` at offsets `0` and `67,108,864`
(`67,108,864` bytes total) and from alpha at offsets `33,554,432` and
`100,663,296` (`58,721,049` bytes total). The source-byte difference was
`8,387,815` bytes, below one `32 MiB` chunk.
- S14 final exactness: `deep-stage-c`'s `alienswarm` SHA-256 manifest matched
`fixture-alpha` exactly for `alienswarm.eti`, `notes.txt`, and `version.ini`.