Skip to content

feat: start stop button in workspace#1140

Open
MarsKubeX wants to merge 2 commits intokortex-hub:mainfrom
MarsKubeX:feat/start-stop-button-workspace-card
Open

feat: start stop button in workspace#1140
MarsKubeX wants to merge 2 commits intokortex-hub:mainfrom
MarsKubeX:feat/start-stop-button-workspace-card

Conversation

@MarsKubeX
Copy link
Contributor

Added start and stop buttons ford workspaces in workspace list view and in workspace detail view

Grabacion.de.pantalla.2026-03-20.a.las.10.29.01.mov

Closes #1123

Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>
Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>
@MarsKubeX MarsKubeX requested a review from a team as a code owner March 20, 2026 09:31
@MarsKubeX MarsKubeX requested review from fbricon and gastoner and removed request for a team March 20, 2026 09:31
@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

This PR adds start/stop workspace controls by introducing a new Svelte store (agentWorkspaceStatuses) to track workspace status, implementing startAgentWorkspace() and stopAgentWorkspace() functions with optimistic updates, and integrating controls into AgentWorkspaceCard and AgentWorkspaceDetails components with loading states.

Changes

Cohort / File(s) Summary
Store Implementation
packages/renderer/src/stores/agent-workspaces.ts, packages/renderer/src/stores/agent-workspaces.spec.ts
Added AgentWorkspaceStatus type and agentWorkspaceStatuses store to track per-workspace lifecycle state. Implemented startAgentWorkspace() and stopAgentWorkspace() functions with optimistic status transitions (startingrunning/stopped, stoppingstopped/running) and error handling.
AgentWorkspaceCard Component
packages/renderer/src/lib/agent-workspaces/AgentWorkspaceCard.svelte, packages/renderer/src/lib/agent-workspaces/AgentWorkspaceCard.spec.ts
Added start/stop button with play/stop icons and loading indicator. Integrated store-driven status state to conditionally render buttons and disable during transitions. Refactored keyboard handler to shared handleActionKeydown pattern. Tests verify conditional rendering and state transitions via store assertions.
AgentWorkspaceDetails Component
packages/renderer/src/lib/agent-workspaces/AgentWorkspaceDetails.svelte, packages/renderer/src/lib/agent-workspaces/AgentWorkspaceDetails.spec.ts
Added start/stop control to DetailsPage actions area with derived status flags (isRunning, inProgress). Implements handleStartStop() to manage transitions and prevent concurrent actions. Tests verify button rendering and state transitions based on workspace status.

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI Component
    participant Store as agentWorkspaceStatuses Store
    participant API as window.startAgentWorkspace/<br/>stopAgentWorkspace

    UI->>Store: startAgentWorkspace(id)
    Store->>Store: set status to 'starting'
    Store-->>UI: status = 'starting' (UI shows loading)
    Store->>API: call window.startAgentWorkspace(id)
    alt Success
        API-->>Store: resolve with {id}
        Store->>Store: set status to 'running'
        Store-->>UI: status = 'running' (button updates)
    else Failure
        API-->>Store: reject error
        Store->>Store: set status to 'stopped' (revert)
        Store-->>UI: status = 'stopped'
    end
Loading
sequenceDiagram
    participant UI as UI Component
    participant Store as agentWorkspaceStatuses Store
    participant API as window.startAgentWorkspace/<br/>stopAgentWorkspace

    UI->>Store: stopAgentWorkspace(id)
    Store->>Store: set status to 'stopping'
    Store-->>UI: status = 'stopping' (UI shows loading)
    Store->>API: call window.stopAgentWorkspace(id)
    alt Success
        API-->>Store: resolve with {id}
        Store->>Store: set status to 'stopped'
        Store-->>UI: status = 'stopped' (button updates)
    else Failure
        API-->>Store: reject error
        Store->>Store: set status to 'running' (revert)
        Store-->>UI: status = 'running'
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: start stop button in workspace' accurately summarizes the main change, clearly indicating the addition of start/stop functionality to workspace controls.
Description check ✅ Passed The description relates to the changeset by mentioning the addition of start and stop buttons for workspaces in list and detail views, referencing the linked issue #1123.
Linked Issues check ✅ Passed The code changes fully implement the requirements from issue #1123: start/stop buttons are added to workspace cards (AgentWorkspaceCard.svelte) and workspace detail view (AgentWorkspaceDetails.svelte), with supporting store functionality and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are scoped to the implementation of start/stop workspace functionality; no unrelated modifications are present outside the stated objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/renderer/src/stores/agent-workspaces.spec.ts (1)

