fix: a change made while an earlier save from the same page is still running is never stored #448

Closed
opened 2026-10-05 03:36:29 +02:00 by clawbot · 1 comment
Collaborator

saveStateOnce() in src/shared/state.js takes its snapshot when it starts, writes that snapshot, and then sets baseline (what the next save compares against) from the page's state as it is after the write. A change made while that save was waiting on storage ends up in the new baseline but not in what was written, so the next save sees nothing changed and keeps the stored value. The change is lost with no error; the page keeps showing it until the popup is reopened.

Reproduced in a unit test against the real module over tests/support/storageStub.js: save theme dark, and from inside that save's storage read set networkId to mainnet and call saveState(). After both saves, storage holds dark and sepolia while the page's state holds mainnet.

Where it bites: showView() saves on every navigation without waiting, so a Settings switch made just after Settings opens, or just after the popup reopens, can land inside that save and be dropped. The e2e failure in #446 (network back on sepolia after the restore) can come from this as well as from the close that issue describes. Introduced with the per-field merge for #304.

Unverified: wallets, allowedSites, deniedSites and networkEndpoints are compared against the same baseline, so a change to them made while the write is in flight should be dropped the same way.

Done when a change made while an earlier save is still running is stored by the save it queued, with a unit test that makes the change from inside the earlier save.

Model: opus-5-5

`saveStateOnce()` in `src/shared/state.js` takes its snapshot when it starts, writes that snapshot, and then sets `baseline` (what the next save compares against) from the page's state as it is after the write. A change made while that save was waiting on storage ends up in the new baseline but not in what was written, so the next save sees nothing changed and keeps the stored value. The change is lost with no error; the page keeps showing it until the popup is reopened. **Reproduced** in a unit test against the real module over `tests/support/storageStub.js`: save theme `dark`, and from inside that save's storage read set `networkId` to `mainnet` and call `saveState()`. After both saves, storage holds `dark` and `sepolia` while the page's state holds `mainnet`. **Where it bites:** `showView()` saves on every navigation without waiting, so a Settings switch made just after Settings opens, or just after the popup reopens, can land inside that save and be dropped. The e2e failure in https://git.eeqj.de/sneak/AutistMask/issues/446 (network back on `sepolia` after the restore) can come from this as well as from the close that issue describes. Introduced with the per-field merge for https://git.eeqj.de/sneak/AutistMask/issues/304. Unverified: `wallets`, `allowedSites`, `deniedSites` and `networkEndpoints` are compared against the same baseline, so a change to them made while the write is in flight should be dropped the same way. **Done when** a change made while an earlier save is still running is stored by the save it queued, with a unit test that makes the change from inside the earlier save. Model: opus-5-5
Author
Collaborator

Fixed in #450: a save now keeps the copy of the page's fields it wrote from as its baseline, so a change made while it runs is stored by the save queued after it.

The unverified claim holds for changes made during the earlier save's write: a wallet added there was dropped from storage, and an origin revoked there stayed allowed. During the read, those four fields were already written; only replaced settings such as networkId were lost there.

Model: opus-5-5

Fixed in https://git.eeqj.de/sneak/AutistMask/pulls/450: a save now keeps the copy of the page's fields it wrote from as its baseline, so a change made while it runs is stored by the save queued after it. The unverified claim holds for changes made during the earlier save's write: a wallet added there was dropped from storage, and an origin revoked there stayed allowed. During the read, those four fields were already written; only replaced settings such as `networkId` were lost there. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#448