Skip to content

Codex/fix/workbench device workspaces - #1524

Merged
qdaxb merged 8 commits into
wecode-ai:mainfrom
FicoHu:codex/fix/workbench-device-workspaces
Jun 23, 2026
Merged

qdaxb merged 8 commits into
wecode-ai:mainfrom
FicoHu:codex/fix/workbench-device-workspaces

Conversation

@FicoHu

@FicoHu FicoHu commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Improved real-time streaming tool argument tracking for Claude responses (start/delta/done with deduplication).
    • Added optional external forwarding of runtime Responses API events.
    • Introduced optional refreshWorkLists behavior for project creation and device workspace preparation, including optimistic updates.
  • Bug Fixes
    • Normalized legacy remote device types to the local-device contract.
    • Filtered device lists to show only Claude Code-capable devices (excluding OpenClaw).
  • Tests
    • Added/updated coverage for streaming tool argument lifecycle, runtime event payloads/order, and new project/workspace flows.
    • Updated chat UI streaming placeholder behavior tests.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc57ec6a-59fa-4db3-86bb-a7f8abb88def

📥 Commits

Reviewing files that changed from the base of the PR and between d2eab87 and 5a3cf3a.

📒 Files selected for processing (2)
  • wework/src/components/chat/MessageList.test.tsx
  • wework/src/components/chat/MessageList.tsx

📝 Walkthrough

Walkthrough

This PR adds Claude Code device filtering to the API and session-storage cache, introduces optimistic project_created and device_workspace_prepared reducer actions with a refreshWorkLists option flag, wires Responses API event forwarding through RuntimeTranscriptTransport, adds stateful tool-argument streaming to the Claude response processor, fixes LocalDeviceProvider to always return DeviceType.LOCAL, and refines streaming assistant message UI.

Changes

Backend: local device type normalization and streaming tool block test

Layer / File(s) Summary
device_type normalization fix and tests
backend/app/services/device/local_provider.py, backend/tests/services/test_local_device_provider.py, backend/tests/api/ws/test_device_capabilities_state.py
Hard-codes DeviceType.LOCAL.value in list_devices; adds a new test module verifying normalization of legacy deviceType="remote" specs; adds a WebSocket test asserting CHAT_BLOCK_CREATED with status="generating_arguments" when argument_status is "streaming".

Executor: Responses API event streaming and Claude tool-argument streaming

Layer / File(s) Summary
Claude response processor: stream state constants and helpers
executor/agents/claude_code/response_processor.py
Adds three attribute-name constants for emitter-attached state tracking and a comprehensive helper function library for per-index tool storage/retrieval, started/finalized tracking, and JSON argument parsing with safe fallback handling.
Streaming event handlers: content_block and input_json_delta
executor/agents/claude_code/response_processor.py
Updates content_block_start to emit tool_argument_start and store tool state by stream index; updates input_json_delta to accumulate partial JSON and emit tool_argument_delta with parsed arguments summary; updates content_block_stop to emit tool_argument_done and mark tool as finalized.
ToolUseBlock finalization with stream deduplication
executor/agents/claude_code/response_processor.py
Finalized ToolUseBlock handling checks stream state to avoid duplicate tool_start emission when streaming already started the tool; emits tool_argument_done for already-started tools instead; UserMessage tool blocks now route through the same deduplication logic.
Claude response processor streaming tests
executor/tests/agents/test_claude_response_processor.py
Replaces old incomplete-block test; adds full tool lifecycle test (start → delta → stop); adds UserMessage tool handling and assistant finalization deduplication tests.
RuntimeTranscriptTransport event forwarding
executor/runtime_work/agent_adapter.py
Extends constructor with runtime and emit_event kwargs; updates send() for all event types to forward through _forward_event; adds _forward_event to build and dispatch payloads with task IDs, local_task_id, runtime, and optional metadata; updates _append_assistant_message to attach optional blocks to messages.
RuntimeAgentAdapter and RpcHandler wiring
executor/runtime_work/agent_adapter.py, executor/runtime_work/rpc_handler.py
RuntimeAgentAdapter stores responses_event_emitter and passes it as emit_event to the transport; RpcHandler passes responses_event_emitter into the claude_code adapter; adds helper functions for millisecond timestamps, reasoning text extraction, and tool argument normalization.
End-to-end streaming test
executor/tests/runtime_work/test_local_task_store.py
Monkeypatches _execute_real_agent to emit a ClaudeCode streaming sequence; asserts forwarded event order, delta payload fields, and final transcript content.

Frontend: Claude device filtering and optimistic project/workspace mutations

