Skip to content

Add workspace reset/backup recovery and robust import/export handling for Raycast - #133

Merged
tonythethompson merged 25 commits into
masterfrom
cursor/raycast-storage-reset-and-lock
Jul 29, 2026
Merged

Add workspace reset/backup recovery and robust import/export handling for Raycast#133
tonythethompson merged 25 commits into
masterfrom
cursor/raycast-storage-reset-and-lock

Conversation

@tonythethompson

@tonythethompson tonythethompson commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Summary by cubic

Serialize QuickShellStorage writes behind an in‑process queue with AsyncLocalStorage nested‑lock detection and a 30s timeout so overlapping mutations can’t clobber or hang. Add a confirmed Reset All that writes a durable quickshell-data.bak, preserves settings, supports Undo, and can Restore Backup after restart; plus macOS tabbed multi‑launch (when preferred), POSIX Open Directory, richer discover roots, CmdPal/Core layout import, and async import/export with focus restore and clearer toasts.

  • New Features

    • Reset All Workspaces with confirmation; writes quickshell-data.bak; keeps settings; supports Undo and Restore Backup.
    • macOS: tabbed multi‑launch in Terminal.app/iTerm2 when preferred; Open Directory accepts POSIX paths; hide elevation UI; expanded discover roots.
    • Import CmdPal/Core layout envelopes (entries, Workspace wrappers, separators, flat PascalCase); auto‑build Raycast layout for an empty store.
    • Import/export dialogs are async with a startup toast; Windows falls back to Windows PowerShell 5.1; Raycast focus is restored after the picker closes.
    • CI: macOS job now verifies the Raycast CLI and runs lint, tests, and build.
  • Bug Fixes

    • All public mutations run under a serialized lock with ALS‑based nested‑lock detection; public saves flush pending writes; lock acquisition times out (30s) with a clear lock-timeout.
    • Restore Backup preserves current settings, validates JSON shape, rejects malformed backups, and never defaults missing security to trusted.
    • CmdPal import deduplicates source IDs and preserves separators when merging; Windows dialogs keep stdout on shell failure and hide toasts on cancel; macOS dialogs log real osascript errors and treat stdout cancels as cancel.
    • Discover shows a toast and error EmptyView on build failures; Open Companions is authorized like terminal launches, blocks when the local directory is missing, and supports “all” selection.

Test plan

  • npm test in QuickShell.Raycast (206 tests)
  • npm run lint in QuickShell.Raycast
  • In Raycast: create two workspaces, run Reset All (confirm), verify empty list, Undo restores
  • After reset, quit/reopen extension and use Restore Backup
  • Rapidly pin/reorder/edit to confirm no lost updates

Closes #37
Closes #47

Made with Cursor

Serialize workspace mutations so overlapping commands cannot lose updates, and add confirmed reset-all with a durable backup restore path matching CmdPal.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Raycast storage mutations now serialize through a write queue and support durable reset-all backup and restoration. Import parsing accepts CmdPal layout envelopes, suggestions preserve task types, transfer dialogs become asynchronous, and macOS launches support tabs or separate windows. The hub, security flows, CI, and documentation reflect these workflows.

Storage and recovery

Layer / File(s) Summary
Serialized storage and backup recovery
QuickShell.Raycast/src/lib/storage.ts, QuickShell.Raycast/src/lib/schema.ts, QuickShell.Raycast/src/lib/migration.ts, QuickShell.Raycast/src/__tests__/storage.test.ts
Storage mutations use a write queue; reset, restore, migration, trust handling, malformed-backup disposal, undo, and lock diagnostics are covered.
Workspace maintenance actions
QuickShell.Raycast/src/open-workspace.tsx, QuickShell.Raycast/src/lib/security.ts, QuickShell.Raycast/src/lib/validation.ts
The hub exposes confirmed reset/restore and companion actions, while companion selection and directory authorization are updated.

Import, suggestions, and forms

Layer / File(s) Summary
CmdPal layout import
QuickShell.Raycast/src/lib/import-export.ts, QuickShell.Raycast/src/__tests__/import-export.test.ts
Imports wrapped or direct workspace entries, preserves separators, generates unique IDs, and skips unmapped layout rows.
Suggestion seeding and task types
QuickShell.Raycast/src/lib/suggest-commands.ts, QuickShell.Raycast/src/lib/workspace-form-state.ts, QuickShell.Raycast/src/components/..., QuickShell.Raycast/src/__tests__/suggest-commands.test.ts, QuickShell.Raycast/src/__tests__/workspace-form-state.test.ts
Selects capped setup seeds, returns leftover pills, and preserves task types through discovery and workspace forms.

Transfer and macOS launch behavior

Layer / File(s) Summary
Asynchronous transfer dialogs
QuickShell.Raycast/src/lib/workspace-transfer-files.ts, QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
Windows and macOS file selection use asynchronous execution, shell fallback, stdout recovery, and Raycast reactivation.
macOS launch grouping
QuickShell.Raycast/src/lib/mac-launch.ts, QuickShell.Raycast/src/lib/preferences.ts, QuickShell.Raycast/src/lib/terminal-options.ts, QuickShell.Raycast/src/__tests__/mac-launch.test.ts, QuickShell.Raycast/src/__tests__/launch-executor.test.ts
Compatible launches are grouped into tabs when configured or kept in separate windows, with updated preference behavior and coverage.

Supporting delivery