64-101: Add a regression test for concurrent updates across different workspace IDs.

Current tests are single-workspace only. A multi-workspace concurrent scenario would protect against stale snapshot overwrites in async stop/start flows.

🧪 Suggested test case
+test('stopAgentWorkspace should preserve updates made to other workspaces while awaiting', async () => {
+  agentWorkspaceStatuses.set(
+    new Map([
+      ['ws-1', 'running'],
+      ['ws-2', 'stopped'],
+    ]),
+  );
+
+  let resolveStop: (value: { id: string }) => void = () => {};
+  vi.mocked(window.stopAgentWorkspace).mockReturnValue(
+    new Promise(resolve => {
+      resolveStop = resolve;
+    }),
+  );
+
+  const stopPromise = stopAgentWorkspace('ws-1');
+
+  // Simulate another workspace changing while ws-1 stop is in-flight.
+  agentWorkspaceStatuses.update(statuses => {
+    const next = new Map(statuses);
+    next.set('ws-2', 'running');
+    return next;
+  });
+
+  resolveStop({ id: 'ws-1' });
+  await stopPromise;
+
+  expect(get(agentWorkspaceStatuses).get('ws-2')).toBe('running');
+});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/stores/agent-workspaces.spec.ts` around lines 64 - 101,
Add a regression test that exercises concurrent updates across different
workspace IDs to ensure async state updates don't overwrite each other:
initialize agentWorkspaceStatuses with two entries (e.g., 'ws-1' => 'running',
'ws-2' => 'running'), mock window.stopAgentWorkspace to return a controllable
promise for one id (using a resolver variable) and an immediate resolved value
for the other, call stopAgentWorkspace for both IDs concurrently, assert that
the in-flight one sets its status to 'stopping' while the other transitions to
'stopped' immediately, then resolve the pending promise and assert both end up
'stopped' (use get(agentWorkspaceStatuses), stopAgentWorkspace and
window.stopAgentWorkspace to locate code paths).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/renderer/src/stores/agent-workspaces.ts`:
- Around line 57-69: The function stopAgentWorkspace captures a Map snapshot in
statuses and reuses it across an await, which can overwrite concurrent updates;
instead make status changes atomically against the current store (use
agentWorkspaceStatuses.update or re-read agentWorkspaceStatuses with get right
before each write) so you never apply a stale Map. Concretely: don't keep and
call agentWorkspaceStatuses.set(new Map(statuses)) after the await; set
'stopping' by updating the live store, then after the await update the live
store again to 'stopped' or in the catch to 'running' (use the update callback
to only modify the entry for id), ensuring other workspace entries remain
untouched; reference stopAgentWorkspace, agentWorkspaceStatuses, and the local
statuses variable when making the change.

---

Nitpick comments:
In `@packages/renderer/src/stores/agent-workspaces.spec.ts`:
- Around line 64-101: Add a regression test that exercises concurrent updates
across different workspace IDs to ensure async state updates don't overwrite
each other: initialize agentWorkspaceStatuses with two entries (e.g., 'ws-1' =>
'running', 'ws-2' => 'running'), mock window.stopAgentWorkspace to return a
controllable promise for one id (using a resolver variable) and an immediate
resolved value for the other, call stopAgentWorkspace for both IDs concurrently,
assert that the in-flight one sets its status to 'stopping' while the other
transitions to 'stopped' immediately, then resolve the pending promise and
assert both end up 'stopped' (use get(agentWorkspaceStatuses),
stopAgentWorkspace and window.stopAgentWorkspace to locate code paths).
🪄 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: CHILL

Plan: Pro

Run ID: e00ca8c6-eaf7-4691-90b1-c1ce23208fc5

📥 Commits

Reviewing files that changed from the base of the PR and between 7651253 and b349114.

📒 Files selected for processing (6)
  • packages/renderer/src/lib/agent-workspaces/AgentWorkspaceCard.spec.ts
  • packages/renderer/src/lib/agent-workspaces/AgentWorkspaceCard.svelte
  • packages/renderer/src/lib/agent-workspaces/AgentWorkspaceDetails.spec.ts
  • packages/renderer/src/lib/agent-workspaces/AgentWorkspaceDetails.svelte
  • packages/renderer/src/stores/agent-workspaces.spec.ts
  • packages/renderer/src/stores/agent-workspaces.ts

