Scan resilience + live progress + actionable sign-in + version footer (v1.0.4)#1
Merged
Merged
Conversation
…ss + history reconcile Make the MV3 connection scan survive the service worker being torn down mid-scan (closing the popup was its only keepalive), surface progress, and stop diverging from the server's recorded syncs. E1/E3 — checkpoint-and-resume: - fetch-engine `scanConnections` gains `startAt`/`initialItems` (resume) and an `onPage(items, nextStart)` checkpoint hook fired after EACH page (incl. the cap-reached page). Stays pure/injectable; passes a fresh snapshot per tick. - service-worker: new resumable `continueScan()` reads the persisted checkpoint, fetches from the cursor, checkpoints to storage per page, and on the last/short page or the per-session cap finalizes (pendingScan, clears scan state, clears the keepalive tick, opens the handoff tab). Module-level re-entrancy guard so a tick-triggered resume can't double-fetch. Lost-session → needs:network-signin + clears scan state. `runScan` is now a thin init+continue wrapper (monthly alarm + scanNow paths preserved). - new State fields: scanInProgress, scanCursor, scanItems, scanStartedAt (+ KEYS). Keepalive + auto-resume: - scanNow arms a 0.5-min `scan-tick` alarm then runs continueScan inline. The tick resumes an in-progress, non-stale scan (drops a >1h-old zombie). On SW startup, an interrupted non-stale scan re-arms the tick so it resumes. E2 — progress: - getStatus returns scanning + scannedCount. The popup polls getStatus (~1.5s, injectable) and shows "scanned N…" while scanning; spinner kept. E5 — reconcile: - getSyncHistory reconciles local state from the newest extension run (stamps lastScanAt/lastScanCount when local lags, clears a stale noticed-signin). The popup derives the last-sync line from the newest run so it never shows "no sync yet" while a recorded sync is listed. Recipe-driven; no platform-name literals in shipped code (purity guard green). Test infra: chrome mock + chrome.d.ts get alarms.clear; add @types/node so the pre-existing popup test's node:fs/path imports typecheck. 73/73 tests pass, tsc --noEmit clean. (Run under Node 22, matching CI; Node 20 hits an unrelated jsdom ERR_REQUIRE_ESM.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZEx7hFyqqrzeptpN85TLc
E4 — make the Sign-in warning actionable. The popup had two disagreeing sign-in signals: render() set red "finish syncing" text from status.needs, but the #signin-cta button was only revealed when getSyncHistory returned history.needs === "noticed-signin" (a 401). A user could see the red text with no button (status.needs was noticed-signin while the history fetch returned 200). Unify into a single source of truth: applyNoticedSigninState reveals + wires the button whenever EITHER signal needs a noticed sign-in, and clears the dangling red text so it never appears without the button. network-signin keeps its informative (non-actionable) text. E6 — add a persistent version footer at the bottom of the panel that ALWAYS shows the installed manifest version (e.g. "v1.0.3"), and move the "update available" notice into that footer (revealed by checkForUpdate when behind, linking to LATEST_DOWNLOAD_URL). setVersion() is called in init() alongside checkForUpdate. No platform-name literals — purity guard stays green. TDD: extended popup.test.ts (E4 button from either signal + network-signin path + E6 always-on version + bottom update notice). All 80 tests pass; tsc --noEmit clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZEx7hFyqqrzeptpN85TLc
Scan resilience (checkpoint-and-resume + keepalive), live progress, unified actionable Sign-in, server-history reconcile, and a version/update footer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MVRtK6RQbp12pHMV9dgA6Q
…d tab, close on syncConfirmed
The post-scan handoff tab now opens with active:false so it never steals
focus on the monthly auto-scan, and its tab id is persisted (syncTabId).
The syncConfirmed handler closes that background tab (guarded + best-effort
via chrome.tabs.remove(...).catch(()=>{})) once the POST + recipe refresh
have run, then clears syncTabId so a later scan starts clean.
No new permission needed — tabs.create/tabs.remove work without "tabs".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZEx7hFyqqrzeptpN85TLc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the extension issues from Filipe's June-23 testing (scan thread + screenshots).
E1 — scan survives popup close / SW teardown
runScanpreviously paginated in memory withsetTimeoutsleeps and only persisted at the end, so MV3 killed the service worker mid-scan (closing the popup removed its keepalive) and lost everything → "failed silently". Now the scan is checkpoint-and-resume: progress is written tochrome.storageafter every page, a shortscan-tickkeepalive alarm re-drives it, and an interrupted non-stale scan auto-resumes on SW startup. A torn-down SW loses at most the in-flight page. Re-entrancy guard + 1h stale cutoff.E2 — live progress
getStatusnow returnsscanning+scannedCount; the popup polls and shows "scanned N…" (survives closing/reopening the popup mid-scan), replacing the bare "scanning…" spinner.E3 — completeness
The resume + the popup state-reconcile (E5) fix the "stuck on 2367" report (that account was under the cap; the scan had died partial and the popup showed "no sync yet"). The per-session page cap (
maxPagesPerSession) is server-side and is raised separately in the monorepo (60→250, 2.4k→10k) for >2,400-connection networks.E4 — actionable Sign-in
The red "sign in to noticed to finish syncing" used to render as text while the clickable button was gated on a different signal, so it could show with no button. Now a single source of truth reveals + wires the
#signin-ctabutton whenever a noticed sign-in is needed (from either signal); the text never dangles alone.E5 — reconcile with the server
The popup showed "no sync yet" while "recent syncs" listed a recorded sync (the
syncConfirmedmessage can be lost if the SW dies). Now both the SW (getSyncHistory) and the popup reconcile local state from/api/sync/runs: stamplastScanAt/count from the newest extension run and clear a stalenoticed-signin.E6 — version + update footer
A persistent installed-version indicator (
v1.0.4) plus the "update available" notice, both moved to a bottom footer.Verification
vitest run: 80 passed (7 files; +new tests for resume/checkpoint/progress/reconcile/sign-in/footer, TDD).tsc --noEmit: clean.991a5658…).Pre-release Helium smoke test pending. Tag
v1.0.4triggers the release workflow once merged.🤖 Generated with Claude Code