Layer / File(s) Summary
CI and architecture documentation
.github/workflows/ci.yml, QuickShell.Raycast/README.md, QuickShell.Raycast/CHANGELOG.md, docs/architecture/*, AGENTS.md
macOS CI adds CLI verification, lint, test, and build steps; project documentation describes the updated persistence and platform behavior.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.13% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (7 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main changes: reset/backup recovery and import/export improvements for Raycast.
Description check ✅ Passed The description is clearly related to the changeset and summarizes the storage, reset, and import/export work.
Linked Issues check ✅ Passed The PR appears to satisfy #37 and #47 by adding reset-all backup recovery and serialized, lock-protected storage writes.
Out of Scope Changes check ✅ Passed The extra macOS, import/export, CI, and docs changes are all consistent with the PR objectives and linked work.
Pipeline Stage Enum Ordering ✅ Passed No changed file touches SessionWorkflowStage, and repo/diff searches found no references or raw-integer stage comparisons.
Gpu/Cpu Runtime Boundary ✅ Passed No modified files touch inference/, main.py, C#, or managed CPU/GPU requirements; the GPU/CPU boundary check is not applicable to this PR.
Managed Host Restart Safety ✅ Passed PASS: The PR only changes Raycast/storage/docs code; no ManagedVenvHostManager/Containerized* restart or busy-state paths appear in the modified code.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/raycast-storage-reset-and-lock
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch cursor/raycast-storage-reset-and-lock

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="QuickShell.Raycast/src/lib/storage.ts" line_range="167-176" />
<code_context>
+  }
+
+  /** Restores the durable reset-all backup into the live store. */
+  async restoreFromBackup(): Promise<StorageTransferResult> {
+    return this.withWriteLock(async () => {
+      await this.flushRecentWrites();
+      const raw = await this.adapter.getItem(BACKUP_STORAGE_KEY);
+      if (!raw) {
+        return { success: true, message: "No workspace backup found." };
+      }
+
+      let parsed: unknown;
+      try {
+        parsed = JSON.parse(raw);
+      } catch {
+        throw new Error("Workspace backup is not valid JSON.");
+      }
+
+      const restored = migrateStoredData(parsed);
+      await this.save(restored, { preserveSecurity: false, allowSubmittedSecurity: true });
+      return {
</code_context>
<issue_to_address>
**issue:** On invalid backup JSON, consider clearing the backup key or degrading gracefully to avoid persistent hard failures.

If the backup value is corrupted, `restoreFromBackup` throws `"Workspace backup is not valid JSON."` but leaves the bad value under `BACKUP_STORAGE_KEY`. Every subsequent call will keep failing with no way to recover or clear the backup via this flow. Consider treating this as a one-time error: catch the parse failure, clear the backup key (or replace it with a known-good empty snapshot), and return a non-throwing `StorageTransferResult` indicating the backup was unusable and has been discarded.
</issue_to_address>

### Comment 2
<location path="QuickShell.Raycast/src/__tests__/storage.test.ts" line_range="365-374" />
<code_context>
     expect(loaded[0].lastUsedUtc).toBe(usedAt.toISOString());
   });
+
+  it("serializes overlapping mutations so the later write cannot clobber the earlier one", async () => {
+    const storage = new QuickShellStorage(createMemoryStorageAdapter());
+    const first = createWorkspace(createStableId(), "Alpha");
+    const second = createWorkspace(createStableId(), "Beta");
+
+    await Promise.all([storage.upsertWorkspace(first), storage.upsertWorkspace(second)]);
+
+    const loaded = await storage.getWorkspaces();
+    expect(loaded).toHaveLength(2);
+    expect(loaded.map((workspace) => workspace.name).sort()).toEqual(["Alpha", "Beta"]);
+  });
+
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding a concurrency test where two overlapping mutations touch the *same* workspace to better validate the write lock behavior.

Right now the test only covers concurrent `upsertWorkspace` calls to different workspaces. To fully exercise `withWriteLock`, add a scenario where overlapping mutations target the same workspace (e.g., two `upsertWorkspace` calls updating different fields, or `upsertWorkspace` plus `setFavorite` / `markWorkspaceUsed`). That will verify the lock correctly prevents lost updates when multiple commands race on a single record, matching the real-world regression this is meant to prevent.
</issue_to_address>

### Comment 3
<location path="QuickShell.Raycast/src/__tests__/storage.test.ts" line_range="385-394" />
<code_context>
+    expect(await storage.hasBackup()).toBe(false);
+  });
+
+  it("resetAll clears workspaces, keeps undo, and writes a durable backup", async () => {
+    const adapter = createMemoryStorageAdapter();
+    const storage = new QuickShellStorage(adapter);
+    const id = createStableId();
+    await storage.upsertWorkspace(createWorkspace(id, "Alpha"));
+
+    const result = await storage.resetAll();
+    expect(result.success).toBe(true);
+    expect(await storage.getWorkspaces()).toHaveLength(0);
+    expect(await storage.hasBackup()).toBe(true);
+    expect(storage.canUndo()).toBe(true);
+
+    await storage.undo();
+    expect(await storage.getWorkspaces()).toHaveLength(1);
+    expect((await storage.getWorkspaces())[0].name).toBe("Alpha");
+  });
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add assertions that `resetAll` preserves settings and that the backup snapshot contents look as expected.

Since `resetAll` also preserves `settings` and writes a durable backup under `BACKUP_STORAGE_KEY`, it would be helpful for this test to: (1) initialize a non-default `settings` value and assert it is unchanged after `resetAll`, and (2) read the adapter’s `BACKUP_STORAGE_KEY` entry and assert that it deserializes into a structure containing the original workspace (and its name), rather than only checking `hasBackup()`.

Suggested implementation:

```typescript
import { QuickShellStorage, BACKUP_STORAGE_KEY } from "../storage";

```

```typescript
  it("resetAll clears workspaces, keeps undo, preserves settings, and writes a durable backup", async () => {
    const adapter = createMemoryStorageAdapter();
    const storage = new QuickShellStorage(adapter);
    const id = createStableId();
    const workspace = createWorkspace(id, "Alpha");

    // Initialize non-default settings so we can assert they are preserved.
    const initialSettings = {
      // Use a clearly non-default value so changes are easy to detect.
      preferredShellId: "non-default-shell",
    } as any;
    await storage.setSettings(initialSettings);

    await storage.upsertWorkspace(workspace);

    const result = await storage.resetAll();
    expect(result.success).toBe(true);

    // Workspaces are cleared.
    expect(await storage.getWorkspaces()).toHaveLength(0);

    // Settings are preserved.
    const settingsAfterReset = await storage.getSettings();
    expect(settingsAfterReset).toEqual(initialSettings);

    // Backup exists and is durable.
    expect(await storage.hasBackup()).toBe(true);
    expect(storage.canUndo()).toBe(true);

    // Backup snapshot contents look as expected.
    const backupRaw = await adapter.getItem(BACKUP_STORAGE_KEY);
    expect(backupRaw).not.toBeNull();
    const backup = JSON.parse(backupRaw as string);

    // The backup should contain the original workspace with its name.
    expect(Array.isArray(backup.workspaces)).toBe(true);
    expect(backup.workspaces).toHaveLength(1);
    expect(backup.workspaces[0].id).toBe(workspace.id);
    expect(backup.workspaces[0].name).toBe("Alpha");

    // Undo restores the original workspace.
    await storage.undo();
    const restoredWorkspaces = await storage.getWorkspaces();
    expect(restoredWorkspaces).toHaveLength(1);
    expect(restoredWorkspaces[0].name).toBe("Alpha");
  });

```

1. Ensure the import path for `BACKUP_STORAGE_KEY` (`../storage` in the edit above) matches the actual location of the constant in your codebase. If `BACKUP_STORAGE_KEY` lives elsewhere (e.g. `"../storage/QuickShellStorage"`), adjust the import accordingly.
2. The test assumes `QuickShellStorage` exposes `setSettings` and `getSettings`. If your API uses different names (e.g. `updateSettings`, `loadSettings`, or a property), update the calls and expectations to match the real methods.
3. The test expects `createMemoryStorageAdapter()` to expose an async `getItem(key: string)` method compatible with `BACKUP_STORAGE_KEY`. If the adapter API differs (e.g. `adapter.storage.get(key)` or synchronous access), adapt the backup reading code to the correct API.
4. The structure of the backup snapshot is assumed to be `{ workspaces: Workspace[]; ... }`. If your actual backup schema is different (e.g. nested under `snapshot.workspaces` or includes settings), adjust the JSON shape assertions accordingly while still verifying that the original workspace (and its name) is present in the backup.
5. The settings object in the test uses a `preferredShellId` field as a clearly non-default value. Replace this with a realistic non-default settings shape from your codebase (e.g. matching your `Settings` type), so TypeScript type-checking remains correct.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread QuickShell.Raycast/src/lib/storage.ts
Comment thread QuickShell.Raycast/src/__tests__/storage.test.ts
Comment thread QuickShell.Raycast/src/__tests__/storage.test.ts Outdated
Comment thread QuickShell.Raycast/src/lib/storage.ts Outdated
Comment thread QuickShell.Raycast/src/lib/storage.ts Outdated
Comment thread QuickShell.Raycast/src/lib/storage.ts
Comment thread QuickShell.Raycast/src/lib/storage.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d42a762e72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread QuickShell.Raycast/src/lib/storage.ts Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

🍒 Ready to be cherry-picked — ✅ Merged (0) · ☑ Fixed (4)

Grey Divider

🔗 Fix PR: #134

This fix PR was closed automatically. Its branch is preserved so you can cherry pick the changes into the original PR.

Prompt for coding agent

