Skip to content

fix(ui): workspace list and detail state views - #907

Merged
skevetter merged 4 commits into
mainfrom
ui/fix-workspace-table-loading
Aug 9, 2026
Merged

fix(ui): workspace list and detail state views#907
skevetter merged 4 commits into
mainfrom
ui/fix-workspace-table-loading

Conversation

@skevetter

@skevetter skevetter commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Signed-off-by: GitHub noreply@github.com

Summary by CodeRabbit

  • New Features

    • Added workspace start and stop controls with clearer lifecycle status indicators.
    • Added progress feedback for workspace operations, including rebuild, reset, recovery, IDE opening, and deletion.
    • Added confirmation dialogs for rebuild and delete actions.
    • Added end-to-end coverage for workspace lifecycle workflows.
  • Bug Fixes

    • Workspace statuses are preserved when updates omit status information.
    • Failed workspace actions restore the previous status.
    • Improved cancellation and cleanup for long-running commands and tunnels.
  • Chores

    • Automated Linux desktop test dependency and virtual display setup.

@netlify

netlify Bot commented Aug 8, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 2a48785
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a77bcfca9d6f600089e4d7c

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds desktop dependency and Xvfb setup, extends workspace lifecycle behavior, preserves omitted workspace statuses, propagates command IDs, improves process cancellation cleanup, and adds workspace detail E2E coverage.

Changes

Workspace lifecycle

Layer / File(s) Summary
Desktop dependency and display setup
Taskfile.yml
Adds Linux runtime dependencies, connects them to desktop setup, and prepares Xvfb for E2E tests.
Mock lifecycle commands and E2E coverage
desktop/e2e/fixtures/mock-devsy.cjs, desktop/e2e/workspaces.e2e.ts
Preserves workspace identity during materialization, adds workspace start, and tests detail-page lifecycle actions and confirmations.
Workspace status merge behavior
desktop/src/main/state.ts, desktop/src/main/__tests__/state.test.ts, desktop/src/renderer/src/lib/stores/workspaces.ts, desktop/src/renderer/src/lib/stores/workspaces.test.ts
Preserves cached statuses when updates omit status data in the main process and renderer store.
Lifecycle command propagation and cancellation
desktop/src/renderer/src/lib/ipc/commands.ts, desktop/src/main/ipc.ts, desktop/src/main/cli.ts, desktop/src/main/pty.ts
Propagates optional command IDs and adds timeout-based cleanup, callback suppression, stream disposal, and force-kill handling.
Optimistic lifecycle status UI
desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte, desktop/src/renderer/src/pages/WorkspacesPage.svelte
Adds transitional statuses, failure restoration, fallback status labels, spinners, and state-based control availability.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary UI changes to workspace list and detail state views.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

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

@netlify

netlify Bot commented Aug 8, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit 2a48785
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a77bcfcc00b360008b44ed5

@github-actions github-actions Bot added the size/l label Aug 8, 2026
@skevetter
skevetter marked this pull request as ready for review August 8, 2026 03:24

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🤖 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 `@desktop/e2e/fixtures/mock-devsy.cjs`:
- Around line 209-228: Update materializeWorkspace so existing workspace entries
retain their current source, provider, ide, and context metadata instead of
rebuilding those fields from command arguments. For an existing workspace, merge
only explicitly changed lifecycle fields such as status, lastUsed, and relevant
identifiers; keep the current initialization behavior for new workspaces.

In `@desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte`:
- Around line 693-711: Update the Stop button’s disabled condition near
handleStop so it is enabled only when the workspace is running, including all
transitional states such as starting, stopping, and deleting. Do not rely solely
on operationRunning; use the existing isRunning state and preserve the current
behavior for non-running workspaces.

In `@Taskfile.yml`:
- Around line 149-152: Update the desktop:deps installation list to include
libnotify4, libxtst6, xdg-utils, and libuuid1, keeping it aligned with the
DESKTOP runtime dependencies declared by desktop/electron-builder.yml and the
installation documentation.
- Around line 195-198: Update the Xvfb setup in the task to verify that display
:99 is running specifically, rather than matching any Xvfb process. After
starting Xvfb for :99, add a readiness wait loop before invoking npm run
test:e2e so the test cannot race server initialization.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 52f264e9-b87b-4ddb-9551-42db3785298e

