Repository navigation
feat(history): add quick copy and hide silent taps - #85
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
📝 WalkthroughWalkthroughThe PR classifies capture signals, persists session-history visibility, filters short absolute-silence sessions, prevents stale history updates, and adds guarded copy controls for session text. ChangesHistory visibility and session controls
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
crates/lumen-store/src/lib.rs (1)
1050-1067: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winCorrupted metrics JSON is silently discarded.
map_session_historyuses.ok()to drop theserde_json::from_strerror formetrics_json, so a corrupted or unreadable metrics blob silently producesinitial_attempt: Nonewith no trace. This differs frommap_attempt(line 1075), which surfaces arusqlite::Error::FromSqlConversionFailurefor the same kind of malformed JSON. The fallback path is safe (the session stays visible rather than being wrongly hidden), but a corrupted record passes unnoticed, which makes diagnosing bad data harder.Add a
tracing::warn!on the parse failure before falling back toNone, so genuinely corrupted rows are observable.🩹 Proposed fix to log a parse failure
let initial_attempt = metrics_json - .and_then(|json| serde_json::from_str::<PipelineMetrics>(&json).ok()) + .and_then(|json| { + serde_json::from_str::<PipelineMetrics>(&json) + .inspect_err(|error| { + tracing::warn!(%error, "failed to parse initial-attempt pipeline metrics") + }) + .ok() + }) .map(|pipeline_metrics| SessionHistoryAttempt {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lumen-store/src/lib.rs` around lines 1050 - 1067, Update map_session_history so serde_json::from_str failures for metrics_json emit a tracing::warn! containing the parse error before falling back to initial_attempt: None. Preserve the existing safe fallback and successful parsing behavior, and do not change the row-mapping error contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop/src/App.tsx`:
- Around line 3063-3101: Update the raw-text copy button handler near the
existing copy controls to use showCopyFeedback instead of setting copied and
creating its own window.setTimeout. Preserve the raw-copy behavior while
ensuring its feedback timer is tracked by copyFeedbackTimerRef and shared with
copySession, so only the latest copy action controls copied state.
In `@crates/lumen-store/src/lib.rs`:
- Around line 667-697: Add the `idx_dictation_attempts_by_session_ordinal` index
to the database schema or migration for `dictation_attempts`, covering
`session_id` followed by `attempt_ordinal` in ascending order. Keep
`list_history_sessions` unchanged so its correlated earliest-attempt lookup can
use this index.
---
Nitpick comments:
In `@crates/lumen-store/src/lib.rs`:
- Around line 1050-1067: Update map_session_history so serde_json::from_str
failures for metrics_json emit a tracing::warn! containing the parse error
before falling back to initial_attempt: None. Preserve the existing safe
fallback and successful parsing behavior, and do not change the row-mapping
error contract.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 64ff1cf3-08b4-4163-956d-d2a911b22fc9
📒 Files selected for processing (5)
apps/desktop/src-tauri/src/commands.rsapps/desktop/src-tauri/src/dictation.rsapps/desktop/src/App.tsxapps/desktop/src/styles.csscrates/lumen-store/src/lib.rs
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
crates/lumen-store/src/lib.rs (1)
20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShare the 2-second threshold with the v14 migration.
HIDDEN_SILENT_CAPTURE_MAX_MSdefines the runtime hide threshold. The v14 backfill incrates/lumen-store/src/schema.rsat line 564 repeats the same value as the literal2_000. If one value changes, migrated rows and newly written rows classify differently for the same evidence. Makeschema.rsuse this constant, or move the constant intoschemaand import it here.♻️ Proposed change in crates/lumen-store/src/schema.rs
// crates/lumen-store/src/schema.rs if metrics.audio_duration_ms >= super::HIDDEN_SILENT_CAPTURE_MAX_MS { continue; }Make
HIDDEN_SILENT_CAPTURE_MAX_MSpub(crate)incrates/lumen-store/src/lib.rs.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lumen-store/src/lib.rs` at line 20, Share the hidden silent-capture threshold between runtime classification and the v14 migration: make HIDDEN_SILENT_CAPTURE_MAX_MS accessible within the crate, then update the v14 backfill logic in schema.rs to compare against that constant instead of the duplicated 2_000 literal.crates/lumen-store/src/schema.rs (1)
507-581: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winWrap the v14 backfill in one transaction.
The backfill runs one autocommitted
UPDATEper hidden session. On a database with many silent captures, this produces one commit (and one WAL fsync) per row, so migration time grows with the number of hidden rows. A single transaction around the whole loop also makes the backfill atomic with the version marker at line 587, which removes the partial-backfill state after an interrupted upgrade.The loop itself terminates correctly: the keyset cursor
sessions.id > ?2advances strictly, and the predicate does not depend onhistory_visible.♻️ Proposed change: single transaction for the backfill and the version marker
if !has_v14 { + let backfill = conn.unchecked_transaction()?; let mut after_session_id = String::new(); loop { let candidates = { - let mut statement = conn.prepare( + let mut statement = backfill.prepare( r#"Apply the same substitution to the
UPDATE sessions SET history_visible=0call, then commit the transaction after the loop.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lumen-store/src/schema.rs` around lines 507 - 581, Wrap the entire !has_v14 backfill in a single transaction, beginning before the pagination loop and keeping all candidate queries and history_visible updates within it. Update the existing UPDATE sessions call to execute through the transaction, then commit only after the loop completes and immediately before the v14 version marker is written, preserving atomicity between the backfill and marker.apps/desktop/src-tauri/src/dictation.rs (1)
362-379: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePersist a consistent message form for both capture issues.
record_capture_signal_issuestores the localizedABSOLUTE_SILENCE_MESSAGEforAbsoluteSilence, but stores the machine tokenINVALID_CAPTURE_ISSUEforInvalidSignal.CaptureSignalIssue::message()returnsINVALID_CAPTURE_MESSAGEfor the same variant, so the persisted message and the user-facing message diverge for one variant only. Choose one convention forPipelineStageIssue::message: either both localized, or both stable machine tokens with the localized text kept in the returned error.♻️ One option: store stable tokens for both kinds
fn record_capture_signal_issue(attempt: &mut DictationAttemptRecord, issue: CaptureSignalIssue) { let (kind, message) = match issue { CaptureSignalIssue::AbsoluteSilence => { - (PipelineIssueKind::AbsoluteSilence, ABSOLUTE_SILENCE_MESSAGE) + (PipelineIssueKind::AbsoluteSilence, ABSOLUTE_SILENCE_ISSUE) } CaptureSignalIssue::InvalidSignal => { (PipelineIssueKind::InputUnavailable, INVALID_CAPTURE_ISSUE) } };Note: the test at lines 2142-2151 and the
PipelineIssueKind-based filtering incrates/lumen-store/src/schema.rswould need the same token update.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src-tauri/src/dictation.rs` around lines 362 - 379, Make record_capture_signal_issue persist a consistent message convention for both CaptureSignalIssue variants: use stable machine tokens for AbsoluteSilence and InvalidSignal, or localized messages for both. Keep CaptureSignalIssue::message() aligned with the chosen convention, and update the related test and PipelineIssueKind-based filtering in schema.rs if changing the stored token.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop/src/latestRequestGate.ts`:
- Around line 11-25: Update LatestRequestGate.run to detect when the in-flight
cycle’s revision was superseded by cancelPending() before returning the existing
cycle; only coalesce onto this.cycle when it still represents the current
revision, otherwise start a new cycle for the newly supplied request. Add
coverage for run(), cancelPending(), then run() before the first request
settles, asserting the second result is current.
In `@apps/desktop/tests/latestRequestGate.test.ts`:
- Around line 1-3: Update the apps/desktop test script to run Node’s test runner
with --experimental-strip-types so TypeScript test imports load under the CI
Node 22 version. Apply this root-cause fix for
apps/desktop/tests/latestRequestGate.test.ts (lines 1-3),
apps/desktop/tests/sessionText.test.ts (lines 1-3), and
apps/desktop/tests/clipboardWriteGate.test.ts (lines 1-3); no direct changes are
needed in those test files.
---
Nitpick comments:
In `@apps/desktop/src-tauri/src/dictation.rs`:
- Around line 362-379: Make record_capture_signal_issue persist a consistent
message convention for both CaptureSignalIssue variants: use stable machine
tokens for AbsoluteSilence and InvalidSignal, or localized messages for both.
Keep CaptureSignalIssue::message() aligned with the chosen convention, and
update the related test and PipelineIssueKind-based filtering in schema.rs if
changing the stored token.
In `@crates/lumen-store/src/lib.rs`:
- Line 20: Share the hidden silent-capture threshold between runtime
classification and the v14 migration: make HIDDEN_SILENT_CAPTURE_MAX_MS
accessible within the crate, then update the v14 backfill logic in schema.rs to
compare against that constant instead of the duplicated 2_000 literal.
In `@crates/lumen-store/src/schema.rs`:
- Around line 507-581: Wrap the entire !has_v14 backfill in a single
transaction, beginning before the pagination loop and keeping all candidate
queries and history_visible updates within it. Update the existing UPDATE
sessions call to execute through the transaction, then commit only after the
loop completes and immediately before the v14 version marker is written,
preserving atomicity between the backfill and marker.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ef081bfa-fae7-4821-a5a1-5484a1bb6f4f
📒 Files selected for processing (13)
apps/desktop/src-tauri/src/commands.rsapps/desktop/src-tauri/src/dictation.rsapps/desktop/src/App.tsxapps/desktop/src/clipboardWriteGate.tsapps/desktop/src/latestRequestGate.tsapps/desktop/src/sessionText.tsapps/desktop/src/styles.cssapps/desktop/src/types.tsapps/desktop/tests/clipboardWriteGate.test.tsapps/desktop/tests/latestRequestGate.test.tsapps/desktop/tests/sessionText.test.tscrates/lumen-store/src/lib.rscrates/lumen-store/src/schema.rs
💤 Files with no reviewable changes (1)
- apps/desktop/src/styles.css
Summary
No list delete shortcut is added, avoiding accidental destructive actions.
Scope
Public repository boundary
scripts/ci/check-public-repo-boundary.shpassesscripts/ci/check-pr-scope.sh origin/main HEADpassesorigin/main...HEADcommit and file range inspectedValidation
npm test(18 passed)npm run buildcargo test -p lumen-store(66 passed)cargo test -p lumen-asr-desktop(166 passed, 5 ignored)cargo test --workspacecargo clippy -p lumen-store -p lumen-asr-desktop --all-targets(only pre-existing warnings)scripts/ci/check-public-repo-boundary.shscripts/ci/check-pr-scope.sh origin/main HEAD