Context
apps/loopover-miner-extension/options.js's manual-paste flow validates a pasted rankedCandidates payload against a byte-size quota before writing to chrome.storage.local (lines 9-24: measures real serialized UTF-8 byte size via TextEncoder, not the UTF-16 .length, per bug #4863 — "an unbounded paste can silently fail to save or leave storage in a partial state").
apps/loopover-miner-extension/background.js's syncRankedCandidatesFromMinerUi (the live-fetch replacement for manual paste, #4859) writes to the same chrome.storage.local keys (rankedCandidates/rankedCandidatesSavedAt, line 121) with no equivalent size check on the fetched candidates array before writing — the exact #4863 failure mode (silent partial save past the 10 MiB QUOTA_BYTES shared-across-keys limit) can still happen via this path, it just was never guarded here.
Requirements
- Apply the same byte-size validation
options.js already performs (measuring real UTF-8 byte size via TextEncoder, comparing against the same MAX_RANKED_CANDIDATES_JSON_BYTES constant) to syncRankedCandidatesFromMinerUi's fetched candidates before the chrome.storage.local.set(...) call.
- On an over-quota fetch result, return the module's existing typed
{ ok: false, error, minerUiUrl } shape (matching every other failure branch in this function) rather than throwing — per this function's own documented "never throws" contract.
- Reuse the existing size-check logic/constant from
options.js rather than duplicating the byte-measurement approach with different logic.
Deliverables
Test Coverage Requirements
apps/** extension code — check whether this repo's coverage.include covers apps/loopover-miner-extension; if so, 99%+ Codecov patch coverage plus the regression test above; if excluded, the regression test alone suffices.
Expected Outcome
The live-fetch sync path can no longer silently write a partial/corrupted rankedCandidates payload past the shared 10 MiB storage quota — it fails closed with a typed error, matching the manual-paste flow's existing protection.
Links & Resources
apps/loopover-miner-extension/options.js:9-24 — the existing byte-size guard (precedent to reuse). apps/loopover-miner-extension/background.js:107-124 (syncRankedCandidatesFromMinerUi) — the unguarded write path.
Context
apps/loopover-miner-extension/options.js's manual-paste flow validates a pastedrankedCandidatespayload against a byte-size quota before writing tochrome.storage.local(lines 9-24: measures real serialized UTF-8 byte size viaTextEncoder, not the UTF-16.length, per bug #4863 — "an unbounded paste can silently fail to save or leave storage in a partial state").apps/loopover-miner-extension/background.js'ssyncRankedCandidatesFromMinerUi(the live-fetch replacement for manual paste, #4859) writes to the samechrome.storage.localkeys (rankedCandidates/rankedCandidatesSavedAt, line 121) with no equivalent size check on the fetchedcandidatesarray before writing — the exact#4863failure mode (silent partial save past the 10 MiBQUOTA_BYTESshared-across-keys limit) can still happen via this path, it just was never guarded here.Requirements
options.jsalready performs (measuring real UTF-8 byte size viaTextEncoder, comparing against the sameMAX_RANKED_CANDIDATES_JSON_BYTESconstant) tosyncRankedCandidatesFromMinerUi's fetchedcandidatesbefore thechrome.storage.local.set(...)call.{ ok: false, error, minerUiUrl }shape (matching every other failure branch in this function) rather than throwing — per this function's own documented "never throws" contract.options.jsrather than duplicating the byte-measurement approach with different logic.Deliverables
syncRankedCandidatesFromMinerUivalidates fetched candidates against the same byte-size quotaoptions.jsenforces{ ok: false, ... }instead of writing a partial payloadTest Coverage Requirements
apps/**extension code — check whether this repo'scoverage.includecoversapps/loopover-miner-extension; if so, 99%+ Codecov patch coverage plus the regression test above; if excluded, the regression test alone suffices.Expected Outcome
The live-fetch sync path can no longer silently write a partial/corrupted
rankedCandidatespayload past the shared 10 MiB storage quota — it fails closed with a typed error, matching the manual-paste flow's existing protection.Links & Resources
apps/loopover-miner-extension/options.js:9-24— the existing byte-size guard (precedent to reuse).apps/loopover-miner-extension/background.js:107-124(syncRankedCandidatesFromMinerUi) — the unguarded write path.