diff --git a/AGENTS.md b/AGENTS.md
index d33108e1..5ce15f5c 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -11,7 +11,7 @@
- **Primary deploy target:** Static SPA on GitHub Pages (`/WorldScript-Studio/` base path)
- **Secondary targets:** Vercel (root base) and Cloudflare Pages via edge builds (`pnpm run build:edge`)
- **Desktop:** Tauri 2 bundles for Linux (AppImage), macOS (DMG), and Windows (MSI); auto-updater enabled via `latest.json`
-- **Version:** `1.28.3`
+- **Version:** `1.28.4`
- **License:** MIT
The app supports a multi-provider AI stack (Gemini, OpenAI, Claude, Grok, OpenRouter, Ollama, WebLLM, ONNX Runtime Web, Transformers.js), four AI execution modes (Hybrid / Cloud / Local / Eco), real-time collaboration with E2E encryption, a Plot Board v2 with swimlane/canvas/timeline modes, character/world management, manuscript export, voice dictation, and a 19-locale i18n layer.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea58407d..88a7fa6e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,12 +7,69 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+
+## [1.28.4] — 2026-09-05
+
+### Fixed
+
+- **PWA first-install unprompted reload:** `clients.claim()` on activation fired `controllerchange`
+ on the very first-ever page load, not only on genuine updates, causing every first-time visitor
+ to undergo one automatic reload shortly after landing. Fixed via absolute-URL own-worker
+ identification, shared-origin foreign-worker exclusion, persistent installation history, event
+ queuing during classification, and recovery when `getRegistration()` fails. A narrow residual —
+ two tabs racing the very first-ever activation of this origin's service worker so closely that
+ one tab's evidence reads as "already installed" — requires real cross-tab coordination and
+ remains open as #614. Fixes #585, PR #613.
+- **Service-worker cache reads are now positively ownership-scoped:** every `caches.match()` call
+ in the fetch handler and offline fallback now specifies its owning cache by name, closing a
+ shared-origin (e.g. `qnbs.github.io` hosting multiple projects) cache-read isolation gap. Fixes
+ #514, PR #612.
+- **Factory Reset could reboot back into Settings instead of Welcome Portal:** a stale
+ view-carrying URL hash survived `wipeAllAppData()`'s reload, and `readInitialView()` read it with
+ higher priority than checking whether a project still existed. PR #592.
+- **Desktop project corruption is now preserve-first:** a corrupt desktop project is quarantined
+ (moved to `quarantined-projects` with a collision-safe name) rather than risking deletion; the
+ destructive IndexedDB-reset action is no longer offered for project-load failures. PR #542.
+- **Desktop filesystem I/O errors now get a distinct, truthful recovery action:** separated from
+ corruption-quarantine and generic-storage-reset, so each failure class only exposes the action
+ that's actually safe for it. PR #545.
+- **Intentionally cleared project metadata no longer reappears:** an empty title/logline set by the
+ user could be silently repopulated during returning-user bootstrap. PR #546.
+- **Factory Reset could report success while user data remained:** `deleteDatabase()` treated a
+ genuine `onerror` or an `onblocked` event (another connection still open) as success, so
+ `wipeAllAppData()` could resolve without every database actually being deleted. `onerror` now
+ rejects, and `onblocked` waits for the connection to close before giving up. PR #596.
+
+### Accessibility
+
+- **Welcome/Home dashboard WCAG AA contrast** and a repaired `prefers-reduced-motion` cascade (the
+ override now correctly wins over equal-specificity base rules); default appearance preset is now
+ `default` for new and invalid-legacy sessions. Fixes #565, PR #609.
+- **ManuscriptEditor deferred-mirror contrast** raised from 4.45:1 to ~5.10:1. PR #560.
+
+### Security
+
+- **`fflate` ZIP64-parsing DoS**, used by this app's export path: overridden to `0.8.3`. PR #595.
+- Routine dependency maintenance: `xmldom`/`fast-uri`/`qs` floor bumps (PR #587), `log` crate in
+ `src-tauri` (PR #561), `codeql-action` group (PR #562), `actions/setup-node` (PR #594).
+
### Documentation
-- **Post-release v1.28.3 truth sync:** removed the now-stale release-candidate markers from
- README.md and CHANGELOG.md now that the `v1.28.3` tag and GitHub Release are published, and
- recorded real release-gate evidence in AUDIT.md (main CI/CD run, CodeQL, GitHub Pages deploy,
- tag-triggered Tauri/CI/Docker runs, published release assets).
+- **R-15 secure desktop storage — design contract admitted, implementation not started:** the full
+ storage/migration/recovery contract (S5-A, S5-B1, S5-B2, S5-B3) is now defined and its
+ cross-contract consistency audited. No code has shipped yet; desktop project files remain
+ documented as plaintext until this implementation lands. PRs #564, #580, #581, #582, #584.
+
+### Tests
+
+- **Visual regression testing repaired:** VRT baselines were directory listings, not the
+ application — the gate was not protecting against real visual regressions. Now reaches actual app
+ routes with real baselines and a proven negative control demonstrating a visible regression fails
+ the gate. PR #610.
+- **Async, generation/epoch-based IDB reset-quiescence contract** across 9 service modules, closing
+ every long-lived connection before a factory reset deletes anything (PR #596; issue #532's own
+ WelcomePortal entry-nondeterminism root cause is a related but distinct class and remains open).
+ WelcomePortal E2E recovery navigation made locale-independent (PR #590).
## [1.28.3] — 2026-08-27
diff --git a/README.md b/README.md
index 062f6c48..973cb8ad 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@
-
+
+
diff --git a/TODO.md b/TODO.md
index 2f4312c9..c29b180b 100644
--- a/TODO.md
+++ b/TODO.md
@@ -8,12 +8,49 @@ Status: 🔄 in progress | ⬜ open | ✅ done
---
-## Current Sprint — Post-#477 reconstruction reconciliation, Dependabot, and documentation truth (2026-08-26)
+## Current Sprint — Release truth reconciliation and R-15 desktop at-rest encryption priority (2026-09-05)
> **Status: 🔄 in progress.** The authoritative native sequence remains
> [`docs/native/ROADMAP-QT-GPUI-DESKTOP.md`](docs/native/ROADMAP-QT-GPUI-DESKTOP.md), with the
-> next Rust-Core capability choice recorded in [`docs/native/CORE-MIGRATION-LEDGER.md`](docs/native/CORE-MIGRATION-LEDGER.md);
-> no Qt or GPUI implementation work is part of this sprint.
+> next Rust-Core capability choice recorded in [`docs/native/CORE-MIGRATION-LEDGER.md`](docs/native/CORE-MIGRATION-LEDGER.md).
+> R-15 (desktop at-rest encryption) design work is complete — S5-A/B1/B2/B3 are all admitted — but
+> [`docs/native/DESKTOP-MIGRATION-ROADMAP-REV3.md`](docs/native/DESKTOP-MIGRATION-ROADMAP-REV3.md)
+> explicitly forbids pulling Wave 3/4 R-15 **implementation** ahead of unresolved Wave 2 authority
+> prerequisites, and the ledger's row 10 records `S5_IMPLEMENTATION_READY=NO`. This sprint's
+> desktop-storage work is therefore the still-open Wave 2 prerequisite (ledger row 9: the
+> project state-shape compatibility adapter), not R-15 implementation itself. No Qt or GPUI
+> implementation work is part of this sprint.
+
+- ✅ `v1.28.2` (2026-08-27) and `v1.28.3` (2026-08-27) released, closing out the prior sprint's
+ accumulated reconstruction-reconciliation and documentation-truth work.
+- ✅ PWA first-install unprompted-reload fix (#585, PR #613) and shared-origin service-worker
+ cache-read isolation fix (#514, PR #612) merged.
+- ✅ WCAG AA contrast + `prefers-reduced-motion` cascade fix and default-appearance-preset change
+ (#565, PR #609); ManuscriptEditor contrast fix (#341, PR #560).
+- ✅ Preserve-first desktop corruption recovery (PR #542) and a distinct filesystem-I/O recovery
+ action (PR #545) landed.
+- ✅ Visual regression testing repaired — baselines previously pointed at directory listings, not
+ the application (PR #610).
+- 🔄 `v1.28.4` release cut in progress: version/`CHANGELOG.md`/`TODO.md`/`README.md`
+ reconciliation for everything merged since `v1.28.3` is in PR #615. Tag, GitHub Release, release
+ artifacts, and the post-release `AUDIT.md` evidence entry all remain pending until after that PR
+ merges and post-merge main CI/CodeQL are green.
+- ⬜ Close the outstanding Wave 2 prerequisite (ledger row 9: project state-shape compatibility
+ adapter) — currently in progress, not complete. Wave 3/4 R-15 implementation stays blocked
+ (`S5_IMPLEMENTATION_READY=NO`) until this and `S5_TERMINAL=YES` are both true.
+- ⬜ #614 (narrow concurrent-first-install multi-tab race, requires cross-tab coordination) and
+ #532 (WelcomePortal E2E entry nondeterminism root cause) remain open, tracked separately —
+ not part of this sprint unless they directly block release or R-15 work.
+
+## Archived sprint history
+
+The completed release and infrastructure sections below are retained for provenance. They are not
+the current sprint plan; long-term native sequencing belongs in the linked roadmap and ledger.
+
+## Archived — Post-#477 reconstruction reconciliation, Dependabot, and documentation truth (2026-08-26)
+
+> **Status: ✅ Superseded by the current sprint above.** The release this section's final bullet
+> anticipated shipped as `v1.28.2` and `v1.28.3` (2026-08-27).
- ✅ PR #477 (Qt/PWA architecture-governance roadmap) merged; `v1.28.1` released 2026-08-23.
- ✅ Reconstruction reconciliation: the frozen PR #491 bundle's three genuinely-still-required
@@ -27,14 +64,6 @@ Status: 🔄 in progress | ⬜ open | ✅ done
test-oracle fix for the hardcoded SHA it changed) and #496 (`docker/setup-buildx-action`
4.2.0→4.3.0) merged.
- ✅ Documentation truth pass (`AUDIT.md`/`TODO.md`, this update).
-- 🔄 A new release cut for this sprint's accumulated work (reconstruction reconciliation,
- Dependabot integration, this documentation pass) remains open — a new version, not a repeat of
- the already-published `v1.28.1` above.
-
-## Archived sprint history
-
-The completed release and infrastructure sections below are retained for provenance. They are not
-the current sprint plan; long-term native sequencing belongs in the linked roadmap and ledger.
## Archived — Native desktop consolidation and Wave 2 G1 (2026-08-20)
diff --git a/package.json b/package.json
index 798ba526..ad873d31 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "worldscript-studio",
"private": true,
- "version": "1.28.3",
+ "version": "1.28.4",
"description": "WorldScript Studio — offline-first, AI-powered creative writing application.",
"author": "QNBS",
"keywords": [
diff --git a/public/sw.js b/public/sw.js
index 1556f55a..1c705a57 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -6,7 +6,7 @@
// ============================================================
// QNBS-v3 (CodeRabbit): version bump auto-synced from package.json by scripts/sync-sw-version.mjs — every CACHE_STATIC/CACHE_DYNAMIC name changes too, so this alone invalidates all prior caches on next activate.
-const APP_VERSION = '1.28.3';
+const APP_VERSION = '1.28.4';
const CACHE_STATIC = `worldscript-static-v${APP_VERSION}`;
const CACHE_DYNAMIC = `worldscript-dynamic-v${APP_VERSION}`;
const CACHE_IMAGES = `worldscript-images-v${APP_VERSION}`;
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 7db6ca79..cb859962 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -6685,7 +6685,7 @@ dependencies = [
[[package]]
name = "worldscript-studio"
-version = "1.28.3"
+version = "1.28.4"
dependencies = [
"base64 0.23.1",
"candle-core",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 27154357..646b0e1a 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "worldscript-studio"
-version = "1.28.3"
+version = "1.28.4"
description = "AI-powered creative writing and world-building application"
authors = ["QNBS"]
license = "MIT"
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 9486bbbb..8c0274e0 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "WorldScript Studio",
- "version": "1.28.3",
+ "version": "1.28.4",
"identifier": "com.worldscript.studio",
"build": {
"frontendDist": "../dist",