Comment on lines +57 to +69
export async function stopAgentWorkspace(id: string): Promise<void> {
const statuses = get(agentWorkspaceStatuses);
statuses.set(id, 'stopping');
agentWorkspaceStatuses.set(new Map(statuses));
try {
await window.stopAgentWorkspace(id);
statuses.set(id, 'stopped');
} catch (error: unknown) {
statuses.set(id, 'running');
console.error('Failed to stop agent workspace', error);
}
agentWorkspaceStatuses.set(new Map(statuses));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

stopAgentWorkspace can clobber concurrent status updates.

Line 58 captures a Map snapshot and reuses it after await (Lines 62-68). If another workspace status changes in between, the final set(new Map(statuses)) can overwrite that newer state.

🔧 Suggested fix
 export async function stopAgentWorkspace(id: string): Promise<void> {
-  const statuses = get(agentWorkspaceStatuses);
-  statuses.set(id, 'stopping');
-  agentWorkspaceStatuses.set(new Map(statuses));
+  agentWorkspaceStatuses.update(statuses => {
+    const next = new Map(statuses);
+    next.set(id, 'stopping');
+    return next;
+  });
   try {
     await window.stopAgentWorkspace(id);
-    statuses.set(id, 'stopped');
+    agentWorkspaceStatuses.update(statuses => {
+      const next = new Map(statuses);
+      next.set(id, 'stopped');
+      return next;
+    });
   } catch (error: unknown) {
-    statuses.set(id, 'running');
+    agentWorkspaceStatuses.update(statuses => {
+      const next = new Map(statuses);
+      next.set(id, 'running');
+      return next;
+    });
     console.error('Failed to stop agent workspace', error);
   }
-  agentWorkspaceStatuses.set(new Map(statuses));
 }
📝 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
export async function stopAgentWorkspace(id: string): Promise<void> {
const statuses = get(agentWorkspaceStatuses);
statuses.set(id, 'stopping');
agentWorkspaceStatuses.set(new Map(statuses));
try {
await window.stopAgentWorkspace(id);
statuses.set(id, 'stopped');
} catch (error: unknown) {
statuses.set(id, 'running');
console.error('Failed to stop agent workspace', error);
}
agentWorkspaceStatuses.set(new Map(statuses));
}
export async function stopAgentWorkspace(id: string): Promise<void> {
agentWorkspaceStatuses.update(statuses => {
const next = new Map(statuses);
next.set(id, 'stopping');
return next;
});
try {
await window.stopAgentWorkspace(id);
agentWorkspaceStatuses.update(statuses => {
const next = new Map(statuses);
next.set(id, 'stopped');
return next;
});
} catch (error: unknown) {
agentWorkspaceStatuses.update(statuses => {
const next = new Map(statuses);
next.set(id, 'running');
return next;
});
console.error('Failed to stop agent workspace', error);
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/stores/agent-workspaces.ts` around lines 57 - 69, The
function stopAgentWorkspace captures a Map snapshot in statuses and reuses it
across an await, which can overwrite concurrent updates; instead make status
changes atomically against the current store (use agentWorkspaceStatuses.update
or re-read agentWorkspaceStatuses with get right before each write) so you never
apply a stale Map. Concretely: don't keep and call
agentWorkspaceStatuses.set(new Map(statuses)) after the await; set 'stopping' by
updating the live store, then after the await update the live store again to
'stopped' or in the catch to 'running' (use the update callback to only modify
the entry for id), ensuring other workspace entries remain untouched; reference
stopAgentWorkspace, agentWorkspaceStatuses, and the local statuses variable when
making the change.

@codecov
Copy link

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 95.23810% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...src/lib/agent-workspaces/AgentWorkspaceCard.svelte 86.36% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@gastoner gastoner left a comment

Choose a reason for hiding this comment

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

LGTM otherwise

On the detail page the start/stop button seems like it is not rouded in the same way as in the Card

export type AgentWorkspaceStatus = 'stopped' | 'running' | 'starting' | 'stopping';

export const agentWorkspaces: Writable<AgentWorkspaceSummary[]> = writable([]);
export const agentWorkspaceStatuses: Writable<Map<string, AgentWorkspaceStatus>> = writable(new Map());
Copy link
Contributor

Choose a reason for hiding this comment

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

(NIT: not req) Thinking if e.g. usage of SvelteMap here would be better? + renaming the file or just a Map
WDYT @benoitf

@jeffmaury jeffmaury self-requested a review March 20, 2026 10:46
Copy link
Contributor

@bmahabirbu bmahabirbu left a comment

Choose a reason for hiding this comment

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

LGTM id check out the coderabbit suggestions also!

Copy link
Contributor

@jeffmaury jeffmaury left a comment

Choose a reason for hiding this comment

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

LGTM. Tested on Windows

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.

add button to start/stop workspace in the UI

4 participants