# Native browser-state recovery — 20 October 2026 The shared automatic browser saver previously cleared native localStorage before hydrating a positive account snapshot. A divergent native dictionary could be lost without a durable local original. A native-browser reproduction with local score 99 and cloud score 3 confirmed this separate shared-service hazard. It does not establish that the platform server deleted the reported user's save. The helper now preserves a divergent nonempty native dictionary in a reserved archive bound to the server's account/project/slot `save_identity` before hydration. Current cloud data can be loaded for inspection, but unresolved recovery fences automatic cloud writes and native `setItem`, `removeItem` and `clear` calls until the player chooses and reopens. The recovery warning remains unresolved across reopening, even when the hydrated native dictionary now equals cloud data. A first empty account snapshot with native progress also requires an explicit local/cloud choice; enabling synchronization no longer silently imports unbound native data. Positive empty revisions are intentional resets. A selected empty named/default slot is authoritative. Both preserve any old native original and hydrate empty state without resurrecting it or uploading anything at startup. Other accounts, slots, guest/readonly previews and runtime origins are not searched or merged. Existing default-slot account storage keys remain unchanged. ## Reserved data and failure behavior `hackemist:progress:` and `hackemist:browser-recovery:` keys are excluded from browser-state hydration and cloud snapshots. Ordinary app `clear`/`removeItem` cannot erase those recovery keys. Managed-progress cache writes to its reserved namespace remain independent of native browser recovery. Archives retain at most eight distinct native originals and never evict one automatically. Invalid/full archives, local quota failures, missing stable identity and restoration failures stop hydration or syncing as appropriate, preserve the available originals and raise a persistent warning. Native method calls are fenced before mutation after rejected startup. A failed partial hydration attempts to restore the previous native dictionary; its durable backup remains available even if rollback itself hits a storage error. Network failures retain native work; revision conflicts fence retries rather than overwriting a newer server save. Unacknowledged writes are not reported as acknowledged. Native property assignments such as `localStorage.score = "0"` bypass `Storage.prototype.setItem` interception in actual browsers. The helper cannot reliably stop arbitrary creator code using those assignments or previously retained native method references. Such writes remain fenced from cloud while recovery is blocked; originals already archived remain intact, and the changed dictionary is preserved before the next hydration when storage permits. If backup creation itself fails, arbitrary property assignment can still mutate the unarchived native source. Apps requiring a strict write gate must use the supported managed-progress adapter or intercepted storage methods. This is an explicit integration limitation, not a claim that all hidden application state can be captured or protected automatically. ## Recovery interface The player sends a bound parent message to the running iframe: ```js {type: 'hackemist:browser-recovery', nonce, action: 'local' | 'cloud' | 'export'} ``` The helper requires exact parent-window source, configured control origin and launch nonce. Local/cloud choices persist the decision against the current cloud revision, restore the chosen native dictionary and reload before app memory can continue saving. If the server revision changes before reopening, the decision is stale and the player must choose again. Local import uses the existing cloud revision guard and is acknowledged only after the server accepts it. An explicit export replies with `hackemist:browser-recovery-result`, the bound nonce, `action: "export"`, `ok: true` and `recovery`. The export includes identity, project/slot, revision, current native/cloud dictionaries and the raw archive string. Combined export is bounded to 48 MiB of UTF-8 JSON. Failures return `ok: false`, a safe code and a message. No archive data appears in ordinary warnings, telemetry or operator logs. Apps can also use `window.__HACKEMIST_BROWSER_RECOVERY__`: - `export()` returns the identity-bound recovery object. Save it privately. - `list()` returns retained entry IDs, reasons and key counts. - `select("local", entryId?)` chooses the specified original, defaulting to the latest retained original. `select("cloud")` chooses the current launch snapshot. Both return `reload_required: true`; caller code must reopen/reload. - `removeOriginal(entryId)` explicitly removes that exported original. - `discardArchive(exactIdentity)` explicitly discards only the confirmed current archive, including a corrupt one, and requires reopening. Export first. No automatic cleanup or player action invokes this deletion method. These dictionary choices cannot validate arbitrary application-level schema or hidden game state. A declared pending format migration blocks local selection; use the app/Saves migration path and preserve originals before conversion. ## Deployment, regression evidence and rollback Deploy server browser-snapshot identity metadata, the autosave helper and player recovery controls together. An older server without identity fails closed for this helper. No existing player data is migrated, deleted or merged. Historical reserved data already in an account snapshot is not automatically destroyed; the updated helper does not hydrate or re-upload it. `node --test tests/autosave.spec.mjs` passed 22 VM contract tests, including quota/invalid/full archives, malformed responses, stale decisions, offline and conflict behavior, selected empty/reset state, forged parent messages and native lifecycle methods after rejected startup, oversize snapshots and permanent account write rejections. `tests/browser_autosave_slots.spec.mjs` passed seven actual local Chrome tests with real Storage behavior and intercepted synthetic account saves: separate slot origins/devices/accounts, explicit native adoption, reset/import, preserved divergent progress, reserved caches and the documented property-assignment limitation. These use no production player data and do not establish arbitrary game-state or live-backend correctness. Do not roll back by restoring clear-before-backup hydration or implicit native imports. If an interface regression requires a temporary rollback, retain the preservation and revision gates and leave unresolved recovery paused. Export current originals, inspect the selected account/slot and resolve deliberately; never clear site storage as a repair step. Monitor safe recovery codes and acknowledged revisions without collecting private dictionary values.