Files
rclone-jav/bugs-extension-popup.md
admin f7fc15b17c Sync working tree before initial Gitea push
Includes:
- cli.py path fix (parents[1]) for config/catalog resolution
- Library cleanup feature design docs (TODO.md, mockup)
- Audit + bug-queue markdowns from May 2026 reliability pass
- .gitignore expanded for transient artifacts
2026-05-26 22:35:42 +02:00

11 KiB
Raw Permalink Blame History

Bug Report — Extension Popup + Bulk Check — audit-snapshot-2026-05-24T15-55Z.md

Snapshot: audit-snapshot-2026-05-24T15-55Z.md Required-reading docs read: ext AGENTS.md / mockup / bug-audit-plan.md / project memory Auditor agent: fresh Explore agent (chunk 5 auditor) Verifier agents: fresh Explore agents per candidate, blind context, stricter contract-check prompt + UI-lifecycle rule + bulk-check window-lifecycle awareness

Chunk 5 calibration note: Moderate verification yielded 2 confirmed bugs + 2 demoted (M→L) with 60% combined downgrade rate (1/5 pure REFUTED, 2/5 demoted). Auditor's recurring weakness: flagging timing/race theories as M without checking whether (a) Chrome platform contract gates the race, (b) underlying action object reference stays correct despite cosmetic UI staleness, (c) user can recover with one click. Stricter prompt + UI-lifecycle rule + "self-correcting cosmetic" filter caught all 3. Light candidates were NOT verified per audit-plan stop condition (>30% rejection → halt L verification). See bugs-candidates-extension-popup.md for unverified L list (C-3, C-6, C-8, C-9, C-10).


Severe (S)

(none flagged by auditor in this chunk)


Cross-chunk re-rank note: Per bugs-fix-queue.md, this chunk's M-1 (Clear button leaves modal open) was demoted to Light because two-click recovery (Cancel button visible) makes it cosmetic + minor friction, not data loss or stuck workflow. M-2 (profile selector race) remains Moderate. Severity sections below reflect post-rerank placement.


Moderate (M)

