A native WM_DELETE_WINDOW reproduction completed the frontend drain and then
failed with "window.destroy not allowed". All three windows lacked the destroy
permission used by Tauri's onCloseRequested wrapper. Removing the listener had
previously let a second click bypass that denied IPC; retaining the listener
made every click fail. The frontend was not stuck waiting on its own listener.
Grant window destruction to the three configured application windows. Keep the
existing frontend drains, early peer cancellation, and final runtime/task joins.
Test the application's actual generated RuntimeAuthority, including expanded
plugin defaults, instead of assuming a mocked successful destroy proves access.
Explicitly include generated capabilities and ACL manifests in the context
constructor's rustc dependencies. The configured compiler cache reused the old
library after a permission-only edit because Tauri's macro reads these files
without recording compiler dependencies. A remove/restore native probe now
rebuilds with the correct permission in both directions.
Document the complete ownership chain and failure evidence. Add a PID-checked
X11 WM_DELETE_WINDOW helper for repeatable close-button probes without killing
the process or bypassing the frontend boundary.
Test Plan:
- Native baseline: destroy denied and process remained alive after one request.
- Resolved-ACL regression failed before the permission fix and passes afterward.
- Native main/companion close probes: one request per window; normal process exit.
- Permission-only rebuild with compiler cache: normal exit in 197 ms.
- Production executable: normal exit in 43 ms; QUIC port released and rebound.
- just test: 797 passed on unchanged rerun after one initial subprocess fixture
startup-marker timeout, before that test exercised cancellation.
- just frontend-test: 99 passed.
- just fmt, just clippy, just build, and git diff --cached --check: passed.
- Native helper compiled with -Wall -Wextra -Werror.
- Native probes ran on Linux X11/XWayland; Windows/macOS were not measured.
Tauri's onCloseRequested wrapper already waits for the asynchronous handler and
destroys the window when that handler returns without preventing the event. The
launcher instead prevented every first request, unregistered the active listener
inside its own callback, and relied on an explicit destroy. If that callback did
not complete, the first click only started peer shutdown and a second click
bypassed the removed listener.
Let the first close request follow Tauri's automatic destruction path after
frontend owners and persistence have drained. Keep the listener installed during
that drain, and prevent only repeated requests so they share the first close
without scheduling duplicate destruction. Bootstrap and render failures still
unregister the listener and destroy their empty or failed webview explicitly.
Test Plan:
- `just frontend-test` -- passed (99 tests)
- `just build` -- passed
- `just fmt` -- passed
- `git diff --check` -- passed
- Native GUI close timing was not available in the automated UI surface.
The launcher previously began peer cancellation only after the frontend had
finished draining its pending invokes and the Tauri process emitted Exit. A
pending acknowledgement could therefore keep the close handler waiting while
the peer still owned the work needed to complete that acknowledgement.
Close admission and unrar cancellation now begin on the native main-window
CloseRequested event. A tracked application task signals the currently owned
peer runtime early, while the existing Exit handler still waits for all
admitted invokes, takes and joins the runtime, and drains background tasks.
An atomic latch keeps repeated close and exit notifications from scheduling
multiple shutdown requests, and a runtime that is being created remains owned
by its in-flight invoke until the final join boundary.
Test Plan:
- `just fmt` -- passed
- `just test` -- passed (796 Rust tests)
- `just frontend-test` -- passed (98 tests)
- `just clippy` -- passed
- `just build` -- passed
- `git diff --check` -- passed
- Native window-close timing was not available in the automated UI surface.
The shared search style applied a 380px flex basis intended for a horizontal
bar to the Call to Play form's vertical row, making its search field 380px
tall. Keep sizing in the existing topbar rules so the form uses the 36px
search height and can fit within its available width.
Keep search results in normal flow so the scrolling modal contains them,
and restore the design reference's accent background and border for selected
timing, duration, and day buttons.
Test Plan:
- just frontend-test -- 98 passed
- deno task build (frontend directory) -- TypeScript and Vite build passed
- git diff --cached --check -- passed
- Independent static review -- no regressions found
- Visual verification unavailable: this session has no browser surface
Cancelled handshakes can remain in transport state after their application
owners return. Waiting for client idleness before signaling endpoint stop
added a measured three-second grace timeout to shutdown.
Stop and join the owned endpoint directly after application scopes drain.
Keep supervisor joining and isolated runtime teardown, which also finish
s2n's adapter workers and release their socket clones. Use this normal cleanup
for rejected-handshake tests instead of their former special shutdown path.
Test Plan:
- A loopback silent-peer handshake reproduced a 3.001-second wait before the fix.
- New supervisor regression verifies wait_stopped completes within one second
and releases the client UDP socket after cancelling an in-flight handshake.
- just test: all 796 workspace tests passed, including the new regression.
- just fmt and just clippy: passed.
- Actual native window-close timing was not manually measured.
Operation admission withdraws local availability before changing game files.
The UI previously received that withdrawal before the busy state, then
filtered out installations because only downloads counted as local activity.
Publish the active operation first while preserving atomic network withdrawal.
Keep active operations in All Games and Local, retain prior Installed filter
membership until settlement, and show operation-specific status labels. Local
availability and playability remain derived from the backend. Document the
installation presentation and cover withdrawal, settlement, and filter counts.
Test Plan:
- just fmt and just clippy: passed.
- just test: passed all workspace suites.
- just frontend-test: 98 passed, including installation lifecycle regressions.
- deno task build: TypeScript and Vite production build passed.
- Native GUI interaction was not manually exercised.
The launcher previously defaulted to fixtures and production generation used
the source database directly. Generate a separate database and manifest set
from package directories, validating staged output with the application loader
before installation. Keep strict selection by default, with independent
opt-ins for missing games and package version overrides in the copied database.
Share database filtering and staging with the fixture publisher, and include
source metadata and generation modes in the publication cache.
Make normal runs consume existing production authority. Add an explicit local
test recipe with separate resources, app settings, and a compiled startup game
directory. Build-time gates exclude local authority from production and require
Tauri development mode. Document the generation and launch workflows.
Clear generated catalog copies before Tauri copies the selected resource tree
so mode switches and reduced catalogs cannot retain stale manifests. Watch the
copied files to repair deletion and preserve prior output mtimes only when the
bytes are unchanged, allowing subsequent builds to become fresh.
Test Plan:
- `just fmt` -- passed.
- `just clippy` -- passed with warnings denied.
- `just test` -- workspace tests passed using fixture authority.
- `just frontend-test` -- 94 passed.
- `python3 -m unittest discover -s tools -p 'test_catalog_source_cache.py'`
-- 4 passed.
- `git diff --cached --check` -- passed.
- Interactive GUI launches and production bundles were not exercised.
Make the repository guidance explicit that agents should commit completed
changes without requesting confirmation.
Test Plan:
- `just fmt` -- passed on the full worktree, including Markdown checks.
- `git diff --cached --check` -- passed.
The accepted root transition already publishes the replacement local snapshot
before the transfer-status fence completes. Clearing local flags again when
committing the folder erased that snapshot, and the subsequent ListGames
refresh only restored remote availability. Keep the accepted local state.
Test Plan:
- `just fmt`, `just clippy`, and `just test` -- passed on the full worktree.
- `just frontend-test` -- 94 passed on the full worktree.
- `git diff --cached --check` -- passed.
- GUI directory switching was not exercised interactively.
Reassess the Fable ignored-finding ledger against the LAN-party threat model, move the newly bounded findings to fixed, and state the remaining S33, anonymous-membership, unrar, local-race, and elevated-script decisions precisely.
Add the 2026-09-12 scan report with dependency results, independent follow-up review, validation evidence, and platform limits; update architecture limits to match the implementation.
Test Plan:
- just fmt
- just clippy
- just test
- just frontend-test
- just build-fixture
- just peer-cli-build
- just mdns-vendor-test
- just security-audit
- git diff --check
Record the repository formatter's deterministic layout for the catalog-preflight helper and handshake-window assertion.
Test Plan:
- just fmt
- just clippy
- just test
- git diff --check
Keep the catalog-sized absolute provider deadline and independently reset a ten-minute no-progress deadline after each successful frame write. Large valid games can run past ten minutes, while stalled producers, readers, and FIN still release their permits promptly.
Test Plan:
- just test
- just clippy
- long-total stalled-provider and slow-reader regressions
- git diff --check
Before TLS and application leases exist, admit at most eight handshake attempts per observed IP during one three-second handshake window. Bound the spoofed-origin ledger at 256 entries while retaining s2n's default port and global handshake controls.
Test Plan:
- just test
- just clippy
- per-origin window and spoofed-origin ledger tests
- git diff --check
Track the last attempted configured root separately from the last successful snapshot. A root switch clears backoff once; repeated budget failures on that same root now retain exponential delay.
Test Plan:
- just test
- just clippy
- repeated failing-root backoff regression
- git diff --check
Give a provider ten setup minutes plus its catalog streamed bytes at 1 MiB/s instead of one fixed ten-minute absolute window. Large healthy games can complete while the sender still has a finite nonrenewable deadline through FIN.
Test Plan:
- just test
- just clippy
- large-provider timeout regression
- git diff --check
Reuse the stable 64 KiB non-link version.ini reader in anonymous transfer admission and install recovery. A stale Ready snapshot can no longer turn a locally enlarged sentinel into repeated unbounded request-path reads or diagnostic content.
Test Plan:
- just test
- just clippy
- oversized public-admission sentinel regression
- git diff --check
Charge every SmartSteamEmu.ini chunk to one 64 MiB aggregate budget and the existing ten-minute scan clock. All candidate reads still finish before the first write, so exhaustion cannot leave partial settings or a completion marker.
Test Plan:
- just test
- just clippy
- aggregate-read zero-partial-write regression
- git diff --check
Acquire one 8 MiB endpoint-IP budget before the shared 32 MiB control-response pool. Same-origin maximal-prefix waiters now stay out of the fair global semaphore queue, so they cannot head-of-line block small responses from other LAN hosts.
Test Plan:
- just test
- just clippy
- cross-origin semaphore ordering regression
- git diff --check
Share one catalog-derived deadline across at most four distinct endpoint-IP attempts: ten minutes plus two catalog copies at 1 MiB/s. Keep the ten-minute inactivity timer, but renew it only after a nonempty catalog-checked file chunk is written to staging.
Cancellation, rollback, integrity quarantine, and public exhaustion states retain their existing semantics.
Test Plan:
- just test
- just clippy
- shared-deadline, source-IP budget, metadata-inactivity, and useful-byte regressions
- git diff --check
Set the per-origin control-stream share to eight against the sixteen-slot global pool, preventing one observed host from occupying every decoded control task.
Test Plan:
- just test
- just clippy
- exact admission-limit regression
- git diff --check
Start one twenty-minute retry deadline only after an initial chunk failure, retain it across authenticated source and IP changes, and give each in-flight retry the earlier of that deadline and its ten-minute attempt limit. Expiry remains a non-quarantining transport failure.
Test Plan:
- just test
- just clippy
- three paused-time retry-deadline regressions
- git diff --check
Require each selected package root to be a regular non-link directory before reading its bounded version.ini, and omit package-controlled observed contents from mismatch diagnostics. Full-selection preflight still completes before archive work or publication.
Test Plan:
- just test
- just clippy
- symlink-root and diagnostic-redaction regressions
- git diff --check
Add one just recipe that scans Cargo.lock with the current RustSec database and deno.lock with Deno's advisory service.
Test Plan:
- just security-audit (no known vulnerabilities; seven RustSec informational warnings)
- just --fmt --check
- git diff --check
Preserve required UAC elevation for game_setup.cmd, game_start.cmd, and server_start.cmd through a fixed-role elevated launcher worker. The worker reloads and matches embedded catalog authority, verifies the exact script from a no-follow locked handle, resolves System32 cmd.exe, and transfers path locks into the command process.
Setup still waits for completion; game and server return after the verified handoff while their command process retains the locks. Unmanifested, changed, reparse-backed, markerless, and streamed-only scripts fail closed.
Test Plan:
- just test
- just clippy
- just frontend-test
- just build-fixture
- nine Linux-visible authority/parser/digest tests
- Windows-only lock-transfer test added but not run (no Windows target/runtime available)
- git diff --check
Carry the mDNS response source independently of its advertised A or AAAA target. Active and cooling candidates now charge the observed host, so rotating peer IDs, ports, and target addresses cannot escape the eight-candidate origin budget.
Test Plan:
- just test
- just clippy
- rotating-advertised-target regression
- git diff --check
Vendor the pinned mdns-sd 0.21.1 source so response records retain their observed source IP. Bound unauthenticated cache records to 1024 globally and 128 per source, deduplicate and cap timers at 4096, and poll at least once per second for expiry cleanup.
Expose packet provenance through lanspread-mdns, drop originless resolutions, and add a dedicated vendor test recipe while keeping third-party sources outside workspace formatting and Clippy.
Test Plan:
- just mdns-vendor-test (106 tests passed with socket access)
- just test
- just fmt
- just clippy
- cache/per-source, exact-refresh, timer-cap, and serde tests
- git diff --check
Record the deterministic nightly rustfmt output for the newly added event-channel and retry code after the repository-wide formatter gate was repaired.
Test Plan:
- just fmt
- just clippy
- git diff --check
Filter roots through the catalog before retaining them, cap entries, games, and poll time, and preserve the previous complete snapshot on failure. Repeated failures back off exponentially and a root change resets retry state.
Test Plan:
- just test
- just clippy
- focused catalog-filter, budget-preservation, and backoff tests
- git diff --check
Keep one queued and one replaceable pending snapshot for remote-library and Call-to-Play views while preserving lifecycle-event FIFO delivery. Generation barriers and fenced drains prevent stale nonempty views from crossing disable or acknowledgement boundaries.
Test Plan:
- just test
- just clippy
- focused burst, lifecycle ordering, fence, repeated-barrier, and stale-view tests
- independent ordering review
- git diff --check
Bound recursive setting discovery and INI reads, and finish every scan/read before the first write. Budget exhaustion can no longer leave partial setting changes or a misleading completion marker.
Test Plan:
- just test
- just clippy
- focused traversal, oversized INI, and zero-partial-write tests
- git diff --check
Bound legacy traversal by entry count and cooperative elapsed time, and complete the read-only plan before mutating any source or destination. Reuse the catalog-sized legacy-index read ceiling.
Test Plan:
- just test
- just clippy
- focused preflight and oversized migration tests
- git diff --check
Cap index reads, games, archive fingerprints, selected-root entries, recursive depth and bytes, scan time, and version.ini reads. Failed rescans retain the prior complete index and revision instead of publishing partial state.
Test Plan:
- just test
- just clippy
- focused oversize index, traversal, and prior-snapshot tests
- git diff --check
Move file-chunk and Stream Install branches into focused helpers so the new admission path remains within the workspace's strict Clippy limits without changing request behavior.
Test Plan:
- just clippy
- git diff --check
Track both authenticated peer IDs and endpoint IPs for each failed chunk. Rotating keys or ports at one host no longer grants another automatic ten-minute transfer attempt, while the existing eight-attempt ceiling remains.
Test Plan:
- just test
- focused same-IP Sybil and distinct-source selection tests
- git diff --check
Bound each observed remote IP to eight connections, sixteen control streams, eight bulk transfers, and one Stream Install provider. Drop-owned leases release on normal completion or unwind, and per-origin saturation cannot consume another resource class.
Test Plan:
- just test
- focused port rotation, distinct-origin, drop/unwind, class isolation, and provider saturation tests
- git diff --check
Read the four-byte response length explicitly, acquire bytes from one 32 MiB connector-wide budget before buffering, and require exact body plus EOF. One-way requests now check EOF without allocating an 8 MiB decoder.
Test Plan:
- just test
- focused complete, oversized, truncated, trailing, one-way, and shared-budget tests
- git diff --check
Allow at most eight committed peer identities at one pinned QUIC endpoint IP after Hello authentication. Reject excess keys before address-owner eviction, while removals release capacity and other LAN hosts remain admissible.
Test Plan:
- just test
- focused port-rotation, ninth-key, address-owner, other-IP, and release tests
- git diff --check
Use one ten-minute authority window for all raw chunk writes and FIN, and one window for Stream Install producer waits, frames, and FIN. Successful incremental writes can no longer renew a public bulk slot indefinitely.
Test Plan:
- just test
- focused paused-time cumulative-write regressions
- git diff --check
Keep the generated security report and archived audit artifacts outside the maintained Markdown formatter set, and run Tombi offline. This makes the repository formatter deterministic without rewriting generated evidence.
Test Plan:
- just fmt
- just --fmt --check
- git diff --check
Limit server-wide decoded control requests to 16 and gate Stream Install providers behind a separate two-slot semaphore. The provider permit remains held through producer cleanup, bounding concurrent unrar work while ordinary bulk transfers retain their own capacity.
Test Plan:
- just test (passed after rerunning one transient ETXTBSY failure)
- just fmt (Rust formatting passed; repository's generated security report still triggers pre-existing rumdl violations)
- git diff --check
A valid remote view could make the renderer reduce and mount thousands of calls
every second. A single long chat also re-sorted its growing message array after
every append during every reduction.
Keep only the highest-ranked 128 nominations and latest 256 messages for UI
projection. Events are sorted once before folding, so chat messages remain in
stable order without repeated growing-array sorts. Backend author history and
creator authority are unchanged.
Test Plan:
- `just frontend-test` -- passed; 94 frontend tests passed.
- `just fmt` -- TypeScript formatting completed; the recipe then hit the
pre-existing generated security-report Markdown-lint failures.
- `git diff --cached --check` -- passed.
Wire-level author limits still allowed one peer to publish thousands of creator
roots and a Sybil set to retain hundreds of thousands of aggregate events.
Those valid snapshots were repeatedly projected for the desktop.
Limit one author to 128 creator roots and all retained remote history to 16,384
events. Over-budget revisions replace that author's slice with an empty
watermark, which frees memory and prevents liveness from pulling the rejected
snapshot every five seconds. Local author capacity remains independent.
Test Plan:
- `just test` -- passed outside the sandbox; 498 peer tests and all workspace
targets passed.
- `just fmt` -- Rust formatting completed; the recipe then hit the pre-existing
generated security-report Markdown-lint failures.
- `git diff --cached --check` -- passed.
Per-peer library limits still allowed 64 authenticated identities to retain a
large aggregate map and repeatedly clone it for UI publication.
Cap retained remote availability at 16,384 rows. A peer whose new projection
would exceed the budget stays authenticated with an empty library slice, and
its accepted revision is retained so liveness does not repeatedly pull the same
over-budget snapshot.
Test Plan:
- `just test` -- passed outside the sandbox; 496 peer tests and all workspace
targets passed.
- `just fmt` -- Rust formatting completed; the recipe then hit the pre-existing
generated security-report Markdown-lint failures.
- `git diff --cached --check` -- passed.
A failed catalog chunk could try every authenticated peer identity. Since each
source owns a fresh transfer deadline, one host could use Sybil identities to
turn a single unavailable chunk into a multi-hour retry sequence.
Count the initial source and cap one chunk at eight distinct peer identities.
Integrity quarantine, transport classification, cancellation, and explicit
user retries retain their existing behavior.
Test Plan:
- `just test` -- passed outside the sandbox; 495 peer tests and all workspace
targets passed.
- `just fmt` -- Rust formatting completed; the recipe then hit the pre-existing
generated `security-report/report.md` Markdown-lint failures.
- `git diff --cached --check` -- passed.
The selected game root could supply an unbounded legacy library index whose
revision was copied verbatim into app-owned state. A revision of `u64::MAX`
made every later checked revision advance fail even after the original root was
removed.
Read only a bounded regular, non-link file whose identity stays stable across
the read. Deserialize it before publication, preserve its cached game data, and
reset its stale revision authority to zero. Existing app-owned state still wins
without reading the legacy source, and invalid input remains in place for
recovery.
Test Plan:
- `just test` -- passed outside the sandbox; 494 peer tests and all workspace
targets passed.
- The initial sandboxed `just test` reached an unrelated Unix-socket permission
denial, then passed unchanged with the required socket permission.
- `git diff --cached --check` -- passed.
The main webview retained `shell:allow-open` even though the frontend never
uses the shell plugin directly. File-manager opening already flows through the
`open_game_files` command, which checks catalog membership and containment.
Remove only the frontend permission. The Rust shell plugin and dependency stay
available for the checked backend open operation and bundled unrar sidecar.
Test Plan:
- `just build-fixture` -- passed, including the frontend build and Tauri
capability validation.
- `git diff --cached --check` -- passed.