fix: harden desktop lifecycle boundaries - #208
Conversation
|
Warning Review limit reached
Next review available in: 57 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds window-scoped Tauri command permissions, transactional engine installation, coordinated model and sidecar operations, bounded backup handling, theme boot synchronization, and cancellation-aware invite and break workflows with tests. ChangesApplication safety and lifecycle
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant ModelDownload
participant ModelOperationState
participant Sidecar
participant Filesystem
ModelDownload->>ModelOperationState: acquire model operation gate
ModelDownload->>Sidecar: stop serving managed model
ModelDownload->>Filesystem: update model files
Sidecar->>ModelOperationState: coordinate startup and engine access
sequenceDiagram
participant SessionView
participant BreakQueue
participant AuditPipeline
participant BreakOrchestrator
SessionView->>BreakQueue: submit break request with AbortSignal
BreakQueue->>AuditPipeline: append request audit
AuditPipeline-->>BreakQueue: complete or abort
BreakQueue->>BreakOrchestrator: process session state
BreakOrchestrator-->>SessionView: return verdict or AbortError
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
✨ Simplify code
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: 4
🧹 Nitpick comments (4)
tests/integration/invite.test.ts (1)
505-514: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe join assertion depends on the exact microtask count before
joinTopic.A single
await Promise.resolve()is enough today, becausesendInviteEnvelopequeuesprev.then(...)before the test yields, andjoinTopicruns synchronously inside that callback. If a later change adds oneawaitbeforejoinTopicinsendInviteEnvelopeNow, this assertion reads 0 joins and fails for a reason unrelated to abort behavior.Consider
vi.waitForso the test waits for the join instead of assuming the hop count.♻️ Proposed robustness change
- await Promise.resolve() const trystero = (await import('`@/lib/trystero`')) as unknown as { __getEvents: () => Array<{ type: 'join' | 'leave'; topic: string }> } - expect( - trystero.__getEvents().filter((e) => e.type === 'join') - ).toHaveLength(1) + await vi.waitFor(() => { + expect( + trystero.__getEvents().filter((e) => e.type === 'join') + ).toHaveLength(1) + }) controller.abort()🤖 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 `@tests/integration/invite.test.ts` around lines 505 - 514, Update the join assertion in the invite cancellation test to use vi.waitFor around the trystero.__getEvents join-count check, waiting until one join is observed instead of relying on a single Promise.resolve microtask yield. Preserve the existing expectation that exactly one join occurs.src/features/friends/invite.ts (1)
177-212: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove shared abort helpers to one module.
src/features/friends/invite.tsandsrc/features/session/break.tsboth definecreateAbortError,throwIfAborted, andwithAbortlocally, and onlyinvite.tshaswaitForAbortableDelay. Centralize these helpers and import them from both files to keep abort semantics consistent.🤖 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 `@src/features/friends/invite.ts` around lines 177 - 212, Centralize createAbortError, throwIfAborted, withAbort, and waitForAbortableDelay in a shared abort-helper module, then remove their local definitions from invite.ts and the corresponding duplicated helpers from break.ts. Import and reuse the shared symbols in both feature files, preserving the existing abort behavior and delay handling.src-tauri/src/commands/models.rs (1)
1087-1099: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider adding a short-path case to the rejection table.
The rejection list covers scheme, host, port, path kind, userinfo, and query. It does not cover a URL with fewer than five path segments. That case exercises the
segments.len() >= 5rule, which is the only guard against a resolve URL without a filename.♻️ Proposed additional cases
"https://token@huggingface.co/ggml-org/model/resolve/rev/model.gguf", "https://huggingface.co/ggml-org/model/resolve/rev/model.gguf?download=true", + "https://huggingface.co/ggml-org/model/resolve/rev", + "https://huggingface.co/ggml-org/model/resolve/rev/", ] {🤖 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 `@src-tauri/src/commands/models.rs` around lines 1087 - 1099, Extend the rejection table in trusted_model_url_rejects_non_hugging_face_or_non_resolve_urls with a valid HTTPS Hugging Face resolve URL whose path has fewer than five segments, such as one lacking the filename. Keep the assertion against trusted_hugging_face_url unchanged so the segments.len() >= 5 guard is explicitly covered.tests/unit/break-rules.test.ts (1)
473-499: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the aborted-signal branch inside the timer callback.
This test drives the token mismatch path. The stale callback returns at the token check in
src/features/session/break.tsLine 378 and never reaches thedeps.signal?.abortedcheck at Line 380.That second guard is reachable in production.
SessionViewaborts the controller in the AI-dialog effect cleanup at Line 1487, whilecancelActiveBreakTimerruns only in the separate reset effect cleanup at Line 1094. The two effects have different dependency arrays, so a signal can abort while the timer record is still armed and still owns its token. Without a test, a future change to the guard order would pass silently.💚 Proposed additional test
+ test('an aborted session timer does not end the break when its record is still armed', async () => { + const controller = new AbortController() + const input = { + requestedDurationSec: 300, + aiRecommendation: 'approve' as const, + aiReasoning: '', + now: 1_700_000_000_000, + } + const deps = buildDeps(state()) + await requestBreak(input, { ...deps, signal: controller.signal }) + const timer = scheduledTimers[0]! + + // Abort only. The timer record keeps its own token, so the callback + // reaches the signal guard rather than the token guard. + controller.abort() + timer.handler() + + expect(endBreakCalls).toHaveLength(0) + })As per path instructions: "Use Vitest for unit and integration tests".
🤖 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 `@tests/unit/break-rules.test.ts` around lines 473 - 499, Add a unit test covering the timer callback’s aborted-signal guard when the callback still owns the active timer token: abort the controller without canceling the timer, invoke its handler, and assert the break does not end while the timer record remains consistent. Keep the existing stale-token coverage unchanged and use the current break-rule test helpers and Vitest assertions.Source: Path instructions
🤖 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 `@src-tauri/src/commands/friends.rs`:
- Around line 109-116: Update read_backup_bounded_with_limit to reject FIFO and
other non-regular paths before any potentially blocking read open. Use a
race-safe Unix-appropriate non-blocking/open-and-validate flow, while preserving
the existing "{} is not a regular file" error and bounded-read behavior for
regular files.
In `@src-tauri/src/commands/sidecar.rs`:
- Around line 153-165: Update kill_blocking’s try_lock spin to use a bounded
deadline, such as an Instant several seconds in the future, and stop retrying
once it expires. Preserve the existing lock acquisition and cleanup behavior
while ensuring the blocking exit handler can return instead of waiting
indefinitely.
In `@src/features/session/break.ts`:
- Around line 337-383: Move the break-end timer setup, including clearing any
existing activeBreakTimer and assigning the new timer token/handle, to
immediately after startApprovedBreak() and before the emitAudit()/withAbort()
broadcast. Preserve the callback’s token, abort, and endBreak checks, while
leaving the broadcast as best-effort and retaining its existing error handling.
In `@tests/integration/invite.test.ts`:
- Around line 150-158: Reset the invite retry manager’s delivered cache during
integration-test teardown, not only its pending entries. Update the cleanup
around inviteRetryManager.cancelAll() to invoke the manager’s
reset/clear-delivery mechanism, ensuring SAMPLE_SESSION recipient registrations
do not persist across tests.
---
Nitpick comments:
In `@src-tauri/src/commands/models.rs`:
- Around line 1087-1099: Extend the rejection table in
trusted_model_url_rejects_non_hugging_face_or_non_resolve_urls with a valid
HTTPS Hugging Face resolve URL whose path has fewer than five segments, such as
one lacking the filename. Keep the assertion against trusted_hugging_face_url
unchanged so the segments.len() >= 5 guard is explicitly covered.
In `@src/features/friends/invite.ts`:
- Around line 177-212: Centralize createAbortError, throwIfAborted, withAbort,
and waitForAbortableDelay in a shared abort-helper module, then remove their
local definitions from invite.ts and the corresponding duplicated helpers from
break.ts. Import and reuse the shared symbols in both feature files, preserving
the existing abort behavior and delay handling.
In `@tests/integration/invite.test.ts`:
- Around line 505-514: Update the join assertion in the invite cancellation test
to use vi.waitFor around the trystero.__getEvents join-count check, waiting
until one join is observed instead of relying on a single Promise.resolve
microtask yield. Preserve the existing expectation that exactly one join occurs.
In `@tests/unit/break-rules.test.ts`:
- Around line 473-499: Add a unit test covering the timer callback’s
aborted-signal guard when the callback still owns the active timer token: abort
the controller without canceling the timer, invoke its handler, and assert the
break does not end while the timer record remains consistent. Keep the existing
stale-token coverage unchanged and use the current break-rule test helpers and
Vitest assertions.
🪄 Autofix
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6e02cde1-751f-4f0c-8c34-342292e5d19b
📒 Files selected for processing (27)
.gitignoreARCHITECTURE.mdCHANGELOG.mdsrc-tauri/build.rssrc-tauri/capabilities/ai-dialog.jsonsrc-tauri/capabilities/default.jsonsrc-tauri/permissions/window-commands.tomlsrc-tauri/src/commands/applog.rssrc-tauri/src/commands/engine.rssrc-tauri/src/commands/friends.rssrc-tauri/src/commands/models.rssrc-tauri/src/commands/sidecar.rssrc-tauri/src/lib.rssrc/components/ui/dialog.tsxsrc/design/theme-resolution.tssrc/design/theme.tsxsrc/features/ai/ai-dialog-main.tsxsrc/features/friends/invite.tssrc/features/friends/inviteRetry.tssrc/features/friends/pendingInvitesStore.tssrc/features/session/SessionView.tsxsrc/features/session/break.tstests/integration/invite.test.tstests/unit/ai-sidecar.test.tstests/unit/break-rules.test.tstests/unit/inviteRetry.test.tstests/unit/theme.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: Installer (macOS)
- GitHub Check: Installer (Windows)
- GitHub Check: Frontend
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,rs}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,rs}: Never add telemetry; the application is local-only. Never instruct users to paste model files or BIP39 mnemonics into an AI chat service.
Add comments only when the reason is non-obvious; identifiers should carry the meaning and code should read top-to-bottom.
Maintain scope discipline: do not refactor adjacent code while implementing a feature, add abstractions for hypothetical needs, or expand a bug fix beyond the bug.
Files:
tests/unit/theme.test.tssrc-tauri/src/lib.rssrc/components/ui/dialog.tsxsrc/features/friends/pendingInvitesStore.tstests/unit/ai-sidecar.test.tssrc/design/theme-resolution.tssrc/features/ai/ai-dialog-main.tsxsrc-tauri/src/commands/applog.rssrc-tauri/build.rstests/unit/break-rules.test.tstests/integration/invite.test.tssrc-tauri/src/commands/engine.rssrc/design/theme.tsxsrc-tauri/src/commands/friends.rstests/unit/inviteRetry.test.tssrc/features/friends/invite.tssrc/features/session/break.tssrc/features/friends/inviteRetry.tssrc/features/session/SessionView.tsxsrc-tauri/src/commands/models.rssrc-tauri/src/commands/sidecar.rs
tests/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit and integration tests; component tests are not currently supported because the harness uses node-env without RTL/jsdom, so component behavior belongs in Storybook and axe-core checks.
Files:
tests/unit/theme.test.tstests/unit/ai-sidecar.test.tstests/unit/break-rules.test.tstests/integration/invite.test.tstests/unit/inviteRetry.test.ts
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Use one focused change per commit, with a Conventional Commit subject such as
feat:,fix:,chore:,docs:, orci:; PRs are squash-merged.
Files:
tests/unit/theme.test.tssrc-tauri/src/lib.rssrc/components/ui/dialog.tsxsrc/features/friends/pendingInvitesStore.tsCHANGELOG.mdtests/unit/ai-sidecar.test.tssrc-tauri/capabilities/default.jsonsrc/design/theme-resolution.tssrc-tauri/capabilities/ai-dialog.jsonsrc/features/ai/ai-dialog-main.tsxsrc-tauri/src/commands/applog.rssrc-tauri/build.rstests/unit/break-rules.test.tstests/integration/invite.test.tssrc-tauri/permissions/window-commands.tomlARCHITECTURE.mdsrc-tauri/src/commands/engine.rssrc/design/theme.tsxsrc-tauri/src/commands/friends.rstests/unit/inviteRetry.test.tssrc/features/friends/invite.tssrc/features/session/break.tssrc/features/friends/inviteRetry.tssrc/features/session/SessionView.tsxsrc-tauri/src/commands/models.rssrc-tauri/src/commands/sidecar.rs
src-tauri/**/*.rs
📄 CodeRabbit inference engine (CLAUDE.md)
Rust changes must pass
cargo test,cargo fmt --check, andcargo clippy; dependency and supply-chain changes must passcargo deny check.
Files:
src-tauri/src/lib.rssrc-tauri/src/commands/applog.rssrc-tauri/build.rssrc-tauri/src/commands/engine.rssrc-tauri/src/commands/friends.rssrc-tauri/src/commands/models.rssrc-tauri/src/commands/sidecar.rs
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{ts,tsx}: All design-token values—colors, spacing, fonts, radii, shadows, motion, and z-indexes—must come fromsrc/design/tokens.ts; do not use raw hex values, arbitrarypx, or inlinecubic-beziervalues.
User-facing toast and notification copy must live insrc/strings.ts; prefer centralized strings for JSX text andaria-labelvalues.
Maintain WCAG AA contrast for every text/background pairing in both themes, do not convey information by color alone, and honor the global reduced-motion kill switch; new motion must be gated by default.
Treat peer wire formats and identity derivation as cross-version contracts; coordinate changes so older builds and existing stored data remain compatible.
Every component and feature component must have a Storybook story.
Files:
src/components/ui/dialog.tsxsrc/features/friends/pendingInvitesStore.tssrc/design/theme-resolution.tssrc/features/ai/ai-dialog-main.tsxsrc/design/theme.tsxsrc/features/friends/invite.tssrc/features/session/break.tssrc/features/friends/inviteRetry.tssrc/features/session/SessionView.tsx
src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
src/components/ui/is the only location where Radix or shadcn primitives may be imported.
Files:
src/components/ui/dialog.tsx
src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Components under
src/components/should compose fromui/,src/design/, and shared utilities; reverse imports are prohibited.
Files:
src/components/ui/dialog.tsx
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Do not create new documentation files unless explicitly requested; update canonical documentation,
CHANGELOG.md, orISSUES.mdwhen justified.
Files:
CHANGELOG.mdARCHITECTURE.md
🔇 Additional comments (69)
.gitignore (1)
33-34: LGTM!ARCHITECTURE.md (1)
687-687: LGTM!Also applies to: 714-719
CHANGELOG.md (1)
3-27: LGTM!src-tauri/capabilities/ai-dialog.json (1)
4-10: LGTM!src-tauri/capabilities/default.json (1)
8-8: LGTM!src-tauri/permissions/window-commands.toml (1)
1-82: LGTM!src-tauri/src/commands/applog.rs (1)
27-28: LGTM!Also applies to: 136-138
src-tauri/build.rs (1)
8-79: 🔒 Security & PrivacyRun the required Rust gates before merge.
APP_COMMANDSandwindow-commands.tomlstay synchronized, and there are no unrecognized invoke handlers. If these changes include Rustsrc-tauriupdates, runcargo test,cargo fmt --check, andcargo clippyin CI.src/components/ui/dialog.tsx (1)
42-42: LGTM!Also applies to: 64-64
src/design/theme-resolution.ts (1)
1-12: LGTM!src/design/theme.tsx (1)
11-15: LGTM!Also applies to: 28-100, 120-126
src/features/ai/ai-dialog-main.tsx (1)
10-10: LGTM!Also applies to: 62-62
tests/unit/theme.test.ts (1)
1-15: LGTM!src/features/friends/invite.ts (7)
24-36: LGTM!
118-118: LGTM!
268-278: LGTM!
288-289: LGTM!
349-359: LGTM!Also applies to: 372-376
391-401: LGTM!
420-436: LGTM!src/features/friends/inviteRetry.ts (6)
3-12: LGTM!
25-44: LGTM!
63-71: LGTM!
101-143: LGTM!
145-208: LGTM!
210-222: LGTM!tests/integration/invite.test.ts (3)
249-256: LGTM!
335-339: LGTM!
486-503: LGTM!Also applies to: 515-521
tests/unit/inviteRetry.test.ts (4)
14-14: LGTM!Also applies to: 46-46, 63-63, 73-73, 83-83, 94-94, 108-109, 122-122, 135-135, 165-165, 241-241
26-43: LGTM!
147-149: LGTM!
179-238: LGTM!src/features/friends/pendingInvitesStore.ts (1)
58-61: 📐 Maintainability & Code QualityKeep the
defaultsoption as-is.
@tauri-apps/plugin-store2.4.4 definesdefaultsonStoreOptions, so the comment accurately describes the library requirement.> Likely an incorrect or invalid review comment.src-tauri/src/commands/friends.rs (5)
7-9: LGTM!
278-283: 🚀 Performance & ScalabilityConfirm the scope of the export size limit.
Line 278 runs after
encode_backuphas serialized all rows, sealed the full plaintext, and allocated the output vector. This caps the file written to disk, but it does not cap peak memory. Iffriends::listor friend fields are not bounded elsewhere, a large local database can allocate far above 16 MiB before returning the error.Confirm those bounds or enforce a pre-encoding size budget.
302-302: LGTM!
516-545: LGTM!
516-545: 📐 Maintainability & Code QualityComplete the required Rust checks before merge.
As per coding guidelines, Rust changes must pass
cargo test,cargo fmt --check, andcargo clippy. The PR context marks these gates as pending or unavailable, so verify them before merging.Source: Coding guidelines
src-tauri/src/commands/engine.rs (4)
256-260: LGTM!
357-396: LGTM!
398-454: LGTM!
804-852: LGTM!src-tauri/src/commands/models.rs (7)
100-141: LGTM!
143-207: LGTM!
209-245: LGTM!
314-332: LGTM!
381-408: LGTM!
483-489: LGTM!Also applies to: 677-684
551-612: LGTM!src-tauri/src/commands/sidecar.rs (7)
108-111: LGTM!
227-262: LGTM!
293-332: LGTM!
381-381: LGTM!Also applies to: 419-419, 446-474
863-865: LGTM!Also applies to: 929-929
993-1046: LGTM!
1091-1133: LGTM!src-tauri/src/lib.rs (1)
55-55: LGTM!Also applies to: 324-324
tests/unit/ai-sidecar.test.ts (1)
725-725: LGTM!Also applies to: 764-779
src/features/session/SessionView.tsx (4)
165-165: LGTM!Also applies to: 401-401, 412-412
824-843: LGTM!
845-876: LGTM!
1446-1480: 🩺 Stability & AvailabilityNo change needed. The AI dialog clears pending break request nonces on cleanup and resolves a missing main-window response through
BREAK_REQUEST_TIMEOUT_MS.src/features/session/break.ts (4)
154-165: LGTM!Also applies to: 167-185
193-253: LGTM!
255-273: LGTM!
391-404: LGTM!tests/unit/break-rules.test.ts (2)
336-383: LGTM!
385-471: LGTM!
| pub fn kill_blocking<R: Runtime>(app: &AppHandle<R>) { | ||
| let Some(state) = app.try_state::<SidecarState>() else { | ||
| return; | ||
| }; | ||
| let arc = state.0.clone(); | ||
| let deadline = Instant::now() + Duration::from_millis(500); | ||
| let mut guard = loop { | ||
| match arc.try_lock() { | ||
| Ok(g) => break g, | ||
| Err(_) => { | ||
| if Instant::now() >= deadline { | ||
| return; | ||
| } | ||
| std::thread::sleep(Duration::from_millis(10)); | ||
| std::thread::sleep(Duration::from_millis(1)); | ||
| } | ||
| } | ||
| }; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find every SidecarInner lock acquisition and check for awaits held across the guard.
set -euo pipefail
fd -e rs . src-tauri/src --exec rg -n -C 12 'lock\(\)\.await|try_lock\(\)' {} \; \
| rg -n -C 6 'SidecarInner|state\.lock|arc\.lock' || true
# Locate every kill_blocking call site and the RunEvent arms that reach it.
rg -nP -C 8 '\bkill_blocking\s*\(' src-tauri/srcRepository: scotej/studyvis
Length of output: 12561
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== sidecar relevant ranges =="
wc -l src-tauri/src/commands/sidecar.rs src-tauri/src/lib.rs
sed -n '1,180p' src-tauri/src/commands/sidecar.rs
sed -n '286,430p' src-tauri/src/commands/sidecar.rs
sed -n '430,500p' src-tauri/src/commands/sidecar.rs
sed -n '880,1065p' src-tauri/src/commands/sidecar.rs
sed -n '328,356p' src-tauri/src/lib.rs
echo "== all await in src-tauri/src/commands/sidecar.rs =="
python3 - <<'PY'
import re, pathlib
p=pathlib.Path('src-tauri/src/commands/sidecar.rs')
text=p.read_text()
for i,line in enumerate(text.splitlines(),1):
if '.await' in line or 'lock().await' in line:
print(f"{i}: {line.rstrip()}")
PY
echo "== deterministic search for lock spans with await =="
python3 - <<'PY'
from pathlib import Path
import re
src = Path('src-tauri/src/commands/sidecar.rs').read_text()
lines = src.splitlines()
for idx, line in enumerate(lines):
if 'lock().await' in line or 'try_lock()' in line:
func = None
for fk, fl in enumerate(lines[:idx][::-1], 1):
if re.match(r'async fn \w+|fn \w+|impl .*SidecarState|impl .*Engine', fl):
func = fl.strip()
break
print(f"line {idx+1}: {line.strip()} <= function {func or '<top-level/unknown>'}")
if 'await' in line:
# Show concise context around all awaits
start=max(1, idx-3); end=min(len(lines), idx+3)
print(f"await around line {idx+1}:")
for n in range(start, end+1):
print(f"{n}: {lines[n-1].rstrip()}")
PYRepository: scotej/studyvis
Length of output: 34384
Add a bounded deadline to kill_blocking’s lock spin.
kill_blocking is called from the blocking RunEvent::Exit* handler, so this thread can only quit after it returns. If a watch task is parked at await while holding state.lock().await, the unbounded 1 ms try_lock() loop can wait forever. Add an escalation deadline (for example, several seconds) so the exit path can terminate even during an asynchronous lock holder.
🤖 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 `@src-tauri/src/commands/sidecar.rs` around lines 153 - 165, Update
kill_blocking’s try_lock spin to use a bounded deadline, such as an Instant
several seconds in the future, and stop retrying once it expires. Preserve the
existing lock acquisition and cleanup behavior while ensuring the blocking exit
handler can return instead of waiting indefinitely.
4b86858 to
3fd5de6
Compare
3fd5de6 to
2ef6f55
Compare
2ef6f55 to
66a1098
Compare
What changed and why
Hardens the desktop boundary and lifecycle paths identified in review: custom Tauri commands are now explicitly window-scoped; model downloads, sidecars, engine replacement, and friend backups are safer under failure/cancellation; invite and break flows serialize correctly; and Settings/AI-dialog behavior stays visually and thematically consistent. The PR also adds a
1.11.1changelog section for the release workflow without bumping version files early.Manual test
npm run tauri devlaunched and the changed surface behaves as described — n-a: this Linux container has no desktop hostCompatibility surfaces
Gates
npm run build && npm run lint && npm run test— build and lint passed via Bun; the full Vitest worker runner is unavailable in this container.npm run check-tokens && npm run check-strings && npm run check-contrastnpm run check-migrations && npm run check-storiesnpm run build-storybook && npm run check-a11y— unavailable in this container.cd src-tauri && cargo fmt --check && cargo clippy && cargo test— CI is the first Rust compiler for this box.cd src-tauri && cargo deny check— CI is the first Rust toolchain for this box.CI-only gates remain pending.
Merge style
Summary by CodeRabbit