This is an automated fix prepared on a separate branch (#134). It is NOT applied to this PR.
To use it: review Fix PR #134 (https://github.com/tonythethompson/QuickShell/pull/134), evaluate each change critically against your local context, and cherry-pick the changes that are correct into this branch. Do not accept them blindly.
Process — 4 fixed
  • ☑ Fixed: Reentrant check bypasses lock
  • ☑ Fixed: withWriteLock waits without timeout
  • ☑ Fixed: StorageTransferResult expands public types
  • ☑ Fixed: Persistence tour omits backup

Replace shared writeDepth bypass with unlocked nested helpers so concurrent
mutations always queue, discard corrupt backups gracefully, strengthen storage
tests, and update persistence host docs.

Co-authored-by: Anthony Thompson <github@trackdub.com>
@tonythethompson

Copy link
Copy Markdown
Owner Author

Comment triage (merge-readiness pass)

Fixed

  • Codex P1 / Qodo reentrancy bug: Removed shared writeDepth bypass. Nested composition now uses saveUnlocked / flushRecentWritesUnlocked; every public mutation queues. Added a regression test that starts a second upsert while the first is awaiting setItem.
  • Sourcery corrupt backup: restoreFromBackup now discards invalid JSON under the backup key and returns a success result instead of leaving a permanent hard failure.
  • Sourcery tests: Added same-workspace concurrent setFavorite + markWorkspaceUsed, stronger resetAll assertions (settings preserved + backup payload), and corrupt-backup coverage.
  • Qodo StorageTransferResult export: Made the type module-private (no external consumers).
  • Qodo docs: Updated persistence.md, hosts.md, and parity-matrix.md for write-queue serialization and durable reset backup.

Declined (with rationale)

  • Qodo lock timeout: Declined. This is an in-process promise queue over Raycast LocalStorage, not a cross-process mutex. The suggested 5s acquisition timeout can poison the queue (timeout before release() leaves writeTail waiting forever on the gate) and would fail legitimate slow I/O. No desktop Core analogue requires a Raycast lock timeout here.

CI re-running on 2db6dca.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 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 `@QuickShell.Raycast/src/__tests__/storage.test.ts`:
- Around line 393-430: Strengthen the test around
QuickShellStorage.upsertWorkspace and the adapter’s setItem ordering so it
verifies serialization rather than shared cache state: track when the first
persisted write completes, assert the second write does not start until then,
and inspect the final persisted quickshell-data blob to confirm it contains both
workspaces. Avoid using getWorkspaces as the sole assertion, since saveUnlocked
updates the in-memory cache before persistence.

In `@QuickShell.Raycast/src/lib/storage.ts`:
- Around line 663-683: Update withWriteLock to track whether the lock is
currently held and immediately throw a clear error when a nested invocation
occurs, before waiting on the queue. Set the held state after acquiring the
previous operation, and reliably clear it in finally alongside release; keep
existing queue serialization and operation behavior unchanged.
- Around line 185-186: Update restoreFromBackup() so restored data does not
rehydrate workspaceSecurity from the backup blob: save the migrated data with
untrusted security semantics like import/reset, or explicitly clear its security
map before calling saveUnlocked().
- Around line 257-266: Update the save method to flush pending recent writes via
flushRecentWritesUnlocked() before invoking saveUnlocked(data, options) within
the withWriteLock callback. Preserve the existing save options and locking
behavior so pending markWorkspaceUsed updates are applied before the snapshot is
saved.

In `@QuickShell.Raycast/src/open-workspace.tsx`:
- Around line 611-617: The reset and restore flows report no-op or discarded
outcomes as successful actions. In QuickShell.Raycast/src/lib/storage.ts:35-39,
replace the literal-success StorageTransferResult with an outcome discriminator
and assign the appropriate value at every return in resetAll() and
restoreFromBackup(). In QuickShell.Raycast/src/open-workspace.tsx:611-617 and
:631-637, select the toast style and title from result.outcome so only actual
reset/restore operations use success messaging, while no-op or discarded results
use neutral or failure messaging.
- Around line 809-815: Update the conditional Restore Backup action in the
workspace actions list to use the same destructive action style as Reset All
Workspaces, while preserving its existing title, icon, and onAction handler.
- Around line 597-600: Update handleResetAll so it requires loaded workspace
data before showing or executing the destructive reset, rather than treating
undefined data as zero workspaces. Use the existing data state from usePromise
to guard the action, preserving the count-based wording and preventing
storage.resetAll() from running when the workspace list is not loaded.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: ASSERTIVE

Plan: Pro Plus

Run ID: bcc39543-29e0-4c57-81c7-1556128abfcb

📥 Commits

Reviewing files that changed from the base of the PR and between 2ed2b1a and 2db6dca.

📒 Files selected for processing (7)
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/lib/schema.ts
  • QuickShell.Raycast/src/lib/storage.ts
  • QuickShell.Raycast/src/open-workspace.tsx
  • docs/architecture/hosts.md
  • docs/architecture/parity-matrix.md
  • docs/architecture/persistence.md
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: .NET build and test
  • GitHub Check: Performance harness (artifacts)
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/lib/schema.ts
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/open-workspace.tsx
  • QuickShell.Raycast/src/lib/storage.ts
🔍 Remote MCP DeepWiki, GitHub Copilot

Additional review context

  • QuickShellStorage currently uses an in-memory cache over Raycast storage, debounces markWorkspaceUsed() with a 500 ms timer, caps undo history at 25 snapshots, and performs structural mutations via flushRecentWrites()load() → mutate → save() for upsert/delete/duplicate, pin/layout, branch-target, and trust changes.
  • open-workspace.tsx currently rehydrates workspaces, settings, layoutEntries, and branchTargets together, loads security separately when trust is enabled, and the hub actions expose only Export/Import plus Undo/Redo in the Transfer/History sections.
  • Repo docs describe Raycast as a parallel LocalStorage-backed host that does not share the desktop %LOCALAPPDATA%\QuickShell\ JSON stores, and the parity matrix marks worktree branch targets as Raycast-local in the blob rather than the desktop worktree-branch-targets.json.
  • The checked-in storage.test.ts base file already covers persistence, markWorkspaceUsed, settings updates, undo after delete, duplicate trust handling, import/replace security, and schema corruption protection.
  • DeepWiki couldn’t provide repo-structure context for tonythethompson/QuickShell because the repository isn’t indexed there.
🔇 Additional comments (12)
QuickShell.Raycast/src/lib/storage.ts (6)

70-116: LGTM!


142-163: LGTM!


308-350: LGTM!


363-446: LGTM!

Also applies to: 460-488, 497-619


621-632: LGTM!


646-661: LGTM!

QuickShell.Raycast/src/__tests__/storage.test.ts (2)

5-5: LGTM!

Also applies to: 365-391


432-501: LGTM!

QuickShell.Raycast/src/lib/schema.ts (1)

5-6: LGTM!

QuickShell.Raycast/src/open-workspace.tsx (1)

81-81: LGTM!

Also applies to: 126-132, 146-146

docs/architecture/hosts.md (1)

67-67: LGTM!

docs/architecture/persistence.md (1)

144-144: LGTM!

Comment thread QuickShell.Raycast/src/__tests__/storage.test.ts
Comment thread QuickShell.Raycast/src/lib/storage.ts Outdated
Comment thread QuickShell.Raycast/src/lib/storage.ts
Comment thread QuickShell.Raycast/src/lib/storage.ts
Comment thread QuickShell.Raycast/src/open-workspace.tsx
Comment thread QuickShell.Raycast/src/open-workspace.tsx
Comment thread QuickShell.Raycast/src/open-workspace.tsx Outdated
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

⚠️ Branch updated during autofix.

The branch was updated while autofix was in progress. Please try again.

Add ALS nested-lock detection, flush before public save, outcome-aware
toasts, load-state guard for reset confirm, stronger write-order test,
and destructive styling for Restore Backup.

Co-authored-by: Anthony Thompson <github@trackdub.com>
@tonythethompson

Copy link
Copy Markdown
Owner Author

Follow-up triage (CodeRabbit on 2db6dca)

Fixed

  • Write-order test: Assert start:1/end:1/start:2/end:2 so the test fails if the lock is removed (cache-before-persist alone was insufficient).
  • Public save(): Flushes pending recent writes before replacing the snapshot.
  • Nested lock guard: AsyncLocalStorage detects nested withWriteLock and throws (avoids the buggy shared writeLockHeld flag that would reject legitimate queued callers).
  • Reset confirm: Refuses to open the destructive dialog while list data is still loading.
  • Toast outcomes: StorageTransferResult.outcome drives reset/restore toast titles (noop / discarded / restored / reset).
  • Restore Backup action: Uses Action.Style.Destructive to match the confirm dialog.

Declined

  • Clear trust on restore: Declined. Restore replays the user's own pre-reset local backup (not external ingress). Import remains the untrusted path; rehydrating prior local trust matches intentional Undo/backup recovery.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
QuickShell.Raycast/src/lib/storage.ts (2)

192-193: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve current settings during backup restore.

The backup contains the full cache, so Line 193 restores its old settings too. Changing settings after reset and then restoring silently reverts them, despite this workflow being documented as preserving settings and the UI promising to replace only the workspace list.

Proposed fix
       const restored = migrateStoredData(parsed);
+      const current = await this.load();
+      restored.settings = { ...current.settings };
       await this.saveUnlocked(restored, { preserveSecurity: false, allowSubmittedSecurity: true });
🤖 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 `@QuickShell.Raycast/src/lib/storage.ts` around lines 192 - 193, Update the
backup restore flow around migrateStoredData and saveUnlocked so restored data
replaces only the workspace list while preserving the current settings. Capture
or reuse the existing settings before restoration, then merge them into the
migrated backup data before calling saveUnlocked, without altering the
documented reset behavior.

632-642: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Move scheduleRecentWriteFlush() out of withWriteLock() in markWorkspaceUsed(). AsyncLocalStorage.run() context carries into the timer callback, so flushRecentWrites() can hit the nested-lock guard and leave recentWriteDirty set without persisting lastUsedUtc. Schedule the debounce after the lock releases, and add a Vitest regression for the idle debounce path.

🤖 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 `@QuickShell.Raycast/src/lib/storage.ts` around lines 632 - 642, The
markWorkspaceUsed method schedules the recent-write debounce while still inside
withWriteLock, allowing the timer callback to inherit the lock context and skip
persistence. Keep the workspace update and recentWriteDirty assignment inside
the lock, move scheduleRecentWriteFlush() to run after withWriteLock completes,
and add a Vitest regression covering persistence through the idle debounce path.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@QuickShell.Raycast/src/lib/storage.ts`:
- Around line 192-193: Update the backup restore flow around migrateStoredData
and saveUnlocked so restored data replaces only the workspace list while
preserving the current settings. Capture or reuse the existing settings before
restoration, then merge them into the migrated backup data before calling
saveUnlocked, without altering the documented reset behavior.
- Around line 632-642: The markWorkspaceUsed method schedules the recent-write
debounce while still inside withWriteLock, allowing the timer callback to
inherit the lock context and skip persistence. Keep the workspace update and
recentWriteDirty assignment inside the lock, move scheduleRecentWriteFlush() to
run after withWriteLock completes, and add a Vitest regression covering
persistence through the idle debounce path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 57358b53-d29c-4907-a99b-f8059e024a76

📥 Commits

Reviewing files that changed from the base of the PR and between 2db6dca and 6180429.

📒 Files selected for processing (3)
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/lib/storage.ts
  • QuickShell.Raycast/src/open-workspace.tsx
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Analyze Raycast TypeScript with CodeQL
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: Raycast lint, test, and build
  • GitHub Check: Performance harness (artifacts)
  • GitHub Check: .NET build and test
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/open-workspace.tsx
  • QuickShell.Raycast/src/lib/storage.ts
  • QuickShell.Raycast/src/__tests__/storage.test.ts
🔍 Remote MCP GitHub Copilot

Additional review context

  • Issue #37 requires a management action, confirmation, backup creation/preservation, and recovery after reset.
  • Issue #47 specifically targets lost updates from concurrent load-modify-save operations; serialization or an explicit retry/error path is required.
  • The repository’s baseline QuickShellStorage performs mutations through flushRecentWrites()load()save(), confirming the lost-update risk addressed by this PR.
  • The baseline storage adapter persists arbitrary keys via getItem/setItem, so a separate durable backup key is supported by the existing abstraction.
  • Related PR #116 changes moveFavorite to support top/bottom movement and changes boundary behavior to return null; this overlaps the same storage method and should be checked for merge/API compatibility.
🔇 Additional comments (3)
QuickShell.Raycast/src/lib/storage.ts (1)

1-1: LGTM!

Also applies to: 36-121, 133-168, 265-630

QuickShell.Raycast/src/__tests__/storage.test.ts (1)

403-417: LGTM!

Also applies to: 431-432, 443-443, 463-463, 494-494, 506-511

QuickShell.Raycast/src/open-workspace.tsx (1)

81-81: LGTM!

Also applies to: 126-146, 597-658, 829-842

Expand the persistence tour and host parity docs so reset-all, restart-safe restore, backup key ownership, and mutation serialization are explicit as-built behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread QuickShell.Raycast/src/lib/storage.ts
Comment thread QuickShell.Raycast/src/lib/storage.ts
tonythethompson and others added 3 commits July 28, 2026 20:21
Accept Core layout envelopes (`entries`, including Workspace wrappers) so CmdPal exports import correctly, and make the Windows file picker async with a startup toast so the UI does not freeze during PowerShell cold start.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread QuickShell.Raycast/src/lib/import-export.ts Outdated
Comment thread QuickShell.Raycast/src/open-workspace.tsx
WinForms and osascript steal the foreground; activate Raycast again when the picker closes so the Quick Shell screen resurfaces for toasts and confirmations.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread QuickShell.Raycast/src/lib/workspace-transfer-files.ts Outdated
Comment thread QuickShell.Raycast/src/lib/workspace-transfer-files.ts Outdated
Keep CmdPal layout rows aligned when duplicates are skipped, hide transfer
toasts on cancel, narrow focus-restore error handling, and preserve current
settings when restoring a reset backup.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tonythethompson

Copy link
Copy Markdown
Owner Author

Follow-up triage (open Qodo / CodeRabbit threads)

Fixed (e005e2f)

  • CmdPal layout shift on skipped duplicates: layout rows now carry stable workspace ids through merge; skipped duplicates are omitted instead of shifting later rows/separators.
  • Import cancel leaves loading toast: import/export loading toasts always hide() in finally.
  • Windows AppActivate blanket catch: narrowed to COMException / MethodInvocationException.
  • macOS focus failure cancels transfer: Raycast activate is wrapped in its own try so a focus error cannot discard a valid chosenPath.
  • Restore overwrites settings: restoreFromBackup keeps the current settings (matches “replace workspace list” UX); documented in persistence.md.

Already fixed / skipped

  • Sourcery same-workspace concurrency + resetAll settings/backup assertions (already on branch).
  • Earlier strikethrough Qodo items (reentrancy, corrupt backup discard, trust defaulting, docs) already addressed in prior commits.
  • CodeRabbit “clear trust on restore” previously declined (local backup recovery, not ingress).

Comment thread QuickShell.Raycast/src/lib/workspace-transfer-files.ts Outdated
Comment thread QuickShell.Raycast/src/open-workspace.tsx
Comment thread QuickShell.Raycast/src/open-workspace.tsx Outdated
Comment thread QuickShell.Raycast/src/lib/import-export.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
QuickShell.Raycast/src/lib/storage.ts (1)

794-803: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Failed debounced flush is marked dirty but never rescheduled.

scheduleRecentWriteFlush's timer callback catches a flushRecentWrites() failure (e.g. a lock timeout while another writer is stuck) and sets recentWriteDirty = true, but doesn't call scheduleRecentWriteFlush() again. Unless another markWorkspaceUsed() happens to fire later, that recency update is silently stranded in memory and lost if the process exits.

🛡️ Proposed fix
     this.recentWriteTimer = setTimeout(() => {
       void this.flushRecentWrites().catch(() => {
         this.recentWriteDirty = true;
+        this.scheduleRecentWriteFlush();
       });
     }, RECENT_WRITE_DEBOUNCE_MS);
🤖 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 `@QuickShell.Raycast/src/lib/storage.ts` around lines 794 - 803, Update
scheduleRecentWriteFlush so the flushRecentWrites catch handler preserves
recentWriteDirty and schedules another debounced flush by invoking
scheduleRecentWriteFlush again. Ensure the retry occurs after the failed attempt
while retaining the existing timer cleanup and debounce behavior.
♻️ Duplicate comments (1)
QuickShell.Raycast/src/open-workspace.tsx (1)

673-685: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

resetAll "noop" outcome still shows a Success-styled toast — same complaint as before, only half-fixed.

The title now reflects outcome === "noop", but style is still hard-coded to Toast.Style.Success. handleRestoreBackup right below correctly derives style from outcome (result.outcome === "restored" ? Success : Failure); apply the same here so "nothing to reset" doesn't read as a completed destructive action.

🛡️ Proposed fix
       const isNoop = result.outcome === "noop";
       await showToast({
-        style: Toast.Style.Success,
+        style: isNoop ? Toast.Style.Failure : Toast.Style.Success,
         title: isNoop ? "Nothing to reset" : "Workspaces reset",
         message: result.message,
       });
🤖 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 `@QuickShell.Raycast/src/open-workspace.tsx` around lines 673 - 685, Update the
toast configuration in the resetAll handler to derive style from result.outcome,
using Success only when workspaces were actually reset and Failure for the
"noop" outcome. Keep the existing title, message, and error handling unchanged,
following the outcome-based pattern in handleRestoreBackup.
🤖 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 `@QuickShell.Raycast/src/__tests__/storage.test.ts`:
- Around line 591-615: The existing test covers lock timeout but not the
re-entrancy guard. Add a regression test near the `withWriteLock` timeout test
that triggers a mutator invoking `this.save()` while an operation already holds
the write lock, then assert the promise rejects with the “Nested
QuickShellStorage write lock” error. Ensure the test uses the existing
storage/workspace setup and does not rely on hanging timers.

In `@QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts`:
- Around line 34-56: The runtime fallback behavior of
pickWindowsTransferJsonPath is untested. Mock node:child_process using the
existing Vitest conventions and add a case where the initial pwsh invocation
rejects with a non-ENOENT error containing stdout, then verify the valid
selection is preserved while the powershell.exe fallback and error-swallowing
path are exercised.

In `@QuickShell.Raycast/src/lib/import-export.ts`:
- Around line 125-129: Update the workspace import logic around rawId,
workspaceId, and the workspaces/layoutEntries pushes to track IDs already
assigned and generate a fresh stable ID whenever a valid source ID repeats,
ensuring every layout workspaceId is unique and existing remapping preserves
each row’s workspace. Add a regression test covering duplicate source IDs and
verifying both imported workspaces receive distinct IDs with correctly
associated layout rows.

In `@QuickShell.Raycast/src/lib/workspace-transfer-files.ts`:
- Around line 100-111: Add a diagnostic console.error in the catch block
handling the PowerShell invocation, immediately before returning null for
non-ENOENT errors. Include the caught error and relevant context so real script
or environment failures are distinguishable from user cancellation, while
preserving the existing return behavior and ENOENT fallback.
- Around line 54-64: In the dialog command sequence returned by the workspace
transfer helper, emit the selected path before invoking
reactivateRaycastPowerShellSnippet(). Preserve the existing conditional
selection behavior, then run the focus-restoration step afterward so a valid
path is written even if reactivation fails.

In `@QuickShell.Raycast/src/open-workspace.tsx`:
- Around line 466-503: Update handleOpenCompanions to wrap storage and
post-launch operations in the same try/catch pattern used by handleOpenFolder,
handleOpenUrl, and handleTrust. On any thrown error from
storage.getStoredWorkspace or runPostLaunchActions, invoke showStorageFailure
and prevent the error from becoming an unhandled rejection, while preserving the
existing success and expected-failure toasts.
- Around line 473-477: Remove the unsupported companionSelection option from the
authorizePostLaunchEffects call, unless companion selection is intentionally
required; in that case, add it to the function’s options type and implementation
in authorizePostLaunchEffects within security.ts.

---

Outside diff comments:
In `@QuickShell.Raycast/src/lib/storage.ts`:
- Around line 794-803: Update scheduleRecentWriteFlush so the flushRecentWrites
catch handler preserves recentWriteDirty and schedules another debounced flush
by invoking scheduleRecentWriteFlush again. Ensure the retry occurs after the
failed attempt while retaining the existing timer cleanup and debounce behavior.

---

Duplicate comments:
In `@QuickShell.Raycast/src/open-workspace.tsx`:
- Around line 673-685: Update the toast configuration in the resetAll handler to
derive style from result.outcome, using Success only when workspaces were
actually reset and Failure for the "noop" outcome. Keep the existing title,
message, and error handling unchanged, following the outcome-based pattern in
handleRestoreBackup.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 97ad60a6-b767-4c62-bd45-0b8bc1b3f284

📥 Commits

Reviewing files that changed from the base of the PR and between 6180429 and e005e2f.

📒 Files selected for processing (11)
  • QuickShell.Raycast/src/__tests__/import-export.test.ts
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
  • QuickShell.Raycast/src/lib/import-export.ts
  • QuickShell.Raycast/src/lib/migration.ts
  • QuickShell.Raycast/src/lib/storage.ts
  • QuickShell.Raycast/src/lib/workspace-transfer-files.ts
  • QuickShell.Raycast/src/open-workspace.tsx
  • docs/architecture/hosts.md
  • docs/architecture/parity-matrix.md
  • docs/architecture/persistence.md
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: .NET build and test
  • GitHub Check: Performance harness (artifacts)
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
  • QuickShell.Raycast/src/__tests__/import-export.test.ts
  • QuickShell.Raycast/src/lib/migration.ts
  • QuickShell.Raycast/src/lib/import-export.ts
  • QuickShell.Raycast/src/lib/workspace-transfer-files.ts
  • QuickShell.Raycast/src/open-workspace.tsx
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/lib/storage.ts
🪛 ast-grep (0.45.0)
QuickShell.Raycast/src/lib/workspace-transfer-files.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔍 Remote MCP GitHub Copilot

Additional review context

  • Related PR #112 also modifies QuickShellStorage.save(), layout synchronization, and import persistence. Its implementation carries branchTargets and layoutEntries through saves and explicitly excludes branchTargets from portable exports. This overlaps the PR’s lock/refactor paths and may create merge-order/API conflicts.

  • Related PR #116 changes moveFavorite from "up" | "down" returning Workspace to supporting "top" | "bottom" and returning Workspace | null for boundary no-ops. The PR under review refactors the same method under the write-lock, so compatibility with #116 must be checked carefully.

  • Repository code search found no additional occurrences of pickWorkspaceTransferJsonPath, moveFavorite, resetAll, or restoreFromBackup beyond the available indexed results.

  • DeepWiki could not provide repository context because tonythethompson/QuickShell is not indexed; no architectural facts were obtained from that source.

🔇 Additional comments (22)
QuickShell.Raycast/src/lib/import-export.ts (2)

1-1: LGTM!


245-249: LGTM!

QuickShell.Raycast/src/__tests__/import-export.test.ts (3)

114-155: LGTM!


157-196: LGTM!


198-229: LGTM!

QuickShell.Raycast/src/lib/workspace-transfer-files.ts (6)

66-78: LGTM on the narrowed catch types themselves (matches the test's explicit rejection of a bare catch {}); see the adjacent comment on lines 54-64 for the ordering hazard this creates downstream.


1-11: LGTM!


9-9: 🎯 Functional Correctness | ⚡ Quick win

DialogKind is module-private but leaks into exported function signatures.

DialogKind (line 9) isn't exported, yet it's used as the parameter type of pickWorkspaceTransferJsonPath, buildWindowsTransferPowerShell, and buildMacTransferOsascript, all newly exported here. If this project ever emits declaration files ("declaration": true), TypeScript raises TS4025 ("exported function has or is using private name 'DialogKind'"). Trivial to future-proof by exporting the type.

🔧 Suggested fix
-type DialogKind = "save" | "open";
+export type DialogKind = "save" | "open";

Also applies to: 20-31, 139-139


117-136: LGTM!


139-168: LGTM!


170-183: LGTM!

QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts (1)

1-32: LGTM!

QuickShell.Raycast/src/lib/storage.ts (3)

697-742: Nested-lock guard + timeout resolve prior critical/major concerns.

AsyncLocalStorage-based re-entrancy detection plus the Promise.race timeout give a loud failure instead of a silent deadlock, and the gate/writeTail chaining is structured so a timed-out waiter's own release can't let a later caller jump ahead of a genuinely stuck predecessor (the chain still depends on the stuck writer's gate). Good fix for both previously flagged issues.


144-170: Resolves prior critical/major flags on restore trust and save() flushing.

restoreFromBackup()'s structural validation plus migrateStoredData(parsed, { defaultToTrusted: false }) correctly stops trust rehydration from an untrusted backup blob (matches the new "does not rehydrate trust" test). save() now flushes pending recent writes before persisting, closing the stale-snapshot gap from the earlier review round.

Also applies to: 173-218, 283-296


76-79: Consistent write-lock/flush composition across all mutators.

hasBackup, undo/redo, importJson, grantTrust/revokeTrust, workspace CRUD, branch-target and layout/favorite mutations, and updateSettings all correctly compose via withWriteLock + the private *Unlocked helpers rather than the public save/flushRecentWrites, so nothing here can trigger the new nested-lock guard.

Also applies to: 81-142, 333-663, 675-690

QuickShell.Raycast/src/__tests__/storage.test.ts (1)

393-437: Solid coverage for serialization, backup/restore, and lock-timeout behavior.

The rewritten writeOrder/persisted-blob assertions at 393-437 now actually depend on the lock (addresses the prior comment). Backup no-op/reset/restore/trust and malformed-backup tests are thorough and match storage.ts semantics exactly, including the "no rehydrate trust without explicit backup security" case.

Also applies to: 439-589, 591-615

QuickShell.Raycast/src/lib/migration.ts (1)

18-27: LGTM!

Also applies to: 47-66

QuickShell.Raycast/src/open-workspace.tsx (2)

573-604: 🎯 Functional Correctness | ⚡ Quick win

Confirm the loading toast isn't hidden after the follow-up toast is already shown.

loading is created as an Animated toast and updated in place (loading.title = …) while waiting on the async file dialog — good, that matches Raycast's documented "update the same Toast" pattern. But on completion, a separate showToast({...}) call creates a second toast for the result, and loading.hide() only runs afterward in finally. Since Raycast shows one toast at a time, calling hide() on the superseded handle after the new one is already visible risks dismissing the just-shown result toast (or is at best a no-op) — worth confirming against current Raycast behavior, and simpler either way to just call loading.hide() right before showing the result toast (or update loading in place instead of creating a second toast).

Also applies to: 605-652


654-671: Loading-state guard and destructive styling for Reset/Restore both look right.

handleResetAll now bails out with a toast when data isn't loaded yet instead of understating the delete count, and Restore Backup… now carries Action.Style.Destructive alongside Reset All Workspaces… — both past comments addressed.

Also applies to: 886-899

docs/architecture/hosts.md (1)

13-13: LGTM!

Also applies to: 58-69

docs/architecture/parity-matrix.md (1)

24-25: LGTM!

Also applies to: 45-45

docs/architecture/persistence.md (1)

144-158: LGTM!

Comment thread QuickShell.Raycast/src/__tests__/storage.test.ts
Comment thread QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
Comment thread QuickShell.Raycast/src/lib/import-export.ts
Comment thread QuickShell.Raycast/src/lib/workspace-transfer-files.ts Outdated
Comment thread QuickShell.Raycast/src/lib/workspace-transfer-files.ts
Comment thread QuickShell.Raycast/src/open-workspace.tsx
Comment thread QuickShell.Raycast/src/open-workspace.tsx Outdated
Allow Open Directory on POSIX paths, hide elevation UX on Mac, enable Terminal/iTerm tab multi-launch, expand Mac discover roots, and gate macOS CI with lint and build.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo found 2 finding(s) but none met the inline_comments_severity_threshold (current: None). Lower the threshold or switch comments_location_policy to 'summary' or 'both' to see them.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Anthony Thompson <github@trackdub.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

suggest new title and description

I can’t update the PR title or description here, but I suggest:

Title

Add workspace reset/backup recovery and robust import/export handling

Description

Summary

  • Add serialized storage writes to prevent overlapping mutations from clobbering data.
  • Add Reset All with durable backup recovery, validation, and trust-preserving restore behavior.
  • Improve Windows and macOS transfer dialogs with async execution, cancellation handling, and focus restoration.
  • Support CmdPal layout-envelope imports, separators, duplicate IDs, and skipped entries without corrupting layout.
  • Preserve launch taskType values through workspace forms and project suggestions.
  • Expand coverage for storage locking, backup recovery, import/export behavior, transfer dialogs, and form state.

Testing

  • Added and updated Vitest coverage for the affected storage, import/export, transfer, and workspace-form flows.

macOS CI ray lint failed on discover-git-repos-view.tsx Prettier style after
the usePromise error toast change. Apply the project Prettier layout.

Co-authored-by: Anthony Thompson <github@trackdub.com>
@tonythethompson tonythethompson changed the title Close Raycast storage lock and reset-all gaps (#37, #47) Add workspace reset/backup recovery and robust import/export handling for Raycast Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

⚠️ Branch updated during autofix.

The branch was updated while autofix was in progress. Please try again.

Preserve macOS elevation metadata on save, reject future CmdPal envelope versions, keep separator-only merge layouts from appending phantom workspaces, fix Mac same-as-previous grouping, require existing dirs for companion WSL paths, and tighten Open Companion / reset toast handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread QuickShell.Raycast/src/open-workspace.tsx Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit c9fa50d

@tonythethompson

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Animated style left a forever spinner when resetAll returned outcome noop.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 1317fbf

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@AGENTS.md`:
- Line 7: Update the product overview in AGENTS.md to distinguish the
Windows-only .NET desktop hosts—QuickShell, QuickShell.Run, and
QuickShell.Suggest—from the separate QuickShell.Raycast TypeScript host, which
supports macOS; avoid describing the entire product as Windows-only.

In `@QuickShell.Raycast/src/lib/mac-launch.ts`:
- Around line 93-100: Update the launch planning flow around the entry loop and
separateWindows handling so every entry’s Mac terminal host is resolved
sequentially, carrying previousHostId through same-as-previous entries before
any window grouping occurs. Then group the already-resolved sequence normally or
emit one-entry groups when separateWindows is enabled, preserving existing
launch planning behavior. Add a regression test covering an iTerm entry followed
by same-as-previous with separateWindows enabled.

In `@QuickShell.Raycast/src/open-workspace.tsx`:
- Around line 681-686: Update the toast style selection in the reset flow around
isNoop so the no-op “Nothing to reset” path uses a non-animated Toast.Style,
while preserving the existing success style for completed resets and the current
title/message values.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a1527afe-3f02-4810-b920-3622bde398d9

📥 Commits

Reviewing files that changed from the base of the PR and between 364ecd9 and c9fa50d.

📒 Files selected for processing (16)
  • AGENTS.md
  • QuickShell.Raycast/src/__tests__/import-export.test.ts
  • QuickShell.Raycast/src/__tests__/security.test.ts
  • QuickShell.Raycast/src/__tests__/single-row-launch.test.ts
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/components/workspace-form.tsx
  • QuickShell.Raycast/src/lib/import-export.ts
  • QuickShell.Raycast/src/lib/mac-launch.ts
  • QuickShell.Raycast/src/lib/migration.ts
  • QuickShell.Raycast/src/lib/security.ts
  • QuickShell.Raycast/src/lib/workspace-transfer-files.ts
  • QuickShell.Raycast/src/open-workspace.tsx
  • docs/architecture/hosts.md
  • docs/architecture/persistence.md
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⚠️ CI failures not shown inline (2)

GitHub Actions: CodeQL / Analyze C# with CodeQL: Add workspace reset/backup recovery and robust import/export handling for Raycast

Conclusion: failure

View job details

##[group]Run dotnet test QuickShell.Core.Tests/QuickShell.Core.Tests.csproj -c Release -p:Platform=x64 --no-build --verbosity minimal
 �[36;1mdotnet test QuickShell.Core.Tests/QuickShell.Core.Tests.csproj -c Release -p:Platform=x64 --no-build --verbosity minimal�[0m
 shell: C:\Program Files\PowerShell\7\pwsh.EXE -command ". '{0}'"
 env:
   DOTNET_NOLOGO: true
   DOTNET_CLI_TELEMETRY_OPTOUT: true
   NUGET_XMLDOC_MODE: skip
   DOTNET_ROOT: C:\Program Files\dotnet
   CODEQL_ACTION_FEATURE_MULTI_LANGUAGE: false
   CODEQL_ACTION_FEATURE_SANDWICH: false
   CODEQL_ACTION_FEATURE_SARIF_COMBINE: true
   CODEQL_ACTION_FEATURE_WILL_UPLOAD: true
   CODEQL_ACTION_VERSION: 4.37.3
   JOB_RUN_UUID: 75310bd3-66b5-478f-819a-784e63d6c580
   CODEQL_ACTION_INIT_HAS_RUN: true
   CODEQL_ACTION_ANALYSIS_KEY: .github/workflows/codeql.yml:analyze-csharp
   CODEQL_WORKFLOW_STARTED_AT:
   CODEQL_ACTION_CLI_VERSION_INFO: {"cmd":"C:\\hostedtoolcache\\windows\\CodeQL\\2.26.1\\x64\\codeql\\codeql.exe","version":{"productName":"CodeQL","vendor":"GitHub","version":"2.26.1","sha":"1ffdebae722922fb50a64b14a97789ed6161f4a7","branches":["codeql-cli-2.26.1"],"copyright":"Copyright (C) 2019-2026 GitHub, Inc.","unpackedLocation":"C:\\hostedtoolcache\\windows\\CodeQL\\2.26.1\\x64\\codeql","configFileLocation":"C:\\Users\\runneradmin\\.config\\codeql\\config","configFileFound":false,"overlayVersion":4,"features":{"analysisSummaryV2Default":true,"buildModeOption":true,"bundleSupportsIncludeDiagnostics":true,"bundleSupportsIncludeLogs":true,"bundleSupportsOverlay":true,"databaseInterpretResultsSupportsSarifRunProperty":true,"featuresInVersionResult":true,"indirectTracingSupportsStaticBinaries":false,"informsAboutUnsupportedPathFilters":true,"supportsPython312":true,"mrvaPackCreate":true,"threatModelOption":true,"traceCommandUseBuildMode":true,"v2ramSizing":true,"mrvaPackCreateMultipleQueries":true,"setsCodeqlRunnerEnvVar":true,"sarifMergeRunsFromEqualCategory":true,"forceOverwrite":true,"generateSummarySymb...

GitHub Actions: CodeQL / 0_Analyze C# with CodeQL.txt: Add workspace reset/backup recovery and robust import/export handling for Raycast

Conclusion: failure

View job details

##[group]Run dotnet test QuickShell.Core.Tests/QuickShell.Core.Tests.csproj -c Release -p:Platform=x64 --no-build --verbosity minimal
 �[36;1mdotnet test QuickShell.Core.Tests/QuickShell.Core.Tests.csproj -c Release -p:Platform=x64 --no-build --verbosity minimal�[0m
 shell: C:\Program Files\PowerShell\7\pwsh.EXE -command ". '{0}'"
 env:
   DOTNET_NOLOGO: true
   DOTNET_CLI_TELEMETRY_OPTOUT: true
   NUGET_XMLDOC_MODE: skip
   DOTNET_ROOT: C:\Program Files\dotnet
   CODEQL_ACTION_FEATURE_MULTI_LANGUAGE: false
   CODEQL_ACTION_FEATURE_SANDWICH: false
   CODEQL_ACTION_FEATURE_SARIF_COMBINE: true
   CODEQL_ACTION_FEATURE_WILL_UPLOAD: true
   CODEQL_ACTION_VERSION: 4.37.3
   JOB_RUN_UUID: 75310bd3-66b5-478f-819a-784e63d6c580
   CODEQL_ACTION_INIT_HAS_RUN: true
   CODEQL_ACTION_ANALYSIS_KEY: .github/workflows/codeql.yml:analyze-csharp
   CODEQL_WORKFLOW_STARTED_AT:
   CODEQL_ACTION_CLI_VERSION_INFO: {"cmd":"C:\\hostedtoolcache\\windows\\CodeQL\\2.26.1\\x64\\codeql\\codeql.exe","version":{"productName":"CodeQL","vendor":"GitHub","version":"2.26.1","sha":"1ffdebae722922fb50a64b14a97789ed6161f4a7","branches":["codeql-cli-2.26.1"],"copyright":"Copyright (C) 2019-2026 GitHub, Inc.","unpackedLocation":"C:\\hostedtoolcache\\windows\\CodeQL\\2.26.1\\x64\\codeql","configFileLocation":"C:\\Users\\runneradmin\\.config\\codeql\\config","configFileFound":false,"overlayVersion":4,"features":{"analysisSummaryV2Default":true,"buildModeOption":true,"bundleSupportsIncludeDiagnostics":true,"bundleSupportsIncludeLogs":true,"bundleSupportsOverlay":true,"databaseInterpretResultsSupportsSarifRunProperty":true,"featuresInVersionResult":true,"indirectTracingSupportsStaticBinaries":false,"informsAboutUnsupportedPathFilters":true,"supportsPython312":true,"mrvaPackCreate":true,"threatModelOption":true,"traceCommandUseBuildMode":true,"v2ramSizing":true,"mrvaPackCreateMultipleQueries":true,"setsCodeqlRunnerEnvVar":true,"sarifMergeRunsFromEqualCategory":true,"forceOverwrite":true,"generateSummarySymb...
🧰 Additional context used
📓 Path-based instructions (2)
QuickShell.Raycast/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in TypeScript in parity with Core launch behavior, especially argument escaping, target resolution, and wt launch planning.

Files:

  • QuickShell.Raycast/src/__tests__/single-row-launch.test.ts
  • QuickShell.Raycast/src/__tests__/import-export.test.ts
  • QuickShell.Raycast/src/lib/migration.ts
  • QuickShell.Raycast/src/lib/mac-launch.ts
  • QuickShell.Raycast/src/__tests__/security.test.ts
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/lib/workspace-transfer-files.ts
  • QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
  • QuickShell.Raycast/src/lib/security.ts
  • QuickShell.Raycast/src/lib/import-export.ts
QuickShell.Raycast/src/**/__tests__/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Vitest for Raycast tests and keep Windows launch behavior covered in parity with Core.

Files:

  • QuickShell.Raycast/src/__tests__/single-row-launch.test.ts
  • QuickShell.Raycast/src/__tests__/import-export.test.ts
  • QuickShell.Raycast/src/__tests__/security.test.ts
  • QuickShell.Raycast/src/__tests__/storage.test.ts
  • QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: tonythethompson/QuickShell

Timestamp: 2026-07-29T06:13:56.652Z
Learning: When changing behavior, prefer the as-built architecture tours over proposal documents `docs/architecture/0001`-`0005`, and update the matching tour when changing an architectural spine.
Learnt from: CR
Repo: tonythethompson/QuickShell

Timestamp: 2026-07-29T06:13:56.652Z
Learning: After deploying the CmdPal extension, use `Reload Command Palette Extension`; deploying is required rather than merely building to register the MSIX.
🪛 ast-grep (0.45.0)
QuickShell.Raycast/src/lib/workspace-transfer-files.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 LanguageTool
AGENTS.md

[uncategorized] ~49-~49: The official name of this software platform is spelled with a capital “H”.
Context: ... | | .github/workflows/ | ci.yml (build/test)...

(GITHUB)

🔍 Remote MCP DeepWiki, GitHub Copilot

Additional review context

  • The PR is #133 and currently has successful Raycast Windows/macOS lint, test, build, and TypeScript CodeQL checks. The C# CodeQL check is failing, although the PR changes only Raycast/ documentation files.
  • Prior review findings led to concrete fixes for:
    • write-queue serialization and nested-lock detection;
    • durable malformed-backup disposal;
    • settings-preserving restore;
    • duplicate CmdPal IDs and skipped-layout rows;
    • async transfer fallback/focus restoration;
    • companion directory authorization.
  • One unresolved current finding remains: handleResetAll uses Toast.Style.Animated for the terminal "noop" outcome. Because the toast is not retained or hidden, resetting an empty store may leave an indefinite spinner.
  • The default branch still exposes the old synchronous pickWorkspaceTransferJsonPath() contract and caller; PR #133 updates both to async, so caller consistency must be verified against the final PR revision.
  • DeepWiki could not retrieve the requested repository context because tonythethompson/QuickShell is not indexed/found.
🔇 Additional comments (15)
QuickShell.Raycast/src/lib/import-export.ts (1)

4-4: LGTM!

Also applies to: 83-85, 212-221

QuickShell.Raycast/src/__tests__/import-export.test.ts (1)

114-166: LGTM!

Also applies to: 168-209, 211-250, 252-278, 280-311

QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

30-44: LGTM!

Also applies to: 127-141

QuickShell.Raycast/src/components/workspace-form.tsx (1)

154-200: LGTM!

Also applies to: 334-334, 367-372, 397-397, 524-526, 585-585, 731-731, 782-800

QuickShell.Raycast/src/lib/workspace-transfer-files.ts (1)

112-117: LGTM!

Also applies to: 205-206

QuickShell.Raycast/src/__tests__/workspace-transfer-files.test.ts (1)

17-17: LGTM!

Also applies to: 77-104, 114-142, 144-216

QuickShell.Raycast/src/lib/mac-launch.ts (1)

13-16: LGTM!

QuickShell.Raycast/src/lib/migration.ts (1)

170-175: LGTM!

QuickShell.Raycast/src/__tests__/storage.test.ts (1)

624-626: LGTM!

QuickShell.Raycast/src/lib/security.ts (1)

178-182: LGTM!

Also applies to: 337-337, 495-512

QuickShell.Raycast/src/__tests__/security.test.ts (1)

253-256: LGTM!

Also applies to: 290-293, 323-368, 381-381, 418-440

QuickShell.Raycast/src/__tests__/single-row-launch.test.ts (1)

1-23: LGTM!

docs/architecture/hosts.md (1)

9-19: LGTM!

Also applies to: 33-69, 75-95

docs/architecture/persistence.md (1)

142-158: LGTM!

AGENTS.md (1)

39-49: LGTM!

Also applies to: 124-124

Comment thread AGENTS.md Outdated
Comment thread QuickShell.Raycast/src/lib/mac-launch.ts
Comment thread QuickShell.Raycast/src/open-workspace.tsx
tonythethompson and others added 2 commits July 28, 2026 23:21
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Anthony Thompson <github@trackdub.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Anthony Thompson <github@trackdub.com>
Comment thread QuickShell.Raycast/src/lib/mac-launch.ts Fixed
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Anthony Thompson <github@trackdub.com>
Comment thread QuickShell.Raycast/src/lib/mac-launch.ts Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit ac2d94c

@tonythethompson
tonythethompson merged commit cd4948f into master Jul 29, 2026
12 checks passed
@tonythethompson
tonythethompson deleted the cursor/raycast-storage-reset-and-lock branch July 29, 2026 06:28
@linear-code

linear-code Bot commented Jul 29, 2026

Copy link
Copy Markdown

QUI-6

Comment on lines +168 to +172
"try",
' set chosenFile to choose file name with prompt "Export Quick Shell workspaces" default name defaultName',
" set chosenPath to POSIX path of chosenFile",
"on error",
' set chosenPath to ""',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Remediation Recommended

1. Dialog catches every applescript error 📘 Rule violation ☼ Reliability

The generated macOS save dialog uses an unqualified on error block, converting all AppleScript
failures into an empty path that callers interpret as cancellation. Unexpected permission,
scripting, or runtime failures are therefore silently suppressed.
Agent Prompt
## Issue description
The macOS transfer scripts catch every error and convert it into an empty selection. Handle only the expected user-cancellation error (`-128`) and allow unexpected errors to propagate.

## Issue Context
Both save and open dialog scripts need equivalent narrow error handling so operational failures are not reported as cancellation.

## Fix Focus Areas
- QuickShell.Raycast/src/lib/workspace-transfer-files.ts[168-172]
- QuickShell.Raycast/src/lib/workspace-transfer-files.ts[180-184]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +101 to +103
const existingIndex = groupIndexByHost.get(hostId);
if (existingIndex !== undefined) {
groups[existingIndex].entries.push(entry);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Remediation Recommended

2. Fallback terminals split tabs 🐞 Bug ≡ Correctness

groupMacLaunchEntries groups by the requested host before unavailable iTerm launches fall back to
Terminal.app. With tabs enabled, mixed Terminal/iTerm entries therefore open two Terminal windows
instead of sharing one tabbed window when iTerm is not installed.
Agent Prompt
## Issue description
Mac launch entries are grouped using their requested host ID, but unavailable iTerm hosts later fall back to Terminal.app. Group by the effective installed application so all Terminal.app entries share one tabbed invocation.

## Issue Context
`resolveMacAppName` maps an unavailable iTerm host to Terminal, while `groupMacLaunchEntries` currently creates separate `terminal` and `iterm` groups before that fallback is known.

## Fix Focus Areas
- QuickShell.Raycast/src/lib/mac-launch.ts[52-59]
- QuickShell.Raycast/src/lib/mac-launch.ts[91-108]
- QuickShell.Raycast/src/lib/mac-launch.ts[199-202]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 74a787b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prevent lost updates in workspace storage operations Add reset all workspaces with backup restore

3 participants