Layer / File(s) Summary
filterClaudeCodeDevices helper and API filtering
wework/src/lib/device-capabilities.ts, wework/src/api/devices.ts, wework/src/api/devices.test.ts
Exports filterClaudeCodeDevices; applies it in fetchDevices to filter API response items; adds test asserting the filter excludes non-Claude devices.
Session-storage cache normalization
wework/src/features/workbench/WorkbenchProvider.tsx
Imports filterClaudeCodeDevices; updates session-storage cache reads to filter through filterClaudeCodeDevices; updates cache writes to skip storage when no Claude Code devices exist.
ProjectMutationOptions type and callback signatures
wework/src/components/projects/ProjectCreateDialog.tsx, wework/src/features/workbench/WorkbenchProvider.tsx
Defines ProjectMutationOptions { refreshWorkLists?: boolean }; updates onCreateProject, onPrepareDeviceWorkspace, createProject, and prepareDeviceWorkspace signatures to accept an optional second options argument.
workbenchReducer new actions and upsert helpers
wework/src/features/workbench/workbenchReducer.ts
Extends WorkbenchAction with project_created, project_selected, and device_workspace_prepared; adds DeviceWorkspaceResponse-to-RuntimeDeviceWorkspace converter and upsert helper; adds reducer cases for both new actions.
WorkbenchProvider optimistic dispatch and dialog submission
wework/src/features/workbench/WorkbenchProvider.tsx, wework/src/components/projects/ProjectCreateDialog.tsx
createProject dispatches project_created optimistically when refreshWorkLists===false; prepareDeviceWorkspace dispatches device_workspace_prepared and runs background refresh; ProjectCreateDialog.handleSubmit passes { refreshWorkLists: false } to both callbacks.
Frontend test updates and streaming UI
wework/src/components/projects/ProjectCreateDialog.test.tsx, wework/src/components/layout/DesktopWorkbenchLayout.test.tsx, wework/src/features/workbench/workbenchReducer.test.ts, frontend/src/__tests__/features/inbox/QueueEditDialog.test.tsx, wework/src/components/chat/MessageList.tsx, wework/src/components/chat/MessageList.test.tsx
Asserts { refreshWorkLists: false } as second argument in dialog callback tests; adds reducer test verifying device_workspace_prepared sets selectedDeviceWorkspaceId, upserts the workspace, and clears unmappedDeviceWorkspaces; replaces async userEvent calls with synchronous fireEvent in QueueEditDialog test; updates AssistantMessage streaming placeholder to render ToolBlocksDisplay with empty blocks when streaming with no blocks; adds test coverage for Codex-style thinking timeline rendering.

Sequence Diagram(s)

sequenceDiagram
  participant Dialog as ProjectCreateDialog
  participant Provider as WorkbenchProvider
  participant Reducer as workbenchReducer
  participant API as Backend API

  Dialog->>Provider: onCreateProject(data, { refreshWorkLists: false })
  Provider->>API: POST /projects
  API-->>Provider: ProjectWithTasks
  Provider->>Reducer: dispatch({ type: 'project_created', project })
  Reducer-->>Provider: updated projects list (optimistic)

  Dialog->>Provider: onPrepareDeviceWorkspace(data, { refreshWorkLists: false })
  Provider->>API: POST /device-workspaces/prepare
  API-->>Provider: DeviceWorkspaceResponse
  Provider->>Reducer: dispatch({ type: 'device_workspace_prepared', mapping })
  Reducer-->>Provider: updated runtimeWork + selectedDeviceWorkspaceId
  Provider->>Provider: refreshWorkLists() in background (swallows failures)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • wecode-ai/Wegent#1280: Main PR adds filterClaudeCodeDevices device filtering; retrieved PR changes underlying isClaudeCodeDevice predicate behavior.
  • wecode-ai/Wegent#1285: Both PRs update wework/src/components/chat/MessageList.tsx streaming placeholder rendering for assistant messages.
  • wecode-ai/Wegent#1403: Main PR refines streaming thinking/process text indicators in chat UI; retrieved PR addresses overall chat progress display refinements.

Suggested reviewers

  • qdaxb

🐇 A rabbit hops through the code with glee,
Filtering devices so Claude runs free,
Optimistic updates — no waiting around,
Tool arguments streaming without a sound,
{ refreshWorkLists: false } — quick as can be!
The state updates fast, thinking shows bright! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.76% 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 'Codex/fix/workbench device workspaces' directly reflects the main change: fixing device workspace handling in the workbench, which encompasses optimistic updates, device filtering, callback signature changes, and reducer state transitions across multiple components.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

Caution

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

⚠️ Outside diff range comments (1)
wework/src/features/workbench/WorkbenchProvider.tsx (1)

191-204: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid serving stale cached devices when the live fetch succeeds with zero Claude devices.

On Line 198, cache fallback runs whenever claudeCodeDevices is empty. Since the API now pre-filters to Claude devices, a successful /devices response can legitimately be empty; this path then revives stale cache and hides the real live state.

