Comprehensive user-flow review across guest/host/admin roles, then fixes with
e2e regression guards. Highlights:
- Offline upload queue auto-resumes on reconnect: network errors keep items
pending (not error), 4xx are terminal (no infinite retry), quota exhaustion
returns a distinct 413; queue cap + dedup.
- Export lifecycle: release atomically locks uploads; reopen invalidates and
re-release regenerates the keepsake; workers claim their job atomically so a
reopen->re-release can't corrupt the ZIP; startup re-spawns interrupted
exports.
- Sessions slide on activity (no 30-day cliff); JWT expiry deferred to the
revocable session row; sign-out-everywhere + revoke-on-PIN-reset.
- Ban always hides content (v_feed / find_visible_media / export filter
is_banned) but stays a read-only ban per USER_JOURNEYS §10 — sessions are
not revoked, read access + keepsake download preserved.
- Realtime: server-clock SSE delta cursor; event-closed/opened drive the UI
live; feed_delta rate-limited; like returns {liked, like_count} to fix
multi-device drift; lightbox live comments; diashow delta backfill.
- Forgotten-PIN in-app request flow; simultaneous same-name join returns 409;
quota increment is transactional; operator floor.
Adds e2e/specs/10-flow-review/ (offline resume, export integrity, deterministic
anti-race guard) and updates existing specs for the new contracts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lib/ conventions
Short rules. The patterns we already follow as of v0.16 — write new code that fits.
One store per cross-cutting concern. A single *-store.ts file owns each one:
auth.ts— JWT / PIN inlocalStorage,isAuthenticatedwritableui-store.ts— bottom-nav visibility, upload-sheet open state, FAB badge countdata-mode-store.ts— Saver vs Original media-loading preferenceprivacy-note-store.ts— admin-configured Datenschutzhinweis textquota-store.ts— live per-user storage snapshotupload-queue.ts— IndexedDB-persisted upload queue + processing state
Don't import these into other stores unless strictly necessary; let pages compose them.
DTOs mirror Rust types. All TS interfaces live in types.ts. Each one carries a
// mirrors backend/src/path::TypeName comment so the two stay searchable. If you add
a Rust DTO, add the TS twin in the same PR.
Gestures via Svelte actions in actions/. Long-press, double-tap, future swipe —
each is a use: action that fires a CustomEvent. Components stay free of gesture
plumbing.
Reusable bottom sheets via ContextSheet.svelte. Pass an actions: ContextAction[]
array. Any page that needs a long-press / kebab context menu uses the same primitive.
SSE relays are listed in sse.ts::KNOWN_EVENTS. New server event → add one entry
to that array, that's it.
Diashow transitions live in diashow/transitions/. Each is a Svelte component
plus one entry in transitions/index.ts. Adding a new animation is two-line work; no
diashow code needs to change.
No new global stores beyond the list above unless the new concept is genuinely app-wide. Page state belongs in the page.