fix: BrainBar stability — async store, dates, Enter key, popover#161
Conversation
…er size Five QA fixes from user testing after overnight sprint PRs #155-#160: (1) Store freezes UI: Added storeAsync() — runs DB write on background DispatchQueue via withCheckedThrowingContinuation. QuickCapture submitCapture now uses async path, UI stays responsive. (2) Search results missing dates: SearchQueryCandidate now includes date, project, importance fields. searchCandidates SQL extended to SELECT created_at, project, importance. SearchViewModel passes these through to SearchResult for display. (3) Enter in search goes to capture: Changed applySelectedSearchResult() to copy result to clipboard instead of switching to capture mode. Also auto-selects first result when selectedResultIndex is nil. (4) Popover oversized: Reduced contentSize from 420x620 to 360x320. (5) VoiceBar duplicate struct: Merged voicelayer PR #109. Also: Updated busy_timeout test to match PR #160's 30s value. 212 tests, 0 failures. Build and sign pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 26 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (8)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| @@ -101,6 +101,8 @@ final class QuickCaptureViewModel: ObservableObject { | |||
| private let panelState: QuickCapturePanelState | |||
| private let clipboard: QuickCaptureClipboard | |||
| private var copyResetTask: Task<Void, Never>? | |||
There was a problem hiding this comment.
🟡 Medium BrainBar/QuickCapturePanel.swift:103
Assigning a new Task to _pendingStoreTask without cancelling the previous task causes duplicate entries when submitCapture is called rapidly. Both tasks run concurrently and each calls db.storeAsync, and they race to mutate inputText and feedback, producing unpredictable UI state. Consider cancelling the previous task with _pendingStoreTask?.cancel() before assigning the new one, and checking Task.isCancelled before mutating state.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file brain-bar/Sources/BrainBar/QuickCapturePanel.swift around line 103:
Assigning a new `Task` to `_pendingStoreTask` without cancelling the previous task causes duplicate entries when `submitCapture` is called rapidly. Both tasks run concurrently and each calls `db.storeAsync`, and they race to mutate `inputText` and `feedback`, producing unpredictable UI state. Consider cancelling the previous task with `_pendingStoreTask?.cancel()` before assigning the new one, and checking `Task.isCancelled` before mutating state.
Evidence trail:
brain-bar/Sources/BrainBar/QuickCapturePanel.swift lines 279-305 (REVIEWED_COMMIT): `submitCapture` function assigns `_pendingStoreTask = Task { @MainActor in` at line 287 without calling `_pendingStoreTask?.cancel()` first. The task calls `db.storeAsync()` at line 289, then mutates `inputText` at line 295 and `feedback` at line 297.
git_diff base=MERGE_BASE head=REVIEWED_COMMIT: Shows PR introduces `_pendingStoreTask` (lines 104-105) and the Task assignment (line 287) without cancellation, while the same PR adds proper `copyResetTask?.cancel()` pattern elsewhere (around line 350).
brain-bar/Sources/BrainBar/QuickCapturePanel.swift lines 253-258 (REVIEWED_COMMIT): `copyResultToClipboard` demonstrates the proper pattern: `copyResetTask?.cancel()` before `copyResetTask = Task { ... }` with `guard !Task.isCancelled else { return }` check.
|
|
||
| final class PopoverSizeTests: XCTestCase { | ||
|
|
||
| func testStatusPopoverViewFrameIsCompact() { |
There was a problem hiding this comment.
🟢 Low BrainBarTests/StabilityFixTests.swift:152
testStatusPopoverViewFrameIsCompact creates a hardcoded NSRect with width 360 and height 320, then asserts both dimensions are ≤ 360 and ≤ 320. These assertions always pass because the values are identical, so the test cannot detect whether StatusPopoverView actually exceeds size constraints in production.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file brain-bar/Tests/BrainBarTests/StabilityFixTests.swift around line 152:
`testStatusPopoverViewFrameIsCompact` creates a hardcoded `NSRect` with width 360 and height 320, then asserts both dimensions are ≤ 360 and ≤ 320. These assertions always pass because the values are identical, so the test cannot detect whether `StatusPopoverView` actually exceeds size constraints in production.
Evidence trail:
brain-bar/Tests/BrainBarTests/StabilityFixTests.swift lines 151-157 at REVIEWED_COMMIT - shows the test creates `let frame = NSRect(x: 0, y: 0, width: 360, height: 320)` and then asserts `XCTAssertLessThanOrEqual(frame.width, 360)` and `XCTAssertLessThanOrEqual(frame.height, 320)`, which always pass since 360<=360 and 320<=320.
Summary
Five QA fixes from user testing after overnight sprint PRs #155-#160:
storeAsync()runs DB writes off main thread viaDispatchQueue.global. QuickCapturesubmitCaptureuses async path. Exposed_pendingStoreTaskfor test awaiting.SearchQueryCandidatenow carriesdate,project,importance. SQL extended.SearchViewModelpasses metadata through toSearchResult.applySelectedSearchResult()now copies to clipboard instead of switching modes. Auto-selects first result when none highlighted.contentSizereduced from 420×620 to 360×320.Test plan
swift test --package-path brain-bar— 212 tests, 0 failuresBRAINBAR_APP_DIR=/Applications/BrainBar.app bash brain-bar/build-app.sh— builds, signs🤖 Generated with Claude Code
Note
Fix BrainBar stability with async store, Enter key copy behavior, and compact popover
BrainDatabase.storeAsyncto write captured content off the main thread usingwithCheckedThrowingContinuationandDispatchQueue.global, replacing the synchronous capture path inQuickCaptureViewModel.submitCapture.SearchQueryCandidatewithdate,project, andimportancefields, populated from the database result set and mapped through toSearchResultfor display.AppDelegate.configureStatusItem.applySelectedSearchResultno longer switches to capture mode; it copies content to the clipboard and stays in search mode.📊 Macroscope summarized 18faff5. 8 files reviewed, 4 issues evaluated, 0 issues filtered, 2 comments posted
🗂️ Filtered Issues