fix: reset summary panel on stop-scan [IDE-1035]#1324
Conversation
When the user pressed the stop-scan button (LSP window/workDoneProgress/cancel) the summary panel kept showing the last scan's counts. The org-change path already had a helper that reinitialises the panel to its initial state; this commit wires the same reset into the stop-scan handler. - New resetSummaryPanelOnStopScan helper delegates to the existing resetSummaryPanelForOrgChange with the current workspace folders. - windowWorkDoneProgressCancelHandler now invokes it after progress.Cancel. - Unit tests cover the happy path, nil-aggregator safety, and the no-folders case.
|
/describe |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
|
PR Description updated to latest commit (65ff59e) |
Lint catch: golangci-lint misspell wants the American spelling.
|
/describe |
|
PR Description updated to latest commit (89251fb) |
This comment has been minimized.
This comment has been minimized.
…utines exit [IDE-1035] Address PR #1324 review (D, E, F, G): D) The window/workDoneProgress/cancel handler reset the summary panel for every cancel, including CLI install/download trackers, wiping valid scan results. Introduce progress.NewScanTracker / IsScanToken so the handler can gate the reset on the token being a scan token. Update Code, IaC, OSS, and the code-client tracker factory to use the new constructor. E) The reset raced in-flight scan goroutines: progress.Cancel only signals over a channel, and a late SetScanDone could land after the synchronous Init reset, re-populating the panel. Move the reset into the scan lifecycle: DelegatingConcurrentScanner.RegisterCancelCallback stores a per-folder reset fn that Scan() consumes (LoadAndDelete) after both wait groups return — guaranteeing SetScanDone has already happened. New outcome-level test TestScan_CancelCallback_CalledAfterGoroutinesFinish asserts SetScanDone appears before the reset (Init) in the call sequence. F) The cancel handler discarded the `ok` from scanStateAggregatorFromContext, silently skipping the reset if the aggregator was missing. Log a Warn so wiring failures are observable (panic would be too aggressive for an LSP notification handler). G) resetSummaryPanelForOrgChange is now shared by org-change and stop-scan. Rename to resetSummaryPanel (and update callers/comments) so the name matches the responsibility. Tests: - TestIsScanToken_{ScanTracker,PlainTracker,Unknown,AfterCancel}_* - TestCancelProgress_NonScanToken_DoesNotResetAggregator - TestScan_CancelCallback_CalledAfterGoroutinesFinish - Renamed TestResetSummaryPanel_* (G)
This comment has been minimized.
This comment has been minimized.
- server.go: drop pre-Go-1.22 `fp := fp` (copyloopvar) - scanner.go: extract consumeCancelCallback so Scan's cyclomatic complexity stays under 15 (gocyclo); checked type assertion instead of `fn.(func())()` (forcetypeassert) - scanner.go: drop var-decl-with-type (`var a, b int = -1, -1` → `a, b := -1, -1`) in the new outcome test (staticcheck QF1011) - notification_test.go, scanner_test.go, progress_test.go: cancelled→canceled, recognised→recognized (misspell, repo prefers en_US)
…s first [IDE-1035] Defer the cancel call in windowWorkDoneProgressCancelHandler so the RegisterCancelCallback loop runs before scan goroutines observe the cancel and drain through consumeCancelCallback. Otherwise the reset can be silently dropped and a stale callback fires on the folder's next scan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pper [IDE-1035] The wrapper had no production callers — only three TestResetSummaryPanel OnStopScan_* tests exercised it. The real stop-scan handler in windowWorkDoneProgressCancelHandler calls resetSummaryPanel directly, so the wrapper plus its dedicated tests gave false coverage of a path that never ran in production. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ype the registry, cover handler [IDE-1035] Address the application/server ↔ domain/snyk/scanner layering and type-safety feedback from PR review: - Lift RegisterCancelCallback(folderPath, fn) onto scanner.Scanner so the cancel handler calls it through the interface — drops the *DelegatingConcurrentScanner downcast and the racy synchronous summary-panel reset that used to fire when the type assertion failed. TestScanner gets a no-op implementation since it doesn't run the cancel-drain cycle. - Replace the cancelCallbackMap = sync.Map alias with a plain map[types.FilePath]func() guarded by a sync.Mutex. Drops the defensive v.(func()) assertion in consumeCancelCallback and keeps values typed; the access pattern (one register + one consume per folder) doesn't need sync.Map's machinery. - Extract the handler body into handleWindowWorkDoneProgressCancel so the new cancel_handler_test.go can drive it without going through jrpc2 indirection. Tests cover scan-token register-then- cancel ordering, non-scan-token cancel-without-register, and the no-scanner skip path (proving the racy sync fallback is gone). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rWithResolverAndAgg [IDE-1035] setupScannerWithResolver and setupScannerWithResolverAndAgg duplicated the full NewDelegatingScanner construction; the only difference was an explicit aggregator parameter. Have the former delegate to the latter with NewNoopStateAggregator as the default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…field [IDE-1035] The parallel scanTokens map (and its mutex) had to be kept in sync with the trackers map in three places — deleteTracker, Cancel, and CleanupChannels — so any future tracker-removal path that forgot the second mutation would leak or stale the scan-token state. Model "is scan" as an isScan bool field on Tracker. IsScanToken becomes a single trackers lookup under trackersMutex.RLock and returns false once the tracker entry is gone, so removal logic only has to touch trackers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…mmary-on-stop-scan
This comment has been minimized.
This comment has been minimized.
- gofmt -w internal/progress/progress.go: the new isScan field's preceding comment split the Tracker struct's alignment group; reflow the pre-comment fields to the gofmt-canonical narrow alignment. - recognised → recognized in cancel_handler_test.go to satisfy golangci-lint's misspell checker. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
PR Reviewer Guide 🔍
|
| Msg("scanner not in context; summary panel will not be reset") | ||
| return nil, nil | ||
| } | ||
| for _, fp := range workspaceFolderPaths(conf) { |
There was a problem hiding this comment.
Should fix — cancel resets every folder, not the cancelled one. This loop registers a reset callback for every workspace folder, but params.Token identifies a single per-product tracker belonging to one folder. In a multi-folder workspace, cancelling folder A's scan also registers reset callbacks on folders B, C… Those callbacks are not consumed now (no in-flight scan there) and instead fire on each of those folders' next Scan() completion (scanner.go consumeCancelCallback), wiping valid results the user never asked to clear. Consider mapping the cancelled token back to its owning folder and registering the reset only for that folder.
(Three of the four reviewers independently flagged this as the root cause of a cluster of data-loss bugs.)
— AI review
| // for this folder (IDE-1035). Consuming the callback here — after the | ||
| // WaitGroups — guarantees the aggregator reset cannot race with in-flight | ||
| // SetScanDone calls. | ||
| sc.consumeCancelCallback(folderPath) |
There was a problem hiding this comment.
Should fix — consume is unconditional and unreachable on early returns. Two related issues:
-
consumeCancelCallback(folderPath)fires at the end of everyScan()with no check that this scan was cancelled. A callback registered during a cancel therefore also resets the panel after the folder's next successful scan — turning "reset on cancel" into "reset on next scan completion", wiping valid results. -
This call sits after the WaitGroups, past several early returns in
Scan()(missing FolderConfig, offline, not authenticated, already-cancelled ctx). If the next thing that happens to the folder hits one of those guards, the registered callback is neither fired nor cleared — it lingers and fires against a still-later scan.
Consider draining/clearing the folder's callback in a defer at the top of Scan() (after folderPath is known) so every exit path consumes it exactly once, and gate the reset on whether the scan was actually cancelled.
— AI review
Reviewed What's solid: The core IDE-1035 race fix is correct and well-tested. Scan-token scoping ( Should fix (data-loss cluster, one root cause): The cancel token carries no folder identity, and the reset is keyed to a future
Open question for the author: does the IDE client send one Suggestions (non-blocking):
Coverage gap: no test exercises the stale/orphaned callback paths — e.g. register a callback for a not-scanning folder, run a fresh successful scan, and assert the panel is not wiped; and drive Security: no findings introduced. CI is currently red — please confirm failures are unrelated before merge. — AI review |
User description
Summary
resetSummaryPanelOnStopScanhelper reuses the existingresetSummaryPanelForOrgChangereset path.windowWorkDoneProgressCancelHandlerinvokes it afterprogress.Cancel, so pressing the stop-scan button now clears the summary back to its initial state instead of leaving stale counts on screen.Why
When the user pressed stop-scan, the summary panel kept showing the previous scan's counts even though the scan was cancelled. The org-change flow already had a helper to reset the panel to initial state — this PR wires the same helper into the stop-scan LSP handler (
window/workDoneProgress/cancel).Test plan
TestResetSummaryPanelOnStopScan_CallsInitTestResetSummaryPanelOnStopScan_NilAgg_DoesNotPanicTestResetSummaryPanelOnStopScan_NoFolders_DoesNotCallInitJira: IDE-1035
Triage: Buglist
PR Type
Bug fix, Tests
Description
Fixes stale summary panel counts after scan stop.
Introduces helper to reset summary panel state.
Integrates reset logic into scan cancellation handler.
Adds unit tests for reset functionality verification.
Diagram Walkthrough
File Walkthrough
configuration.go
Add helper to reset summary panelapplication/server/configuration.go
resetSummaryPanelOnStopScanfunction.to
resetSummaryPanelForOrgChange.state.
configuration_test.go
Add tests for summary panel resetapplication/server/configuration_test.go
resetSummaryPanelOnStopScan.Init, handling a nil aggregatorwithout panicking, and not calling
Initwhen no folders are present.server.go
Integrate summary panel reset into cancel scan handlerapplication/server/server.go
windowWorkDoneProgressCancelHandlerto acceptconfiguration.Configuration.resetSummaryPanelOnStopScanafter cancellingprogress, ensuring the summary panel is cleared.