💡 Suggested fix
-function resolveDeviceListWithCache(devices: DeviceInfo[]): DeviceInfo[] {
+function resolveDeviceListWithCache(
+  devices: DeviceInfo[],
+  allowCachedFallback: boolean
+): DeviceInfo[] {
   const claudeCodeDevices = filterClaudeCodeDevices(devices)
   if (claudeCodeDevices.length > 0) {
     writeCachedDeviceList(claudeCodeDevices)
     return claudeCodeDevices
   }

-  const cachedDevices = readCachedDeviceList()
-  if (cachedDevices.length > 0) {
-    return cachedDevices
+  if (allowCachedFallback) {
+    const cachedDevices = readCachedDeviceList()
+    if (cachedDevices.length > 0) {
+      return cachedDevices
+    }
   }

-  return devices
+  return claudeCodeDevices
 }

And pass allowCachedFallback only when the network read failed (e.g., bootstrap rejected path), not on successful empty responses.

🤖 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 `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 191 - 204,
The resolveDeviceListWithCache function cannot distinguish between a successful
empty response (which is now legitimate since the API pre-filters to Claude
devices) and a failed network call, causing it to incorrectly resurrect stale
cached devices on successful empty responses. Add a parameter to
resolveDeviceListWithCache (e.g., allowCachedFallback) that indicates whether
the network fetch succeeded or failed, and only use the cached device fallback
when allowCachedFallback is true and claudeCodeDevices is empty, ensuring that
legitimate empty live responses are not masked by stale cache.
🧹 Nitpick comments (7)
backend/tests/services/test_local_device_provider.py (1)

40-53: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add explicit type hints to new async test functions.

The new test function and nested async helpers are missing parameter/return type hints. Please annotate them to match the Python typing rule used in this repo.

Suggested patch
 import pytest
+from typing import Any
 
 from app.models.kind import Kind
 from app.schemas.device import DeviceInfo
 from app.services.device.local_provider import LocalDeviceProvider
@@
 `@pytest.mark.asyncio`
 async def test_list_devices_normalizes_legacy_remote_device_type(
-    test_db,
-    monkeypatch,
-):
+    test_db: Any,
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
@@
-    async def fake_mget(keys):
-        return {}
+    async def fake_mget(keys: list[str]) -> dict[str, Any]:
+        return {}
 
-    async def fake_latest_version():
+    async def fake_latest_version() -> str:
         return "1.0.0"

As per coding guidelines, **/*.py: “Use type hints for all function parameters and return values”.

🤖 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 `@backend/tests/services/test_local_device_provider.py` around lines 40 - 53,
The test function test_list_devices_normalizes_legacy_remote_device_type and its
nested async helper functions fake_mget and fake_latest_version are missing type
hints for their parameters and return values. Add explicit type annotations to
all parameters and return types for these functions following the repository's
Python typing conventions. This includes annotating the test function parameters
(test_db and monkeypatch), the fake_mget parameter (keys) and return type, and
the fake_latest_version return type to comply with the coding guidelines that
require type hints for all function parameters and return values.

Source: Coding guidelines

backend/tests/api/ws/test_device_capabilities_state.py (1)

398-403: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Type-annotate the new async test and helper function.

Please add explicit parameter and return annotations for the newly introduced test and nested helper.

Suggested patch
 `@pytest.mark.asyncio`
-async def test_local_task_streaming_tool_arguments_emit_generating_block(monkeypatch):
+async def test_local_task_streaming_tool_arguments_emit_generating_block(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
@@
-    async def fake_get_session(sid):
+    async def fake_get_session(sid: str) -> dict[str, object]:
         return {"user_id": 7, "device_id": "device-1"}

As per coding guidelines, **/*.py: “Use type hints for all function parameters and return values”.

🤖 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 `@backend/tests/api/ws/test_device_capabilities_state.py` around lines 398 -
403, The newly introduced test function
test_local_task_streaming_tool_arguments_emit_generating_block and its nested
async helper function fake_get_session are missing explicit type annotations.
Add type hint annotations to both functions: the test function should have a
return type of None, and the fake_get_session helper should have the sid
parameter typed (as str) and include a return type annotation for the dictionary
it returns. This aligns with the project coding guideline requiring type hints
for all function parameters and return values.

Source: Coding guidelines

executor/runtime_work/agent_adapter.py (1)

33-40: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add explicit -> None on updated constructors.

The updated __init__ signatures should include explicit return annotations for typing consistency.

Suggested fix
-    def __init__(
+    def __init__(
         self,
         store: LocalTaskStore,
         local_task_id: str,
         *,
         runtime: str,
         emit_event: Optional[Callable[[str, dict[str, Any]], Any]] = None,
-    ):
+    ) -> None:
@@
-    def __init__(
+    def __init__(
         self,
         *,
         runtime: str,
         store: LocalTaskStore,
         execute_agent: Optional[ExecuteAgent] = None,
         run_background: bool = True,
         responses_event_emitter: Optional[Callable[[str, dict[str, Any]], Any]] = None,
-    ):
+    ) -> None:

As per coding guidelines, **/*.py: “Use type hints for all function parameters and return values.”

Also applies to: 212-220

🤖 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 `@executor/runtime_work/agent_adapter.py` around lines 33 - 40, Add explicit
`-> None` return type annotations to the `__init__` constructor methods in the
AgentAdapter class. For the `__init__` method shown in the diff (with parameters
store, local_task_id, runtime, and emit_event), add `-> None` after the closing
parenthesis and before the colon. Apply the same change to the other `__init__`
method mentioned at lines 212-220 to ensure all constructors have consistent
typing that aligns with the coding guidelines requiring type hints for all
function return values.

Source: Coding guidelines

executor/tests/runtime_work/test_local_task_store.py (1)

2238-2241: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Type-annotate the new async test functions.

The new test and nested async stub omit parameter/return annotations required by repo typing rules.

Suggested fix
-async def test_runtime_work_handler_streams_claude_runtime_task_events(
-    tmp_path,
-    monkeypatch,
-):
+async def test_runtime_work_handler_streams_claude_runtime_task_events(
+    tmp_path: Path,
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
@@
-    async def execute_agent(self, request, emitter):
+    async def execute_agent(
+        self: RuntimeAgentAdapter, request: Any, emitter: Any
+    ) -> None:
         await emitter.start(shell_type="ClaudeCode")
         await emitter.text_delta("Created")
         await emitter.done(content="Created")

As per coding guidelines, **/*.py: “Use type hints for all function parameters and return values.”

Also applies to: 2246-2249

🤖 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 `@executor/tests/runtime_work/test_local_task_store.py` around lines 2238 -
2241, Add type annotations to the test function
test_runtime_work_handler_streams_claude_runtime_task_events and its parameters.
Annotate the tmp_path parameter with the appropriate pytest fixture type, the
monkeypatch parameter with the MonkeyPatch type from pytest, and add a return
type annotation of None to the async function. Also apply the same type
annotation requirements to the nested async stub function mentioned in lines
2246-2249, ensuring all parameters and return types are properly annotated
according to the repository's typing standards.

Source: Coding guidelines

executor/agents/claude_code/response_processor.py (1)

983-989: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider using simpler getattr pattern for attribute access.

The current pattern getattr(emitter, "__dict__", {}).get(ATTR) works but is unnecessarily indirect. The idiomatic Python approach would be:

state = getattr(emitter, CLAUDE_STREAM_TOOL_STATE_ATTR, None)

This applies to _stream_tool_state, _stream_tool_started, and _stream_tool_finalized helpers.

♻️ Suggested simplification
 def _stream_tool_state(emitter: ResponsesAPIEmitter) -> dict[str, dict[str, Any]]:
-    state = getattr(emitter, "__dict__", {}).get(CLAUDE_STREAM_TOOL_STATE_ATTR)
+    state = getattr(emitter, CLAUDE_STREAM_TOOL_STATE_ATTR, None)
     if isinstance(state, dict):
         return state
     state = {}
     setattr(emitter, CLAUDE_STREAM_TOOL_STATE_ATTR, state)
     return state
🤖 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 `@executor/agents/claude_code/response_processor.py` around lines 983 - 989,
Replace the indirect attribute access pattern in the three helper functions
_stream_tool_state, _stream_tool_started, and _stream_tool_finalized that
currently use getattr(emitter, "__dict__", {}).get(ATTR) with the simpler
idiomatic Python approach using getattr(emitter, ATTR, None) or getattr(emitter,
ATTR, {}) depending on whether a None or empty dict default makes sense for that
function. This makes the code more readable and follows Python conventions for
attribute access with fallback defaults.
wework/src/components/layout/DesktopWorkbenchLayout.test.tsx (1)

1516-1528: 🧹 Nitpick | 🔵 Trivial | 🏗️ Heavy lift

Split this oversized test file before it grows further.

The changed assertions are already past Line 1500, so this test module remains well over the repository’s 1000-line limit. Please move the workbench project-creation/workspace-selection coverage into a focused test file.

As per coding guidelines, “Files should not exceed 1000 lines; split large files into multiple sub-modules for better maintainability.”

🤖 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 `@wework/src/components/layout/DesktopWorkbenchLayout.test.tsx` around lines
1516 - 1528, The DesktopWorkbenchLayout.test.tsx file exceeds the 1000-line
limit and needs to be split. Identify all test cases in
DesktopWorkbenchLayout.test.tsx that cover workbench project-creation and
workspace-selection functionality (including the test containing the
onPrepareDeviceWorkspace assertions around line 1516), extract those tests into
a new, focused test file dedicated to workspace and project selection scenarios,
and update any shared test utilities or mock dependencies to ensure both the
original and new test files can access them properly.

Source: Coding guidelines

wework/src/features/workbench/workbenchReducer.test.ts (1)

116-159: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Cover local task preservation in this optimistic move test.

This fixture only uses localTasks: [], so the reducer can pass while dropping existing runtime tasks from the promoted unmapped workspace. Seed one local task and assert it remains under the mapped workspace.

Suggested test strengthening
         unmappedDeviceWorkspaces: [
           {
             deviceId: 'device-1',
             workspacePath: '/workspace/repo',
             available: true,
             mapped: false,
-            localTasks: [],
+            localTasks: [
+              {
+                localTaskId: 'runtime-1',
+                workspacePath: '/workspace/repo',
+                title: 'Existing local task',
+                runtime: 'codex',
+              },
+            ],
           },
         ],
@@
         deviceName: 'Device 1',
         workspacePath: '/workspace/repo',
         mapped: true,
+        localTasks: [
+          expect.objectContaining({
+            localTaskId: 'runtime-1',
+          }),
+        ],
       }),
     ])

As per coding guidelines, “test edge cases and error conditions.”

🤖 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 `@wework/src/features/workbench/workbenchReducer.test.ts` around lines 116 -
159, The test for the device_workspace_prepared action only uses empty
localTasks arrays, which doesn't verify that local tasks are properly preserved
when promoting an unmapped device workspace to a mapped one. In the fixture
setup where unmappedDeviceWorkspaces is initialized, add a sample local task to
the localTasks array instead of leaving it empty, and then add an assertion
after the reducer call to verify that the mapped workspace in
updated.runtimeWork?.projects[0].deviceWorkspaces contains the expected local
tasks that were preserved from the unmapped workspace.

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.

Inline comments:
In `@backend/tests/services/test_local_device_provider.py`:
- Around line 63-64: The test code accesses devices[0] without first verifying
that the devices list contains any elements, which can result in an IndexError
that masks the actual contract failure being tested. Before the assertions that
access devices[0]["device_type"] and the DeviceInfo(**devices[0]) call, add an
explicit assertion to ensure the devices list is not empty, such as checking
that its length is greater than zero or that it is truthy.

In `@executor/runtime_work/agent_adapter.py`:
- Around line 154-174: The _forward_event() method does not handle exceptions
that may be raised by the emit_event callback, which can cause the runtime flow
to be interrupted and prevent _mark_not_running() from being executed. Wrap the
self.emit_event(event_type, payload) call and the subsequent coroutine handling
(the asyncio.iscoroutine check and await) in a try-except block to gracefully
catch and log any exceptions without allowing them to bubble up and disrupt the
task's running state management.

In `@wework/src/features/workbench/workbenchReducer.ts`:
- Around line 128-167: When promoting a workspace from unmapped to mapped state,
the existing local tasks are lost because nextWorkspace is created with empty
localTasks. Find the matching unmapped workspace in
currentRuntimeWork.unmappedDeviceWorkspaces before it gets filtered out
(matching by deviceId and workspacePath), extract its localTasks property, and
ensure those local tasks are preserved when creating nextWorkspace via
runtimeWorkspaceFromMapping instead of starting with an empty array, so the
optimistic state retains the existing local tasks until a refresh completes.

---

Outside diff comments:
In `@wework/src/features/workbench/WorkbenchProvider.tsx`:
- Around line 191-204: The resolveDeviceListWithCache function cannot
distinguish between a successful empty response (which is now legitimate since
the API pre-filters to Claude devices) and a failed network call, causing it to
incorrectly resurrect stale cached devices on successful empty responses. Add a
parameter to resolveDeviceListWithCache (e.g., allowCachedFallback) that
indicates whether the network fetch succeeded or failed, and only use the cached
device fallback when allowCachedFallback is true and claudeCodeDevices is empty,
ensuring that legitimate empty live responses are not masked by stale cache.

---

Nitpick comments:
In `@backend/tests/api/ws/test_device_capabilities_state.py`:
- Around line 398-403: The newly introduced test function
test_local_task_streaming_tool_arguments_emit_generating_block and its nested
async helper function fake_get_session are missing explicit type annotations.
Add type hint annotations to both functions: the test function should have a
return type of None, and the fake_get_session helper should have the sid
parameter typed (as str) and include a return type annotation for the dictionary
it returns. This aligns with the project coding guideline requiring type hints
for all function parameters and return values.

In `@backend/tests/services/test_local_device_provider.py`:
- Around line 40-53: The test function
test_list_devices_normalizes_legacy_remote_device_type and its nested async
helper functions fake_mget and fake_latest_version are missing type hints for
their parameters and return values. Add explicit type annotations to all
parameters and return types for these functions following the repository's
Python typing conventions. This includes annotating the test function parameters
(test_db and monkeypatch), the fake_mget parameter (keys) and return type, and
the fake_latest_version return type to comply with the coding guidelines that
require type hints for all function parameters and return values.

In `@executor/agents/claude_code/response_processor.py`:
- Around line 983-989: Replace the indirect attribute access pattern in the
three helper functions _stream_tool_state, _stream_tool_started, and
_stream_tool_finalized that currently use getattr(emitter, "__dict__",
{}).get(ATTR) with the simpler idiomatic Python approach using getattr(emitter,
ATTR, None) or getattr(emitter, ATTR, {}) depending on whether a None or empty
dict default makes sense for that function. This makes the code more readable
and follows Python conventions for attribute access with fallback defaults.

In `@executor/runtime_work/agent_adapter.py`:
- Around line 33-40: Add explicit `-> None` return type annotations to the
`__init__` constructor methods in the AgentAdapter class. For the `__init__`
method shown in the diff (with parameters store, local_task_id, runtime, and
emit_event), add `-> None` after the closing parenthesis and before the colon.
Apply the same change to the other `__init__` method mentioned at lines 212-220
to ensure all constructors have consistent typing that aligns with the coding
guidelines requiring type hints for all function return values.

In `@executor/tests/runtime_work/test_local_task_store.py`:
- Around line 2238-2241: Add type annotations to the test function
test_runtime_work_handler_streams_claude_runtime_task_events and its parameters.
Annotate the tmp_path parameter with the appropriate pytest fixture type, the
monkeypatch parameter with the MonkeyPatch type from pytest, and add a return
type annotation of None to the async function. Also apply the same type
annotation requirements to the nested async stub function mentioned in lines
2246-2249, ensuring all parameters and return types are properly annotated
according to the repository's typing standards.

In `@wework/src/components/layout/DesktopWorkbenchLayout.test.tsx`:
- Around line 1516-1528: The DesktopWorkbenchLayout.test.tsx file exceeds the
1000-line limit and needs to be split. Identify all test cases in
DesktopWorkbenchLayout.test.tsx that cover workbench project-creation and
workspace-selection functionality (including the test containing the
onPrepareDeviceWorkspace assertions around line 1516), extract those tests into
a new, focused test file dedicated to workspace and project selection scenarios,
and update any shared test utilities or mock dependencies to ensure both the
original and new test files can access them properly.

In `@wework/src/features/workbench/workbenchReducer.test.ts`:
- Around line 116-159: The test for the device_workspace_prepared action only
uses empty localTasks arrays, which doesn't verify that local tasks are properly
preserved when promoting an unmapped device workspace to a mapped one. In the
fixture setup where unmappedDeviceWorkspaces is initialized, add a sample local
task to the localTasks array instead of leaving it empty, and then add an
assertion after the reducer call to verify that the mapped workspace in
updated.runtimeWork?.projects[0].deviceWorkspaces contains the expected local
tasks that were preserved from the unmapped workspace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0b9d1a6d-52fa-4e77-b823-077eed544be5

📥 Commits

Reviewing files that changed from the base of the PR and between e40d51b and d935e06.

📒 Files selected for processing (19)
  • backend/app/services/device/local_provider.py
  • backend/tests/api/ws/test_device_capabilities_state.py
  • backend/tests/services/test_local_device_provider.py
  • executor/agents/claude_code/response_processor.py
  • executor/runtime_work/agent_adapter.py
  • executor/runtime_work/rpc_handler.py
  • executor/tests/agents/test_claude_response_processor.py
  • executor/tests/runtime_work/test_local_task_store.py
  • frontend/public/fonts/SourceHanSansSC-VF.ttf.downloading
  • frontend/src/__tests__/features/inbox/QueueEditDialog.test.tsx
  • wework/src/api/devices.test.ts
  • wework/src/api/devices.ts
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/components/projects/ProjectCreateDialog.test.tsx
  • wework/src/components/projects/ProjectCreateDialog.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/features/workbench/workbenchReducer.test.ts
  • wework/src/features/workbench/workbenchReducer.ts
  • wework/src/lib/device-capabilities.ts

Comment on lines +63 to +64
assert devices[0]["device_type"] == "local"
DeviceInfo(**devices[0])

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against empty results before indexing devices[0].

This test currently fails with IndexError if filtering/regression returns no devices, which hides the actual contract failure. Add an explicit non-empty assertion first.

Suggested patch
     devices = await LocalDeviceProvider().list_devices(test_db, user_id=7)
 
+    assert len(devices) == 1
     assert devices[0]["device_type"] == "local"
     DeviceInfo(**devices[0])
🤖 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 `@backend/tests/services/test_local_device_provider.py` around lines 63 - 64,
The test code accesses devices[0] without first verifying that the devices list
contains any elements, which can result in an IndexError that masks the actual
contract failure being tested. Before the assertions that access
devices[0]["device_type"] and the DeviceInfo(**devices[0]) call, add an explicit
assertion to ensure the devices list is not empty, such as checking that its
length is greater than zero or that it is truthy.

Comment thread executor/runtime_work/agent_adapter.py
Comment on lines +128 to +167
const nextWorkspace = runtimeWorkspaceFromMapping(mapping, devices)
const hasProject = currentRuntimeWork.projects.some(item => item.project.id === mapping.projectId)
const projectsWithTarget = hasProject
? currentRuntimeWork.projects.map(item => {
if (item.project.id !== mapping.projectId) return item
const workspaces = item.deviceWorkspaces.filter(
workspace =>
!(
workspace.deviceId === mapping.deviceId &&
workspace.workspacePath === mapping.workspacePath
)
)
return {
...item,
project: {
...item.project,
...projectRef,
},
deviceWorkspaces: [...workspaces, nextWorkspace],
}
})
: ([
...currentRuntimeWork.projects,
{
project: projectRef,
deviceWorkspaces: [nextWorkspace],
},
] as RuntimeProjectWork[])

return {
...currentRuntimeWork,
projects: projectsWithTarget,
unmappedDeviceWorkspaces: currentRuntimeWork.unmappedDeviceWorkspaces.filter(
workspace =>
!(
workspace.deviceId === mapping.deviceId &&
workspace.workspacePath === mapping.workspacePath
)
),
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve existing local tasks when promoting a workspace.

Line 128 rebuilds nextWorkspace with localTasks: [], then Lines 160-166 remove the matching unmapped workspace. Any existing local tasks for that device/path disappear from optimistic state unless a later refresh succeeds.

Proposed fix
   const currentRuntimeWork = runtimeWork ?? {
     projects: [],
     unmappedDeviceWorkspaces: [],
     totalLocalTasks: 0,
   }
-  const nextWorkspace = runtimeWorkspaceFromMapping(mapping, devices)
+  const matchesMapping = (workspace: RuntimeDeviceWorkspace) =>
+    workspace.deviceId === mapping.deviceId &&
+    workspace.workspacePath === mapping.workspacePath
+  const existingWorkspace = [
+    ...currentRuntimeWork.unmappedDeviceWorkspaces,
+    ...currentRuntimeWork.projects.flatMap(item => item.deviceWorkspaces),
+  ].find(matchesMapping)
+  const nextWorkspace = {
+    ...runtimeWorkspaceFromMapping(mapping, devices),
+    localTasks: existingWorkspace?.localTasks ?? [],
+  }
   const hasProject = currentRuntimeWork.projects.some(item => item.project.id === mapping.projectId)
   const projectsWithTarget = hasProject
     ? currentRuntimeWork.projects.map(item => {
         if (item.project.id !== mapping.projectId) return item
-        const workspaces = item.deviceWorkspaces.filter(
-          workspace =>
-            !(
-              workspace.deviceId === mapping.deviceId &&
-              workspace.workspacePath === mapping.workspacePath
-            )
-        )
+        const workspaces = item.deviceWorkspaces.filter(workspace => !matchesMapping(workspace))
+        const nextDeviceWorkspaces = [...workspaces, nextWorkspace]
         return {
           ...item,
           project: {
             ...item.project,
             ...projectRef,
           },
-          deviceWorkspaces: [...workspaces, nextWorkspace],
+          deviceWorkspaces: nextDeviceWorkspaces,
+          totalLocalTasks: nextDeviceWorkspaces.reduce(
+            (total, workspace) => total + workspace.localTasks.length,
+            0
+          ),
         }
       })
     : ([
         ...currentRuntimeWork.projects,
         {
           project: projectRef,
           deviceWorkspaces: [nextWorkspace],
+          totalLocalTasks: nextWorkspace.localTasks.length,
         },
       ] as RuntimeProjectWork[])
 
   return {
     ...currentRuntimeWork,
     projects: projectsWithTarget,
     unmappedDeviceWorkspaces: currentRuntimeWork.unmappedDeviceWorkspaces.filter(
-      workspace =>
-        !(
-          workspace.deviceId === mapping.deviceId &&
-          workspace.workspacePath === mapping.workspacePath
-        )
+      workspace => !matchesMapping(workspace)
     ),
   }
 }
🤖 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 `@wework/src/features/workbench/workbenchReducer.ts` around lines 128 - 167,
When promoting a workspace from unmapped to mapped state, the existing local
tasks are lost because nextWorkspace is created with empty localTasks. Find the
matching unmapped workspace in currentRuntimeWork.unmappedDeviceWorkspaces
before it gets filtered out (matching by deviceId and workspacePath), extract
its localTasks property, and ensure those local tasks are preserved when
creating nextWorkspace via runtimeWorkspaceFromMapping instead of starting with
an empty array, so the optimistic state retains the existing local tasks until a
refresh completes.

@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: 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 `@executor/runtime_work/agent_adapter.py`:
- Around line 202-205: The event type condition at lines 202-205 only routes
FUNCTION_CALL_ARGUMENTS_DELTA and FUNCTION_CALL_ARGUMENTS_DONE events to
_record_tool_arguments, but it's missing the corresponding
MCP_CALL_ARGUMENTS_DELTA and MCP_CALL_ARGUMENTS_DONE events. Add
MCP_CALL_ARGUMENTS_DELTA and MCP_CALL_ARGUMENTS_DONE to the set of event types
being checked in the condition at lines 202-205. Additionally, locate the status
logic around line 298 that sets "generating_arguments" for function-call deltas
and update it to also handle MCP argument delta events, ensuring mcp_call blocks
receive proper argument updates and correct status transitions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 10774004-fcb0-40ed-8d00-ae565d385268

📥 Commits

Reviewing files that changed from the base of the PR and between d935e06 and 5e65e01.

📒 Files selected for processing (2)
  • executor/runtime_work/agent_adapter.py
  • executor/tests/runtime_work/test_local_task_store.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • executor/tests/runtime_work/test_local_task_store.py

Comment on lines +202 to +205
if event_type in {
ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DELTA.value,
ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DONE.value,
}:

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.

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the structure and find the agent_adapter.py file
git ls-files | grep -E "agent_adapter|responses_api"

Repository: wecode-ai/Wegent

Length of output: 370


🏁 Script executed:

# Read the relevant section of agent_adapter.py around lines 200-310
sed -n '195,310p' executor/runtime_work/agent_adapter.py

Repository: wecode-ai/Wegent

Length of output: 4569


🏁 Script executed:

# Check the ResponsesAPIStreamEvents enum definition
cat shared/models/responses_api.py | head -300

Repository: wecode-ai/Wegent

Length of output: 7212


🏁 Script executed:

# Search for the ResponsesAPIStreamEvents enum definition
grep -n "class ResponsesAPIStreamEvents" shared/models/responses_api.py

Repository: wecode-ai/Wegent

Length of output: 201


🏁 Script executed:

# Get the full enum definition
sed -n '/class ResponsesAPIStreamEvents/,/^class /p' shared/models/responses_api.py | head -100

Repository: wecode-ai/Wegent

Length of output: 3687


🏁 Script executed:

# Search for MCP_CALL_ARGUMENTS in responses_api.py and related files
grep -r "MCP_CALL_ARGUMENTS" shared/models/

Repository: wecode-ai/Wegent

Length of output: 685


🏁 Script executed:

# Check if MCP events are used/referenced anywhere in agent_adapter
grep -n "MCP_CALL_ARGUMENTS\|mcp_call" executor/runtime_work/agent_adapter.py

Repository: wecode-ai/Wegent

Length of output: 308


🏁 Script executed:

# Get lines around 298 to verify the second claim
sed -n '290,310p' executor/runtime_work/agent_adapter.py

Repository: wecode-ai/Wegent

Length of output: 895


Add MCP argument event routing to tool-argument tracking.

Lines 202–205 route only FUNCTION_CALL_ARGUMENTS_* events to _record_tool_arguments, missing MCP_CALL_ARGUMENTS_* events. Similarly, the status logic at line 298 only sets "generating_arguments" for function-call deltas. Since mcp_call blocks are supported (lines 243, 311), they will not receive argument updates or correct status transitions when MCP argument streaming events are emitted.

Suggested fix
         if event_type in {
             ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DELTA.value,
             ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DONE.value,
+            ResponsesAPIStreamEvents.MCP_CALL_ARGUMENTS_DELTA.value,
+            ResponsesAPIStreamEvents.MCP_CALL_ARGUMENTS_DONE.value,
         }:
             self._record_tool_arguments(data)
             return
         block["status"] = (
             "generating_arguments"
-            if data.get("type")
-            == ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DELTA.value
+            if data.get("type")
+            in {
+                ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DELTA.value,
+                ResponsesAPIStreamEvents.MCP_CALL_ARGUMENTS_DELTA.value,
+            }
             else "pending"
         )
📝 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
if event_type in {
ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DELTA.value,
ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DONE.value,
}:
if event_type in {
ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DELTA.value,
ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DONE.value,
ResponsesAPIStreamEvents.MCP_CALL_ARGUMENTS_DELTA.value,
ResponsesAPIStreamEvents.MCP_CALL_ARGUMENTS_DONE.value,
}:
🤖 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 `@executor/runtime_work/agent_adapter.py` around lines 202 - 205, The event
type condition at lines 202-205 only routes FUNCTION_CALL_ARGUMENTS_DELTA and
FUNCTION_CALL_ARGUMENTS_DONE events to _record_tool_arguments, but it's missing
the corresponding MCP_CALL_ARGUMENTS_DELTA and MCP_CALL_ARGUMENTS_DONE events.
Add MCP_CALL_ARGUMENTS_DELTA and MCP_CALL_ARGUMENTS_DONE to the set of event
types being checked in the condition at lines 202-205. Additionally, locate the
status logic around line 298 that sets "generating_arguments" for function-call
deltas and update it to also handle MCP argument delta events, ensuring mcp_call
blocks receive proper argument updates and correct status transitions.

@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: 1

🧹 Nitpick comments (1)
executor/agents/claude_code/response_processor.py (1)

959-962: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tracing to the new async helper.

_emit_tool_use_start is async but is not decorated with @trace_async, so this new path won’t be traced consistently.

🔧 Proposed fix
+@trace_async
 async def _emit_tool_use_start(
     block: ToolUseBlock,
     emitter: ResponsesAPIEmitter,
 ) -> None:

If it is not already imported in this module, add:

from shared.telemetry.decorators import trace_async

As per coding guidelines, “Use @trace_async or @trace_sync decorators from shared.telemetry.decorators for tracing async and sync functions respectively.”

🤖 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 `@executor/agents/claude_code/response_processor.py` around lines 959 - 962,
The async function _emit_tool_use_start is missing the `@trace_async` decorator,
which violates the coding guidelines for consistent tracing. First, check if
trace_async is already imported from shared.telemetry.decorators in this module,
and if not, add the import statement. Then add the `@trace_async` decorator
directly above the _emit_tool_use_start function definition to ensure this async
function is properly traced.

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.

Inline comments:
In `@executor/agents/claude_code/response_processor.py`:
- Around line 986-987: The bare except Exception clause in the
response_processor.py file's tool_start event handler violates Ruff's BLE001
rule against catching overly broad exceptions. Either narrow the exception type
being caught to be more specific (like EventEmitterError or a more targeted
exception), or if catching all exceptions for this logging case is intentional,
add an explicit `# noqa: BLE001` comment with a rationale explaining why broad
exception suppression is necessary for the tool_start event emission failure
handling in that location.

---

Nitpick comments:
In `@executor/agents/claude_code/response_processor.py`:
- Around line 959-962: The async function _emit_tool_use_start is missing the
`@trace_async` decorator, which violates the coding guidelines for consistent
tracing. First, check if trace_async is already imported from
shared.telemetry.decorators in this module, and if not, add the import
statement. Then add the `@trace_async` decorator directly above the
_emit_tool_use_start function definition to ensure this async function is
properly traced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 660696cf-3630-4607-accc-3bb7b2271058

📥 Commits

Reviewing files that changed from the base of the PR and between 5e65e01 and d2eab87.

📒 Files selected for processing (2)
  • executor/agents/claude_code/response_processor.py
  • executor/tests/agents/test_claude_response_processor.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • executor/tests/agents/test_claude_response_processor.py

Comment on lines +986 to +987
except Exception as e:
logger.warning(f"Failed to send tool_start event: {e}")

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Resolve the Ruff BLE001 violation.

If suppressing all emitter failures is intentional, add an explicit noqa rationale; otherwise narrow the exception type.

🔧 Proposed fix when broad suppression is intentional
-    except Exception as e:
+    except Exception as e:  # noqa: BLE001 - event forwarding failures must not abort response processing.
         logger.warning(f"Failed to send tool_start event: {e}")
📝 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
except Exception as e:
logger.warning(f"Failed to send tool_start event: {e}")
except Exception as e: # noqa: BLE001 - event forwarding failures must not abort response processing.
logger.warning(f"Failed to send tool_start event: {e}")
🧰 Tools
🪛 Ruff (0.15.18)

[warning] 986-986: Do not catch blind exception: Exception

(BLE001)

🤖 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 `@executor/agents/claude_code/response_processor.py` around lines 986 - 987,
The bare except Exception clause in the response_processor.py file's tool_start
event handler violates Ruff's BLE001 rule against catching overly broad
exceptions. Either narrow the exception type being caught to be more specific
(like EventEmitterError or a more targeted exception), or if catching all
exceptions for this logging case is intentional, add an explicit `# noqa:
BLE001` comment with a rationale explaining why broad exception suppression is
necessary for the tool_start event emission failure handling in that location.

Source: Linters/SAST tools

@qdaxb
qdaxb merged commit fd355dc into wecode-ai:main Jun 23, 2026
24 checks passed
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.

2 participants