📥 Commits

Reviewing files that changed from the base of the PR and between 1ccdd58 and e6f7ee4.

📒 Files selected for processing (9)
  • Taskfile.yml
  • desktop/e2e/fixtures/mock-devsy.cjs
  • desktop/e2e/workspaces.e2e.ts
  • desktop/src/main/__tests__/state.test.ts
  • desktop/src/main/state.ts
  • desktop/src/renderer/src/lib/stores/workspaces.test.ts
  • desktop/src/renderer/src/lib/stores/workspaces.ts
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
  • desktop/src/renderer/src/pages/WorkspacesPage.svelte

Comment thread desktop/e2e/fixtures/mock-devsy.cjs
Comment on lines +693 to +711
<Button
variant="destructive"
size="sm"
onclick={handleStop}
disabled={operationRunning || (!isRunning && !isBusy)}
>
{#if operationRunning && operationLabel === "Stop"}<Spinner />{:else}<Square class="h-4 w-4" />{/if}
Stop
</Button>

<Button
variant="default"
size="sm"
onclick={handleStart}
disabled={operationRunning || connecting || isRunning || isBusy || !isStopped}
>
{#if operationRunning && operationLabel === "Start"}<Spinner />{:else}<Play class="h-4 w-4" />{/if}
Start
</Button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Disable Stop during every transitional status.

Line 697 enables Stop when isBusy is true. This includes starting, stopping, and deleting. If the page remounts while one of these operations is active, operationRunning is false and the user can dispatch a conflicting Stop command. Enable Stop only when the workspace is Running.

Proposed fix
-            disabled={operationRunning || (!isRunning && !isBusy)}
+            disabled={operationRunning || !isRunning}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button
variant="destructive"
size="sm"
onclick={handleStop}
disabled={operationRunning || (!isRunning && !isBusy)}
>
{#if operationRunning && operationLabel === "Stop"}<Spinner />{:else}<Square class="h-4 w-4" />{/if}
Stop
</Button>
<Button
variant="default"
size="sm"
onclick={handleStart}
disabled={operationRunning || connecting || isRunning || isBusy || !isStopped}
>
{#if operationRunning && operationLabel === "Start"}<Spinner />{:else}<Play class="h-4 w-4" />{/if}
Start
</Button>
<Button
variant="destructive"
size="sm"
onclick={handleStop}
disabled={operationRunning || !isRunning}
>
{`#if` operationRunning && operationLabel === "Stop"}<Spinner />{:else}<Square class="h-4 w-4" />{/if}
Stop
</Button>
<Button
variant="default"
size="sm"
onclick={handleStart}
disabled={operationRunning || connecting || isRunning || isBusy || !isStopped}
>
{`#if` operationRunning && operationLabel === "Start"}<Spinner />{:else}<Play class="h-4 w-4" />{/if}
Start
</Button>
🤖 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 `@desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte` around lines 693 -
711, Update the Stop button’s disabled condition near handleStop so it is
enabled only when the workspace is running, including all transitional states
such as starting, stopping, and deleting. Do not rely solely on
operationRunning; use the existing isRunning state and preserve the current
behavior for non-running workspaces.

Comment thread Taskfile.yml Outdated
Comment thread Taskfile.yml
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 3 file(s) based on 4 unresolved review comments.

Files modified:

  • Taskfile.yml
  • desktop/e2e/fixtures/mock-devsy.cjs
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte

Commit: 445c6b43f53b903d6522cd4148ed1f3cc4330cfd

The changes have been pushed to the ui/fix-workspace-table-loading branch.

Time taken: 4m 42s

@skevetter
skevetter marked this pull request as draft August 8, 2026 16:47

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
desktop/src/renderer/src/lib/ipc/commands.ts (1)

50-52: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add explicit command ID assertions.

The adjacent test in desktop/src/renderer/src/lib/ipc/commands.test.ts, Lines [80-85], only calls workspaceStop("ws-1"). It does not verify the new commandId field. Add explicit-ID assertions for all five workspace wrappers, or confirm that equivalent coverage exists elsewhere.

Example assertion
+it("workspaceStop forwards commandId", async () => {
+  await workspaceStop("ws-1", false, "cmd-1")
+  expect(mockInvoke).toHaveBeenCalledWith("workspace_stop", {
+    workspaceId: "ws-1",
+    debug: false,
+    commandId: "cmd-1",
+  })
+})

Also applies to: 58-60, 66-68, 74-76, 82-84

🤖 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 `@desktop/src/renderer/src/lib/ipc/commands.ts` around lines 50 - 52, Add
explicit commandId coverage for all five workspace wrapper functions in the
related tests, including workspaceUp and the wrappers at the additional
referenced locations. Invoke each with a command ID and assert the underlying
IPC call receives that ID, or reuse equivalent existing coverage if already
present.
.github/workflows/commit.yml (1)

27-27: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound the GitHub API request.

Add connection and transfer timeouts, bounded retries, and --fail or --fail-with-body. Curl does not treat HTTP error responses as command failures by default, so an API error is passed to jq and can produce an opaque failure. Validate the response shape before filtering. (curl.se)

🤖 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 @.github/workflows/commit.yml at line 27, Update the curl invocation in the
unsigned_commits assignment to use connection and transfer timeouts, bounded
retries, and HTTP failure handling via --fail or --fail-with-body. Validate that
the API response has the expected array shape before passing it to jq,
preserving the existing commit filtering behavior.
🤖 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 @.github/workflows/commit.yml:
- Line 27: Update the unsigned_commits retrieval in the commit workflow to
paginate the pull-request commits endpoint through its 250-commit limit, then
query the repository commits endpoint for any remaining pull-request commits.
Combine all fetched commits before applying the existing verification and author
exclusions, preserving the current approval behavior for signed commits and
exempt authors.
- Line 27: Update the unsigned-commit filter in the workflow’s unsigned_commits
assignment to exclude only the trusted bot identity via an exact match on the
appropriate GitHub author/committer login or ID, or canonical bot email;
explicitly apply the exception to the author, committer, or both, and remove
substring checks against raw commit author name or email.

In `@desktop/src/main/ipc.ts`:
- Around line 232-248: Prevent quiescence waits from resolving while child
processes can still emit workspace callbacks: in desktop/src/main/ipc.ts lines
232-248, retain ownership of tunnelProc, terminate it, or suppress late
sink.done callbacks before resolving; in desktop/src/main/cli.ts lines 350-366,
block late onLine callbacks and release child tracking when the process does not
close; in desktop/src/main/pty.ts lines 107-117, fully detach or terminate the
PTY before resolution. Add regression tests covering each process remaining open
beyond two seconds and asserting no workspace callbacks occur after quiescence.

---

Nitpick comments:
In @.github/workflows/commit.yml:
- Line 27: Update the curl invocation in the unsigned_commits assignment to use
connection and transfer timeouts, bounded retries, and HTTP failure handling via
--fail or --fail-with-body. Validate that the API response has the expected
array shape before passing it to jq, preserving the existing commit filtering
behavior.

In `@desktop/src/renderer/src/lib/ipc/commands.ts`:
- Around line 50-52: Add explicit commandId coverage for all five workspace
wrapper functions in the related tests, including workspaceUp and the wrappers
at the additional referenced locations. Invoke each with a command ID and assert
the underlying IPC call receives that ID, or reuse equivalent existing coverage
if already present.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d74a76c-5063-41da-9a6b-4a672adc08dc

📥 Commits

Reviewing files that changed from the base of the PR and between e6f7ee4 and be9b674.

📒 Files selected for processing (8)
  • .github/workflows/commit.yml
  • Taskfile.yml
  • desktop/e2e/fixtures/mock-devsy.cjs
  • desktop/src/main/cli.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/pty.ts
  • desktop/src/renderer/src/lib/ipc/commands.ts
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
🚧 Files skipped from review as they are similar to previous changes (3)
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
  • Taskfile.yml
  • desktop/e2e/fixtures/mock-devsy.cjs

Comment thread .github/workflows/commit.yml Outdated
exit 1
fi

unsigned_commits="$(curl -s -H "Authorization: token $GITHUB_TOKEN" "$COMMITS_URL" | jq '.[] | select(.commit.verification.verified == false and (.commit.author.email | contains("google-labs-jules") | not) and (.commit.author.name | contains("google-labs-jules") | not)) | .commit.message')"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow files =="
git ls-files .github/workflows || true

echo "== commit.yml excerpt =="
if [ -f .github/workflows/commit.yml ]; then
  cat -n .github/workflows/commit.yml | sed -n '1,120p'
fi

echo "== search for COMMITS_URL and pull request commit endpoints =="
rg -n "COMMITS_URL|pulls/.*/commits|page=|per_page|Link|unsigned_commits|GITHUB_TOKEN" .github/workflows || true

Repository: devsy-org/devsy

Length of output: 3615


🌐 Web query:

GitHub REST API list pull request commits pagination default per_page limit comparison commits endpoint

💡 Result:

In the GitHub REST API, both the "List commits on a pull request" and "Compare two commits" endpoints support pagination using the per_page and page query parameters [1][2]. For these endpoints, the default value for the per_page parameter is 30, and the maximum allowed value is 100 [1][2]. If a value greater than 100 is specified, GitHub will automatically reduce it to the maximum of 100 without returning an error [3][4]. Key details for these endpoints include: List commits on a pull request: While this endpoint supports pagination, it is documented to list a maximum of 250 commits for a pull request [1][5]. If you need to access a complete commit list for pull requests exceeding this limit, GitHub recommends using the "List commits" endpoint on a repository [1][5][6]. Compare two commits: This endpoint also supports pagination [7][2]. When called without pagination parameters, it is limited to returning 250 commits [2][8]. When you enable pagination by using per_page or page, the results are returned in chronological order, although the list of changed files remains restricted to the first page (with a limit of up to 300 changed files) [2][8].

Citations:


Fetch every pull-request commit before accepting the check.

This request reads only the first API page. GitHub defaults the pull-request commits endpoint to 30 results and caps this endpoint at 250 commits. Paginate up to the endpoint limit and use the repository commits endpoint for any remaining commits. Unsigned commits returned outside this check can therefore bypass approval.

🤖 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 @.github/workflows/commit.yml at line 27, Update the unsigned_commits
retrieval in the commit workflow to paginate the pull-request commits endpoint
through its 250-commit limit, then query the repository commits endpoint for any
remaining pull-request commits. Combine all fetched commits before applying the
existing verification and author exclusions, preserving the current approval
behavior for signed commits and exempt authors.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,80p' .github/workflows/commit.yml

echo
echo "== related workflow files =="
shopt -s nullglob
for f in .github/workflows/*.yml .github/workflows/*.yaml; do
  echo "--- $f ---"
  wc -l "$f"
done

Repository: devsy-org/devsy

Length of output: 2359


🌐 Web query:

GitHub REST API Pulls list commits response author committer verification fields

💡 Result:

When using the GitHub REST API to list commits on a pull request (GET /repos/{owner}/{repo}/pulls/{pull_number}/commits), the response returns an array of commit objects [1][2]. Each commit object contains a nested commit object that includes details about the author, the committer, and the signature verification status [1][3]. Author and Committer Fields Within the nested commit object, both author and committer are represented as objects containing the following fields [1][4]: - name: The name of the user (string) [1]. - email: The email address associated with the user (string) [1]. - date: The date and time the action occurred (string, ISO 8601 format) [1]. These fields can be null if the information is missing from the Git commit data [1][5]. Verification Object The commit object also includes a verification object, which provides the results of GitHub's signature verification process for the commit [6][7]. It contains the following fields: - verified (boolean): Indicates whether GitHub considers the commit signature to be valid [6][7]. - reason (string): The explanation for the verification status (e.g., 'valid', 'unsigned', 'expired_key', 'bad_email') [6][7]. - signature (string or null): The actual signature extracted from the commit [6][7]. - payload (string or null): The data that was signed [6][7]. - verified_at (string or null): The timestamp of when GitHub verified the signature [1][6].

Citations:


🌐 Web query:

docs.github.com GitHub commit author email name can be spoofed raw commit fields

💡 Result:

Yes, it is possible to spoof the author and committer name and email address in a Git commit [1][2][3]. Because Git was designed in a distributed environment where email was the primary form of identity, it does not inherently validate that the metadata provided in a commit matches the actual person pushing the code [4][5]. When you push a commit to GitHub, the platform examines the email address recorded in the commit metadata [2][6]. If that email address is associated with a GitHub account, GitHub will display that account's name, profile picture, and profile link as the commit author or committer [2][7][3]. This can be performed easily using standard Git commands, such as: git config user.name "Name To Spoof" git config user.email "email@example.com" git commit --allow-empty -m "Spoofed commit message" Because this attribution happens automatically, it can be used to impersonate other developers, create misleading contribution graphs, or misrepresent the source of code in a repository [7][3][8]. To protect against this form of spoofing, GitHub provides a feature called Commit Signature Verification [5][9]. By cryptographically signing commits locally using GPG, SSH, or S/MIME keys, a developer provides a verifiable proof of identity [9][10]. When a commit is signed and the signature is verified, GitHub displays a green "Verified" badge [1][9]. Commits that are not signed do not receive this badge [9][10]. For environments requiring higher security, you can enable "Vigilant Mode" in your GitHub account settings under "SSH and GPG keys" [4]. Enabling this feature allows you to flag unsigned commits as "Unverified" with a warning in the user interface, providing a clear visual indicator that the commit's identity has not been cryptographically confirmed [4]. Top results: [1][7][4][9]

Citations:


Use an exact trusted identity for the bot exclusion.

The rule currently skips any unsigned commit whose raw Git author name or email contains google-labs-jules. A contributor can set author metadata with that substring to bypass the check. Compare an exact trusted GitHub login or ID from .author/.committer, or an exact canonical bot email, and define whether the exception applies to the author, committer, or both.

🤖 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 @.github/workflows/commit.yml at line 27, Update the unsigned-commit filter
in the workflow’s unsigned_commits assignment to exclude only the trusted bot
identity via an exact match on the appropriate GitHub author/committer login or
ID, or canonical bot email; explicitly apply the exception to the author,
committer, or both, and remove substring checks against raw commit author name
or email.

Comment thread desktop/src/main/ipc.ts
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 3 file(s) based on 2 unresolved review comments.

Files modified:

  • desktop/src/main/cli.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/pty.ts

Commit: 7deb184194a867cbf1f5bf816dadb02bb3514466

The changes have been pushed to the ui/fix-workspace-table-loading branch.

Time taken: 13m 52s


⚠️ 1 file(s) could not be committed — the agent does not have permission to push to .github/workflows/. Please apply these changes manually:

.github/workflows/commit.yml — 1 change:

Lines 24–30
             exit 1
           fi
 
-          unsigned_commits="$(curl -s -H "Authorization: token $GITHUB_TOKEN" "$COMMITS_URL" | jq '.[] | select(.commit.verification.verified == false and (.commit.author.email | contains("google-labs-jules") | not) and (.commit.author.name | contains("google-labs-jules") | not)) | .commit.message')"
+          # Paginate through all commits (up to 250-commit limit for PR commits endpoint)
+          all_commits="[]"
+          page=1
+          while true; do
+            page_commits="$(curl -s -H "Authorization: token $GITHUB_TOKEN" "$COMMITS_URL?page=$page&per_page=100")"
+            if [[ "$(echo "$page_commits" | jq 'length')" -eq 0 ]]; then
+              break
+            fi
+            all_commits="$(jq -s '.[0] + .[1]' <(echo "$all_commits") <(echo "$page_commits"))"
+            # GitHub's PR commits endpoint caps at 250 commits total
+            if [[ "$(echo "$all_commits" | jq 'length')" -ge 250 ]]; then
+              break
+            fi
+            page=$((page + 1))
+          done
+
+          unsigned_commits="$(echo "$all_commits" | jq '.[] | select(.commit.verification.verified == false and (.commit.author.email | contains("google-labs-jules") | not) and (.commit.author.name | contains("google-labs-jules") | not)) | .commit.message')"
           if [[ -z "$unsigned_commits" ]]; then
             echo "🎉 No unsigned commits found (excluding bot commits)"
             exit 0

@github-actions github-actions Bot added size/xl and removed size/l labels Aug 8, 2026
@skevetter
skevetter marked this pull request as ready for review August 8, 2026 19:12

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
desktop/src/main/ipc.ts (1)

231-257: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Track close before skipping cleanup.

This is a residual case in the earlier cancellation defect. The timeout sets settled to true. If the child exits but a descendant retains its stdout or stderr pipe, exitCode can be set while close is still pending. Lines 257-283 then skip suppression and stream cleanup. workspace_delete can start while late onLine callbacks still append to the removed log.

Set a separate closed flag only in the "close" handler. Run suppression and stream cleanup whenever the timeout resolves without observing "close".

Proposed fix
-      let settled = false
+      let closed = false
       const tunnelExit = new Promise<void>((resolve) => {
         let timer: ReturnType<typeof setTimeout> | null = setTimeout(() => {
           timer = null
-          settled = true
           resolve()
         }, 2000)
 
-        if (tunnelProc.exitCode !== null || tunnelProc.signalCode !== null) {
-          if (timer) clearTimeout(timer)
-          settled = true
-          resolve()
-          return
-        }
         tunnelProc.once("close", () => {
           if (timer) {
             clearTimeout(timer)
             timer = null
           }
-          settled = true
+          closed = true
           resolve()
         })
       })
       tunnelProc.kill("SIGTERM")
       await tunnelExit
-      if (!settled || (tunnelProc.exitCode === null && tunnelProc.signalCode === null)) {
+      if (!closed) {

Also applies to: 1024-1030, 1077-1077, 1090-1093

🤖 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 `@desktop/src/main/ipc.ts` around lines 231 - 257, Update the tunnel shutdown
flow around tunnelExit to track a separate closed flag that is set only by the
tunnelProc "close" handler, rather than using settled to represent process
closure. After awaiting tunnelExit, run late-callback suppression and
stdout/stderr stream cleanup whenever closed is false, including cases where
exitCode or signalCode is already set; apply the same close-aware logic to the
related cleanup paths.
🤖 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 `@desktop/src/main/cli.ts`:
- Around line 408-409: The force-kill timeout cleanup must run lifecycle
handlers before suppressing callbacks. In desktop/src/main/cli.ts lines 408-409,
invoke finish(...) for the CLI child, clear its sessionsByWorkspace entry, and
delete this.sessions before removing listeners and sending SIGKILL; in
desktop/src/main/pty.ts lines 129-130, send terminal:exit for the PTY child
before listener removal and SIGKILL.

---

Duplicate comments:
In `@desktop/src/main/ipc.ts`:
- Around line 231-257: Update the tunnel shutdown flow around tunnelExit to
track a separate closed flag that is set only by the tunnelProc "close" handler,
rather than using settled to represent process closure. After awaiting
tunnelExit, run late-callback suppression and stdout/stderr stream cleanup
whenever closed is false, including cases where exitCode or signalCode is
already set; apply the same close-aware logic to the related cleanup paths.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c6b28851-b9cb-44fb-a753-dafee3a6839b

📥 Commits

Reviewing files that changed from the base of the PR and between be9b674 and 7deb184.

📒 Files selected for processing (3)
  • desktop/src/main/cli.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/pty.ts

Comment thread desktop/src/main/cli.ts
@skevetter
skevetter marked this pull request as draft August 8, 2026 19:18
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 2 file(s) based on 1 unresolved review comment.

⚠️ Build/verification step failed, but autofix code changes were still committed.

Files modified:

  • desktop/src/main/cli.ts
  • desktop/src/main/pty.ts

Commit: 3bbd39d94935d9fe695a59c4696dbb3ea4cb9e95

The changes have been pushed to the ui/fix-workspace-table-loading branch.

Time taken: 1m 58s

@skevetter
skevetter force-pushed the ui/fix-workspace-table-loading branch from 3bbd39d to 5790eae Compare August 8, 2026 22:34
@skevetter
skevetter force-pushed the ui/fix-workspace-table-loading branch from 5790eae to b90db8b Compare August 8, 2026 22:36
@skevetter
skevetter marked this pull request as ready for review August 8, 2026 22:52

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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 `@desktop/src/main/cli.ts`:
- Around line 364-393: Separate timeout state from the promise’s completion
state in the child-wait logic: add a timedOut flag set by the 2-second timer,
then update the post-resolution live-child check to call finish(-1, { code:
"timeout", message: "Process did not exit in time" }) when timedOut is true and
the child has not exited. Keep settled for suppressing duplicate cleanup and
preserve normal close/exit handling.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b7a1e10-0fa7-40c2-882f-80d86084f091

📥 Commits

Reviewing files that changed from the base of the PR and between 7deb184 and b90db8b.

📒 Files selected for processing (3)
  • Taskfile.yml
  • desktop/src/main/cli.ts
  • desktop/src/main/pty.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • Taskfile.yml
  • desktop/src/main/pty.ts

Comment thread desktop/src/main/cli.ts
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • desktop/src/main/cli.ts

Commit: de7c9644bae736a86b1b3ea17679582501617936

The changes have been pushed to the ui/fix-workspace-table-loading branch.

Time taken: 4m 45s

@codacy-production

codacy-production Bot commented Aug 8, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 61 complexity · 5 duplication

Metric Results
Complexity 61
Duplication 5

View in Codacy

AI Reviewer: run a review on demand. To trigger the first review automatically, go to your organization or repository integration settings. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codacy-production codacy-production 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.

Pull Request Overview

The PR successfully implements the core workspace lifecycle UI and state management features. However, a critical logic error and scope issue in the CLI runner (cli.ts) will cause ReferenceError crashes and resource deadlocks during process timeouts, directly undermining the reliability goals of the PR.

While Codacy marks the PR as 'up to standards', there are significant maintainability concerns. desktop/src/main/ipc.ts has grown to over 1000 lines and is listed as an uncovered complex file, increasing the risk of regression. Additionally, while the UI components implement manual rollback logic for failed actions, there are no tests ensuring this behavior functions correctly, leaving a gap in the acceptance criteria fulfillment.

About this PR

  • The forceful process termination logic (SIGKILL after 2s) is a critical safety feature to prevent orphaned processes and log file locks, but the timeout-triggered code path is not exercised by any tests in the diff.
  • The logic for restoring workspace status on failure is implemented manually across multiple UI components (WorkspaceDetailPage and WorkspacesPage) but lacks dedicated unit or E2E tests to ensure the rollback behavior works as expected.
2 comments outside of the diff
desktop/src/main/cli.ts

line 58 🟡 MEDIUM RISK
The extractCliErrorFromStderr function is 271 lines long. Managing many different error patterns in a single function makes it difficult to maintain and test individual cases. Consider refactoring this to use a data-driven approach with a registry of regex-based error matchers.

desktop/src/main/ipc.ts

line 1 🟡 MEDIUM RISK
This file has reached 1077 lines, which significantly exceeds the recommended limit of 500. It currently handles a wide range of responsibilities including workspace lifecycle, logging, SSH tunneling, and status tracking. Breaking this into smaller, domain-specific modules (e.g., workspaces.ipc.ts, logs.ipc.ts) would reduce the risk of merge conflicts.

Test suggestions

  • Verify DaemonState preserves existing workspace status when an update omits it
  • Verify the frontend Svelte store merges and preserves workspace statuses during sync
  • E2E: Perform a Start/Stop playthrough on a workspace and verify transitions to 'Starting', 'Running', 'Stopping', and 'Stopped'
  • E2E: Verify Rebuild and Delete confirmation dialogs appear correctly
  • Verify that a workspace status reverts to its previous state if an IPC action (e.g., handleStart) fails
  • Verify CLI/PTY processes are forcefully terminated (SIGKILL) after the 2-second timeout in cancelFor
  • Implement unit tests for logic in desktop/src/main/ipc.ts to address complexity and lack of coverage
  • Implement unit tests for logic in desktop/src/main/cli.ts to address complexity and lack of coverage
  • Implement unit tests for logic in desktop/src/renderer/src/lib/ipc/commands.ts to address complexity and lack of coverage
  • Implement unit tests for logic in desktop/src/main/pty.ts to address complexity and lack of coverage
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Verify that a workspace status reverts to its previous state if an IPC action (e.g., handleStart) fails
2. Verify CLI/PTY processes are forcefully terminated (SIGKILL) after the 2-second timeout in cancelFor
3. Implement unit tests for logic in `desktop/src/main/ipc.ts` to address complexity and lack of coverage
4. Implement unit tests for logic in `desktop/src/main/cli.ts` to address complexity and lack of coverage
5. Implement unit tests for logic in `desktop/src/renderer/src/lib/ipc/commands.ts` to address complexity and lack of coverage
6. Implement unit tests for logic in `desktop/src/main/pty.ts` to address complexity and lack of coverage

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread desktop/src/main/cli.ts Outdated
// so finish(...) can properly clean up sessions and call onExit
const alreadySettled = settled
if (!alreadySettled) {
finish(-1, { code: "timeout", message: "Process did not exit in time" })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 HIGH RISK

The finish function is not in scope here (causing a ReferenceError), and the surrounding conditional logic makes it unreachable even if it were. This will cause the application to leak CLI execution slots on process timeouts. Furthermore, this complex cleanup logic is duplicated across cli.ts, ipc.ts, and pty.ts. Centralizing this into a forceKillWithCleanup utility would resolve the bug and improve maintainability.

Comment thread desktop/src/main/ipc.ts
@@ -908,6 +953,7 @@ export function registerIpcHandlers(deps: IpcDependencies): {
prebuildRepository?: string

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM RISK

The parameter interface for workspace_up is duplicated here and in desktop/src/renderer/src/lib/ipc/commands.ts. Extracting shared IPC contracts to a central types file would make the contract less brittle.

See Clone in Codacy

Comment thread desktop/src/main/ipc.ts
tunnelProc.kill("SIGTERM")
await tunnelExit
// If process did not close in time, forcefully kill and suppress any late callbacks
if (!settled || (tunnelProc.exitCode === null && tunnelProc.signalCode === null)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ LOW RISK

Nitpick: The !settled check is redundant because the promise only resolves after setting settled to true.

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@skevetter
skevetter marked this pull request as draft August 8, 2026 23:09
Signed-off-by: GitHub <noreply@github.com>
@skevetter
skevetter marked this pull request as ready for review August 9, 2026 00:14

@coderabbitai coderabbitai 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.

Caution

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

⚠️ Outside diff range comments (1)
desktop/src/main/cli.ts (1)

365-392: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Wait for close before resolving cancellation.

exitCode and signalCode can be set before close. runStreaming calls finish from the close handler, so resolving from lines 376-380 can let cancelFor return while the child stays tracked and stdout/stderr can still emit data. Apply the same rule to the cleanup path after timeout resolves.

🤖 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 `@desktop/src/main/cli.ts` around lines 365 - 392, Ensure the Promise in the
cancellation wait resolves only from the child’s close event, not merely when
exitCode or signalCode becomes non-null. Update the early-exit branch to wait
for close while still clearing the timer, and preserve the same close-waiting
behavior in the post-timeout cleanup before cancelFor returns.
🤖 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 `@desktop/src/main/cli.ts`:
- Around line 365-392: Ensure the Promise in the cancellation wait resolves only
from the child’s close event, not merely when exitCode or signalCode becomes
non-null. Update the early-exit branch to wait for close while still clearing
the timer, and preserve the same close-waiting behavior in the post-timeout
cleanup before cancelFor returns.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 02e096e5-0d52-42cc-946d-0aba579f1671

📥 Commits

Reviewing files that changed from the base of the PR and between b90db8b and 2a48785.

📒 Files selected for processing (3)
  • desktop/src/main/cli.ts
  • desktop/src/main/pty.ts
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
  • desktop/src/main/pty.ts

@skevetter
skevetter enabled auto-merge (squash) August 9, 2026 00:20
@skevetter
skevetter disabled auto-merge August 9, 2026 00:20
@skevetter
skevetter merged commit 5db2fc2 into main Aug 9, 2026
28 of 29 checks passed
@skevetter
skevetter deleted the ui/fix-workspace-table-loading branch August 9, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant