fix: Make first-click project opens fast, honest, and recoverable - #427
Draft
Yona-Appletree wants to merge 7 commits into
Draft
fix: Make first-click project opens fast, honest, and recoverable#427Yona-Appletree wants to merge 7 commits into
Yona-Appletree wants to merge 7 commits into
Conversation
…pool recovery A failed browser-worker boot now terminates its Worker via Drop (the forget()ed closures made abandonment invisible — the zombie kept fetching the multi-MB wasm against every retry). The sim connect branch gets a bounded retry ladder (3 × 400 ms backoff) mirroring the hardware ladder. A Dead preview-pool worker revives lazily after cooldown with a 3-attempt exponential budget (2s/8s/30s) instead of poisoning the pool for the page lifetime. Boot-loop magic numbers are now named constants (same 5 s policy; the P5 inactivity rework lands on this seam). Plan: lp2025/2026-08-14-1859-first-click-open-resilience (P3) Co-Authored-By: Claude Fable 5 <[email protected]>
…inactivity timeout The page now fetches + compiles the fw-browser wasm once (engine_cache: streaming fetch with byte progress, WebAssembly.compile, thread-local Module cache) and posts the compiled Module to every booting worker by structured clone; workers only instantiate. The worker posts a status per boot phase (booting → instantiating → gpu-init → runtime-create → ready) and the host timeout is now inactivity-based (BootWaitClock, pure + native-tested): 20 s of silence per phase, 120 s for the path-fallback instantiate that contains the worker-side fetch. A slow network shows progress instead of dying at a flat 5 s; a dead worker still fails in seconds. engine_asset_phase() exposes fetch progress for the opening-frame UI (P6); warm_engine_cache() is the page-load warm-up seam (P4). ADR: docs/adr/2026-08-14-browser-worker-boot-protocol-v2.md Plan: lp2025/2026-08-14-1859-first-click-open-resilience (P5, protocol half) Co-Authored-By: Claude Fable 5 <[email protected]>
…d acquire, sync-registration order A failed open no longer leaks the project's Web Lock for the page lifetime: OpenedProject now carries an RAII OpenReceipt, committed only once the project is active — every earlier failure (load, migrate, read, protocol/link error) routes to OpenRegistry::release_open, the single named teardown close_project also delegates to. The open path acquires with a bounded ladder (10 × 50 ms) via try_acquire_polling instead of one ifAvailable shot, and mount_for_sync registers in 'syncing' before awaiting the lock (with insert-owner tracking so a racing visitor pull cannot clear the winner's registration). uid parse moved ahead of acquisition, removing the second leak site. Three defect entries land with the fix: sync-holds-the-project-lock (open; P2 owns the hold), post-acquire-open-failure-leaks-the-project- lock (fixed here), worker-boot-timeout-races-the-wasm-fetch (open; P3/P5 own the halves). Plan: lp2025/2026-08-14-1859-first-click-open-resilience (P1) Co-Authored-By: Claude Fable 5 <[email protected]>
… user-open priority The page warms the engine assets at load: the wasm through the boot-v2 engine cache (one streaming fetch, one compile, Module shared with every worker) and the glue JS into the HTTP cache. The preview pool now boots one member at a time from a Pending state (pool_may_start_boot / choose_boot_start in slot_policy, natively tested) instead of storming every fetch at construction, and Dead-worker revival respects the same gate. A user-initiated open raises app::open_priority (RAII counter guard set beside pending_open): while it is up, the pool starts no new boots and no new lease starts, and hover-to-play drops its lease request instead of contending with the click. Everything resumes by itself when the open settles. Also formats device_controller.rs (committed unformatted in ac4dde1). Plan: lp2025/2026-08-14-1859-first-click-open-resilience (P4 + P5 preload wiring) Co-Authored-By: Claude Fable 5 <[email protected]>
The cloud sync trip now holds lp-project:<uid> only to snapshot the package and history locally (SyncHold: lock + syncing registration as one unit, register-before-acquire preserved, 20×50 ms acquire ladder), releases it, and publishes from the snapshot. Post-trip bookkeeping reacquires briefly for local writes; a project opened mid-publish gets the snapshot's pending writes replayed into its live store instead (LpFsOpfs::pending_writes). await_sync_handoff demotes 3 s → 500 ms residual guard. The sync queue's request/take_due/finish already modeled one-attempt-against-a-snapshot; its mid-flight test now names D1 as its load-bearing reason. Locks guard local OPFS consistency only, never network I/O — ADR 2026-07-08 amended; lpa-fs-opfs README's stale page-wide-lock story rewritten; sync-holds-the-lock defect closed. Plan: lp2025/2026-08-14-1859-first-click-open-resilience (P2) Co-Authored-By: Claude Fable 5 <[email protected]>
…click supersede (D4) The opening frame now narrates the actual pipeline: downloading engine (byte progress from the P5 cache), starting engine (worker boot phase, new worker_boot_phase read surface), preparing project, waiting for the project lock (new projects_awaiting_lock read surface), and a Failed state with a working Retry + back-to-Explore — no eternal skeleton remains. Labels debounce ~150 ms so fast opens show only the calm skeleton; example-open failures surface on Explore via the same notice. Second click supersedes (D4): CommandSender::send bumps an open generation the parked open observes at three await boundaries, the newest click wins, and a superseded open abandons its OpenReceipt (P1) — lock, registration, flushers all released. The booted sim worker is reused, never torn down, so the newest click is also the fastest. Same-batch double-clicks coalesce (pure opens only; create-and-open is never dropped). Cards dim but stay clickable while an open runs. Stories: six opening-frame states posed via explicit state prop (baselines to follow from validate-stories). Also formats P2's library_host_opfs drift. Plan: lp2025/2026-08-14-1859-first-click-open-resilience (P6) Co-Authored-By: Claude Fable 5 <[email protected]>
Captured in the pinned CI environment by run: https://github.com/PhotomancerArt/lightplayer/actions/runs/31862563271
Contributor
CI refreshed the story baselines on this branchThe Review every PNG in the PR's Files changed view (swipe / onion-skin). 10 more file(s)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.











Plan: lp2025/2026-08-14-1859-first-click-open-resilience
Path: ~/.photomancer/planning/lp2025/2026-08-14-1859-first-click-open-resilience/plan.md
Fixes the demo-day first-click open failures (diagnosed 2026-08-14): two
compounding root causes — (A) cloud sync holding the per-project Web Lock
across network I/O plus a post-acquire lock leak on failed opens
("project is open in another tab" with one tab), and (B) a flat 5 s
worker-boot timeout that includes the fw-browser wasm fetch, with no
recovery (terminal Dead preview pool, zombie workers, eternal skeleton).
Product principle: user clicks act as quickly as possible; every wait
state is visible, honest, and recoverable. Newest click wins.
Phases
Stories note: six new project-opening-frame stories have no baselines yet
— expect a validate-stories auto-commit; merge the bot head after.
Resume notes: read plan.md, then phase files (each carries an
Implementation Result). ADRs: boot-protocol-v2 (new), per-project
locking (amended). Defects: 2 fixed-in-PR, 1 open-with-halves-cited.
🤖 Generated with Claude Code