M-5 (queue) — Profile selector race

  • File: D:\DEV\Extensions\Production\rclone-jav\src\popup\popup.js:605-625 (profile change handler) with runManualSearch at :443-475 and runCheck around :288-302
  • Symptom (one sentence): When the user switches profile while a manual search or auto-check is inflight, the handler fires a NEW search without canceling the OLD inflight RPC — if the old RPC's response arrives AFTER the new one (e.g., host was slow on first call, fast on second), the older Default-profile results render over the newer Other-profile results and stick until the user takes another action.
  • Why it's a bug: No AbortController, no request ID gating, no UI-side request tracking, no profile-selector-disable-while-busy. renderResult unconditionally overwrites lastResult and the DOM with whatever response arrives (lines 237-286). Results are persistent — there's no auto-refresh that would self-correct. User sees wrong-profile results until they manually act. Codex's rule: wrong-data persisting until user action ≠ cosmetic ≠ self-correcting → M.
  • Reproduction:
    1. User on Default profile, searches ABC-001. RPC#1 inflight (host happens to be slow, ~250ms).
    2. Before RPC#1 returns, user switches profile selector to "Other". Handler fires runManualSearch() for same query → RPC#2 inflight (host now fast, ~100ms).
    3. RPC#2 returns first → renderResult shows Other-profile results.
    4. RPC#1 returns later → renderResult overwrites with Default-profile results.
    5. User sees Default-profile results under "Other" selector. Stays wrong until next action.
  • Suggested fix sketch: track a monotonic currentSearchId in module scope. Each new search increments and captures its ID; callback compares its captured ID against the current — discards stale callbacks. Alternative: disable the profile selector + show busy indicator while inflight (simpler, but blocks legitimate fast follow-up actions).
  • Verifier agent: fresh Explore, blind context, stricter prompt + UI-lifecycle rule
  • Verifier verdict: CONFIRMED
  • Verifier confidence: high (95%)
  • Contract refs verifier read: runManualSearch / runCheck callback handling; profile change handler; renderResult lack of guards
  • Mirror check needed in: any other UI control that triggers fresh searches while old may be inflight (history chip clicks at lines 423-425, search clear's runCheck call — both share the same lack of request-ID gating)
  • Status: fixed
  • Fix: D:\DEV\Extensions\Production\rclone-jav\src\popup\popup.js:282-310 (runCheck) + :443-475 (runManualSearch) — added module-level monotonic _currentSearchId counter. Both functions bump it synchronously on entry (BEFORE the scanPaused early-exit, per follow-up below) and capture their own id; sendMessage callbacks compare myId !== _currentSearchId and bail before any UI write if a newer search has started. Stale callbacks neither call setStatus, renderResult, pushHistory, nor any other UI mutator — they return silently so the newer search's render is final. Mirror check resolved in same commit: every entry point that triggers a fresh search (history chip clicks, search clear, profile selector, search-go button, Enter key, pause-while-inflight) funnels through runCheck or runManualSearch, so the gate covers all paths automatically. Manifest bumped 0.1.39 → 0.1.40, then 0.1.40 → 0.1.41 (follow-up: moved bump to BEFORE paused early-exit so pause-while-inflight is also gated — same race class). JS syntax verified via node --check. Logic unit-tested in isolation: 5/5 cases correct.

Light (L)

L-6 (queue) — Search Clear button leaves delete modal open with stale state; no Esc support

Re-ranked from chunk M-1 to queue L-6.

  • File: D:\DEV\Extensions\Production\rclone-jav\src\popup\popup.js:475-490 (Search Clear handler) with modal lifecycle at :318-380
  • Symptom: When delete modal is open and user clicks Search Clear (×), the modal stays visible with stale confirm-input + chosenHit. No Esc-key handler exists. Only recovery is clicking the modal's Cancel button.
  • Why it's a bug (demoted from M to L): Clear handler resets only manualMode and search input. Modal close NOT invoked. BUT: delete RPC stays correct because chosenHit is reference to original hit (delete fires against right file). Two-click recovery via Cancel button. No data loss, no wrong action, no stuck workflow. Cosmetic + minor friction.
  • Reproduction:
    1. Search FILE-A; click hit; click DELETE button
    2. Click × (Search Clear) instead of Cancel
    3. Expected: modal closes
    4. Actual: modal stays visible; pressing Esc does nothing; click Cancel to recover
  • Suggested fix sketch: Clear handler invokes closeDeleteModal before runCheck; add document keydown listener for Esc closes modal
  • Verifier verdict: CONFIRMED — high confidence (95%)
  • Mirror check needed in: other modals (undo modal, profile modal) — do they have Esc support
  • Status: open

L-4 (queue, was chunk L-1) — expectedId not reset between delete-modal sessions; cosmetic state leak

  • File: D:\DEV\Extensions\Production\rclone-jav\src\popup\popup.js:318-380
  • Symptom (one sentence): Global expectedId persists across delete-modal open/close cycles — if user opens modal for FILE-A, cancels, then opens for FILE-B without first clicking a hit, the modal's confirm-input would validate against FILE-A's name briefly until selectHit(B) fires and updates it.
  • Why it's a bug (demoted from M to L): Originally flagged as M "stale expectedId allows wrong delete." Verifier traced the actual delete path: the DELETE RPC at line 382 uses chosenHit.full_path, not expectedId. chosenHit is reset to null in openDeleteModal (line 321), so any "type-and-confirm" attempt without re-clicking a hit returns early at if (!chosenHit) return; (line 379). No wrong file can be deleted. The only effect is expectedId text showing stale value briefly during the open→re-open transition.
  • Reproduction:
    1. Open delete modal for FILE-A, click FILE-A hit (selectHit sets expectedId="FILE-A.mp4")
    2. Cancel modal (close without delete)
    3. Open delete modal for FILE-B WITHOUT clicking any hit yet
    4. Expected: expectedId reset to empty string OR null; confirm-input disabled
    5. Actual: expectedId still "FILE-A.mp4"; confirm-input checks typed text against stale value. No delete possible (chosenHit is null) but state is inconsistent.
  • Suggested fix sketch: add expectedId = ""; to openDeleteModal at line 321
  • Verifier agent: fresh Explore, blind context, stricter prompt
  • Verifier verdict: PARTIAL — bug is real as state leak; severity over-stated by auditor
  • Verifier confidence: high (95%)
  • Contract refs verifier read: delete RPC gating; openDeleteModal init; chosenHit lifecycle
  • Mirror check needed in: none (popup-local state)
  • Status: open

L-5 (queue, was chunk L-2) — History chip click during open delete modal leaves modal floating over fresh results

  • File: D:\DEV\Extensions\Production\rclone-jav\src\popup\popup.js:423-425
  • Symptom (one sentence): Clicking a history chip while the delete modal is open fires runManualSearch() without closing the modal — fresh search results render behind the floating modal, which still shows the OLD search's hit selected for deletion.
  • Why it's a bug (demoted from M to L): Originally flagged as M. Verifier confirmed chosenHit is a reference to the ORIGINAL hit object (set at selectHit, line 347), so if user confirms typing in the stale modal, delete fires correctly against the original file. No wrong delete. Only effect: UI confusion + stale modal over fresh results. User dismisses modal via Cancel button → recovers. Pure cosmetic + minor friction.
  • Reproduction: open modal for FILE-A → click history chip for a different search → modal stays open over new results
  • Suggested fix sketch: close modal in the history chip click handler before invoking runManualSearch (or gate the chip clicks while modal open)
  • Verifier agent: fresh Explore, blind context, stricter prompt
  • Verifier verdict: PARTIAL — symptom real, severity over-stated
  • Verifier confidence: high (90%+)
  • Contract refs verifier read: chosenHit reference semantics; runManualSearch modal-state side-effects; renderResult scope
  • Mirror check needed in: Search Clear's similar issue (covered by M-1)
  • Status: open

Needs Input (N)

(none promoted)


False Positives (discarded)

  • src/popup/popup.js:563-565 (open-bulk-check fire-and-forget race) — flagged as Moderate "message lost if popup closes before IPC dispatch". REFUTED via Chrome runtime contract: chrome.runtime.sendMessage(...) queues the message in the runtime before the sender context unloads. window.close() cannot terminate the popup before the message is enqueued. Fire-and-forget pattern from a popup followed by window.close is documented-safe. Worst-case if anything went wrong: user clicks bulk-check icon again — fully retry-recoverable, not a permanent stuck state.