Skip to content

fix: 修复executor同时修改同一个文件目录,导致异常终止的问题 - #1423

Merged
qdaxb merged 12 commits into
wecode-ai:mainfrom
Micro66:human/owl-20260616-060243
Jun 17, 2026
Merged

qdaxb merged 12 commits into
wecode-ai:mainfrom
Micro66:human/owl-20260616-060243

Conversation

@Micro66

@Micro66 Micro66 commented Jun 16, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

Release Notes

  • New Features
    • Added per-file selection/preview in the file changes card and a target-aware diff review dialog.
    • Enabled unified external-link handling for opening URLs (including desktop/PR flows).
  • Bug Fixes
    • Improved turn file-change tracking by removing workspace locking and enhancing per-turn artifact collection for different edit flows.
  • Documentation
    • Added localized UI strings for per-file review titles/labels and file-level empty diff messaging (including Chinese).
  • Chores
    • Integrated the Tauri opener plugin and updated default permissions for opening external URLs, plus added updater config.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40d82924-8a6f-497a-b568-445793d370fe

📥 Commits

Reviewing files that changed from the base of the PR and between 19427c1 and 82abc8c.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • wework/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • wework/package.json
  • wework/src-tauri/Cargo.toml
  • wework/src-tauri/capabilities/default.json
  • wework/src-tauri/src/lib.rs
✅ Files skipped from review due to trivial changes (1)
  • wework/src-tauri/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (3)
  • wework/src-tauri/src/lib.rs
  • wework/package.json
  • wework/src-tauri/capabilities/default.json

📝 Walkthrough

Walkthrough

This PR adds multi-source turn file change tracking (NativeTurnFileChangeTracker, ClaudeToolFileChangeTracker, TurnFileChangeArtifactStore) to the executor and wires each into its respective agent. On the frontend, it introduces a Tauri plugin-opener-backed openExternalUrl utility replacing raw window.open calls, and adds per-file diff preview selection to FileChangesCard and FileChangesReviewDialog.

Changes

Executor: Multi-source turn file change tracking

Layer / File(s) Summary
TurnFileChangeArtifactStore and new tracker classes
executor/services/turn_file_changes.py
Introduces _PatchStats and _FileContentSnapshot dataclasses; adds TurnFileChangeArtifactStore with persist, summarize_patch, completion_fields_from_patch, and atomic_write; adds NativeTurnFileChangeTracker (records agent-provided diffs) and ClaudeToolFileChangeTracker (captures patches at tool-use hook boundaries). TurnFileChangeTracker.start/finalize/abort updated to remove workspace locking and delegate persistence to store.
CodeXAgent and CodeXEventMapper integration
executor/agents/codex/event_mapper.py, executor/agents/codex/codex_agent.py
CodeXEventMapper accepts an optional NativeTurnFileChangeTracker and calls record_diff on turn/diff/updated events. CodeXAgent conditionally constructs the tracker when device_id is present, updates the emitter's completion-fields provider, and removes the old start_turn_file_change_tracking call.
ClaudeCodeAgent hook-based tracking installation
executor/agents/claude_code/claude_code_agent.py
Adds _install_turn_file_change_hooks() that creates a ClaudeToolFileChangeTracker and injects PreToolUse/PostToolUse hook matchers into self.options["hooks"] using HookMatcher. Called during client/options setup; removed prior start_turn_file_change_tracking call.
Response processor tool boundary recording
executor/agents/claude_code/response_processor.py
process_response accepts optional tool_file_change_tracker parameter; propagates it to _handle_user_message and _handle_assistant_message. Records tool-use start boundaries on ToolUseBlock and tool-result boundaries on ToolResultBlock via tracker methods.
Base agent tracking type and lifecycle
executor/agents/base.py
Loosens turn_file_change_tracker type to Optional[Any]; updates start_turn_file_change_tracking to await tracker.start() into started variable and proceed conditionally. Removes workspace locking.
Executor tracking tests
executor/tests/services/test_turn_file_changes.py, executor/tests/agents/test_codex_event_mapper.py, executor/tests/agents/test_claude_code_config_security.py, executor/tests/agents/test_claude_response_processor.py
Covers NativeTurnFileChangeTracker artifact persistence, ClaudeToolFileChangeTracker pre/post tool-use patch capture, CodeXEventMapper diff forwarding, ClaudeCodeAgent hook installation and idempotency, response processor tool boundary recording.

Wework: Tauri plugin-opener and external link handler

Layer / File(s) Summary
Tauri plugin-opener dependency and registration
wework/package.json, wework/src-tauri/Cargo.toml, wework/src-tauri/capabilities/default.json, wework/src-tauri/src/lib.rs, wework/src-tauri/tauri.conf.json
Adds @tauri-apps/plugin-opener to npm and Cargo dependencies, grants opener:allow-open-url/opener:allow-default-urls capabilities, registers tauri_plugin_opener::init() in the builder chain, and adds a plugins.updater stub to tauri.conf.json.
external-links utility with isHttpUrl, openExternalUrl, and link handler
wework/src/lib/external-links.ts, wework/src/lib/external-links.test.ts, wework/src/main.tsx
Adds isHttpUrl validator for http/https protocols; openExternalUrl that dynamically imports Tauri opener in Tauri runtime or falls back to window.open; installExternalLinkHandler that intercepts left-clicks on matching anchors and routes through openExternalUrl. Full Vitest coverage for both runtimes and DOM interaction. Handler installed in main.tsx at boot.
Migrate window.open call sites to openExternalUrl
wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx, wework/src/components/settings/ConnectionsSettingsPage.tsx, wework/src/components/layout/EnvironmentInfoPopover.tsx, wework/src/components/layout/workspace-panels/WorkspacePanelCards.test.tsx
Replaces direct window.open calls for IDE/desktop/VNC/PR URLs with openExternalUrl. Tests updated to expect noopener,noreferrer feature string.

Wework: Per-file diff preview in FileChangesCard

Layer / File(s) Summary
FileChangesReviewDialog targetFile prop and scoped collapse state
wework/src/components/chat/FileChangesReviewDialog.tsx
Adds FileChangesReviewTarget type and optional targetFile prop. Reworks dialog state to use reviewScope-keyed collapse state so expansion/collapse resets when target changes. Filters diff sections to matching target paths; updates title and empty-state messaging.
FileChangesCard per-file selection and lazy-load diff
wework/src/components/chat/FileChangesCard.tsx
Replaces static FileChangeRow with a clickable button accepting onPreview(file). Adds reviewFile/reviewError state; openReview lazily loads diff when needed and passes targetFile into FileChangesReviewDialog. Adjusts ConfirmRevertDialog styling.
i18n strings, Tailwind base color, and theme-guard coverage
wework/src/i18n/locales/en/chat.json, wework/src/i18n/locales/zh-CN/chat.json, wework/tailwind.config.js, wework/src/test/theme-token-guard.test.ts
Adds file_review_title, preview_file_label, empty_file_diff strings in EN/ZH. Adds base Tailwind color token. Extends theme-token guard to cover FileChangesCard.tsx and verifies semantic surface color tokens.
FileChangesCard component tests
wework/src/components/chat/FileChangesCard.test.tsx
Adds test for clicking a file row to open only that file's diff. Updates revert confirmation test to use confirm-revert button. Reformats parseUnifiedDiff tests to use inline arguments.

Sequence Diagrams

sequenceDiagram
  participant CodeXAgent
  participant NativeTurnFileChangeTracker
  participant CodeXEventMapper
  participant TurnFileChangeArtifactStore
  CodeXAgent->>NativeTurnFileChangeTracker: construct when device_id present
  CodeXAgent->>CodeXEventMapper: construct with tracker
  CodeXAgent->>TurnFileChangeArtifactStore: completion_fields_provider uses finalize
  CodeXEventMapper->>NativeTurnFileChangeTracker: record_diff on turn/diff/updated
  CodeXAgent->>NativeTurnFileChangeTracker: finalize at turn end
Loading
sequenceDiagram
  participant User
  participant FileChangesCard
  participant onLoadDiff
  participant FileChangesReviewDialog
  User->>FileChangesCard: click FileChangeRow (file)
  FileChangesCard->>FileChangesCard: set reviewFile
  FileChangesCard->>FileChangesCard: open dialog
  alt diff not cached
    FileChangesCard->>onLoadDiff: load diff for subtask
    onLoadDiff-->>FileChangesCard: diff bytes
  end
  FileChangesCard->>FileChangesReviewDialog: pass targetFile + diff
  FileChangesReviewDialog-->>User: display scoped diff
Loading
flowchart LR
  openExternalUrl --> isHttpCheck["isHttpUrl()"]
  isHttpCheck -- false --> returnFalse["return false"]
  isHttpCheck -- true --> tauriCheck{"__TAURI_INTERNALS__?"}
  tauriCheck -- yes --> tauriOpener["await openUrl()"]
  tauriCheck -- no --> windowOpen["window.open()"]
  tauriOpener --> returnTrue["return true"]
  windowOpen --> returnTrue
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • wecode-ai/Wegent#1427: Both PRs touch executor/services/turn_file_changes.py's turn tracking concurrency/locking behavior (main PR removes/refactors the locking, while retrieved PR changes lock-path resolution to support concurrent worktrees), so the changes are closely related at the tracker level.

Suggested reviewers

  • qdaxb

Poem

🐇 A rabbit once tracked every file that changed,
With hooks and with stores, all neatly arranged.
The links now hop safely through Tauri's front door,
And each diff you click shows just one file—no more!
From CodeX to Claude, the patches compile,
The warren of changes stretches for a mile. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.43% 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 PR title is in Chinese and addresses a specific executor issue about concurrent file directory modifications causing abnormal termination, which aligns with the changes to file tracking, lock removal, and parallel execution support.
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 and usage tips.

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

Caution

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

⚠️ Outside diff range comments (1)
wework/src/components/layout/EnvironmentInfoPopover.tsx (1)

77-82: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid unhandled promise rejection when opening PR URL.

Line 81 calls an async opener with void and no rejection handling. If opener fails in Tauri runtime, this can surface as an unhandled rejection.

Suggested fix
-  function handleCreatePullRequest() {
+  async function handleCreatePullRequest() {
     if (!info.createPullRequestUrl) {
       return
     }
-    void openExternalUrl(info.createPullRequestUrl)
+    try {
+      await openExternalUrl(info.createPullRequestUrl)
+    } catch (error) {
+      console.error('Failed to open pull request URL:', error)
+    }
   }
🤖 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/EnvironmentInfoPopover.tsx` around lines 77 -
82, The handleCreatePullRequest function calls the async openExternalUrl
function with the void operator but provides no error handling for potential
rejections. Add a catch handler to the openExternalUrl call to properly handle
any errors that may occur when attempting to open the external URL, preventing
unhandled promise rejections in the Tauri runtime. You can use a .catch() method
chained to the function call or wrap it in a try-catch block if restructuring to
async-await.
🧹 Nitpick comments (2)
wework/src/components/chat/FileChangesReviewDialog.tsx (1)

121-125: ⚡ Quick win

Add disclosure ARIA attributes to the section toggle button.

This toggle controls collapsible content but doesn’t expose expanded state to assistive tech. Add aria-expanded and aria-controls (with a stable panel id) so screen readers can track section state.

Suggested patch
-                    <button
+                    <button
                       type="button"
                       onClick={() => toggleSection(index)}
+                      aria-expanded={!collapsed.has(index)}
+                      aria-controls={`file-diff-panel-${index}`}
                       className="flex w-full items-center gap-1.5 border-b border-border bg-surface px-2.5 py-1.5 text-left font-mono text-xs font-medium text-text-primary hover:bg-muted"
                     >
@@
-                      <pre className="grid overflow-x-auto bg-background p-0 font-mono text-xs leading-5">
+                      <pre
+                        id={`file-diff-panel-${index}`}
+                        className="grid overflow-x-auto bg-background p-0 font-mono text-xs leading-5"
+                      >
🤖 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/chat/FileChangesReviewDialog.tsx` around lines 121 -
125, The toggle button in FileChangesReviewDialog that calls
toggleSection(index) is missing accessibility attributes required by screen
readers to understand its expanded/collapsed state. Add the aria-expanded
attribute set to the boolean value indicating whether the section at the given
index is expanded or collapsed, and add the aria-controls attribute set to a
stable panel identifier (typically constructed using the index, such as
"section-panel-" plus the index number) that corresponds to the id of the
collapsible content panel that this button controls. This ensures assistive
technology can properly announce the section's state and its relationship to the
controlled content.
wework/src/main.tsx (1)

11-11: ⚡ Quick win

Dispose the global click handler on HMR updates.

At Line [11], the returned cleanup function is ignored; during hot reloads this can accumulate duplicate document listeners.

Suggested change
 installDesktopExtensions()
-installExternalLinkHandler()
+const removeExternalLinkHandler = installExternalLinkHandler()
+if (import.meta.hot) {
+  import.meta.hot.dispose(removeExternalLinkHandler)
+}
 installPageZoomGuard()
🤖 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/main.tsx` at line 11, The call to installExternalLinkHandler()
returns a cleanup function that is currently being ignored. During hot module
replacement (HMR) updates, this causes duplicate document listeners to
accumulate. Capture the returned cleanup function from
installExternalLinkHandler() and ensure it is called during HMR updates by
integrating it with your module's hot reload handler to properly dispose of the
previous event listeners before new ones are installed.
🤖 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/claude_code_agent.py`:
- Around line 1218-1231: The hook registration in the loop for "PreToolUse" and
"PostToolUse" events appends HookMatcher objects to existing hooks retrieved
from self.options, causing duplicate hooks when _create_and_connect_client() is
called multiple times. Instead of fetching and appending to existing hooks,
initialize a fresh hooks dictionary or explicitly reset the event_hooks for
these specific event names before appending the new HookMatcher. This ensures
that each call to _create_and_connect_client() registers the hooks exactly once
without accumulation.

In `@executor/services/turn_file_changes.py`:
- Around line 400-462: The newly added methods in the
TurnFileChangeArtifactStore class and related tracker/store methods lack
OpenTelemetry tracing decorators, creating observability gaps. First, ensure the
tracing decorators are imported from 'shared/telemetry/decorators.py' at the top
of the file. Then, apply `@trace_sync`() decorator to all synchronous methods and
`@trace_async`() decorator to all asynchronous methods across the affected
locations: the TurnFileChangeArtifactStore class and its methods including
completion_fields_from_patch at lines 400-462, the methods at lines 648-657, the
methods at lines 735-852, and the methods at lines 863-895. This will instrument
all new tracker/store lifecycle paths with proper tracing for observability.

In `@wework/src/components/chat/FileChangesCard.test.tsx`:
- Around line 108-110: The negative assertion at line 108 is checking for 'old
one' but the actual rendered text includes the dash prefix, so it should check
for '-old one' to match the rendered line format and properly verify that the
first file content is excluded. Update the queryByText call in the assertion to
include the dash prefix to be consistent with the subsequent assertion checking
for '-old two'.

In `@wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx`:
- Around line 161-163: The openExternalUrl function returns a boolean indicating
success or failure, but the code ignores this return value and always sets
shouldClosePanel to true. At both locations (lines 161-163 and 185-186 in
WorkspacePanelCards.tsx), wrap the shouldClosePanel assignment in a conditional
check: only set shouldClosePanel to true if openExternalUrl returns true. This
ensures the panel only closes when the URL was actually opened successfully, not
when validation fails due to non-HTTP(S) URLs.

In `@wework/src/lib/external-links.ts`:
- Around line 25-26: The browser fallback in the external-links.ts file
unconditionally returns true when calling window.open, even though window.open
can return null when popups are blocked. Capture the return value from the
window.open() call and check whether it was successful (non-null) before
returning the result. Return the actual success status instead of always
returning true, so that blocked popups are properly reported as failures.

---

Outside diff comments:
In `@wework/src/components/layout/EnvironmentInfoPopover.tsx`:
- Around line 77-82: The handleCreatePullRequest function calls the async
openExternalUrl function with the void operator but provides no error handling
for potential rejections. Add a catch handler to the openExternalUrl call to
properly handle any errors that may occur when attempting to open the external
URL, preventing unhandled promise rejections in the Tauri runtime. You can use a
.catch() method chained to the function call or wrap it in a try-catch block if
restructuring to async-await.

---

Nitpick comments:
In `@wework/src/components/chat/FileChangesReviewDialog.tsx`:
- Around line 121-125: The toggle button in FileChangesReviewDialog that calls
toggleSection(index) is missing accessibility attributes required by screen
readers to understand its expanded/collapsed state. Add the aria-expanded
attribute set to the boolean value indicating whether the section at the given
index is expanded or collapsed, and add the aria-controls attribute set to a
stable panel identifier (typically constructed using the index, such as
"section-panel-" plus the index number) that corresponds to the id of the
collapsible content panel that this button controls. This ensures assistive
technology can properly announce the section's state and its relationship to the
controlled content.

In `@wework/src/main.tsx`:
- Line 11: The call to installExternalLinkHandler() returns a cleanup function
that is currently being ignored. During hot module replacement (HMR) updates,
this causes duplicate document listeners to accumulate. Capture the returned
cleanup function from installExternalLinkHandler() and ensure it is called
during HMR updates by integrating it with your module's hot reload handler to
properly dispose of the previous event listeners before new ones are installed.
🪄 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: bf959ff0-c834-4951-8fbc-1678d29b5695

📥 Commits

Reviewing files that changed from the base of the PR and between b54fe77 and b053bb4.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • wework/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (28)
  • docs/en/developer-guide/wework-startup-animation.md
  • docs/zh/developer-guide/wework-startup-animation.md
  • executor/agents/base.py
  • executor/agents/claude_code/claude_code_agent.py
  • executor/agents/codex/codex_agent.py
  • executor/agents/codex/event_mapper.py
  • executor/services/turn_file_changes.py
  • executor/tests/agents/test_claude_code_config_security.py
  • executor/tests/agents/test_codex_event_mapper.py
  • executor/tests/services/test_turn_file_changes.py
  • wework/package.json
  • wework/src-tauri/Cargo.toml
  • wework/src-tauri/capabilities/default.json
  • wework/src-tauri/src/lib.rs
  • wework/src/components/chat/FileChangesCard.test.tsx
  • wework/src/components/chat/FileChangesCard.tsx
  • wework/src/components/chat/FileChangesReviewDialog.tsx
  • wework/src/components/layout/EnvironmentInfoPopover.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelCards.test.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx
  • wework/src/components/settings/ConnectionsSettingsPage.tsx
  • wework/src/i18n/locales/en/chat.json
  • wework/src/i18n/locales/zh-CN/chat.json
  • wework/src/lib/external-links.test.ts
  • wework/src/lib/external-links.ts
  • wework/src/main.tsx
  • wework/src/test/theme-token-guard.test.ts
  • wework/tailwind.config.js

Comment thread executor/agents/claude_code/claude_code_agent.py
Comment thread executor/services/turn_file_changes.py
Comment thread wework/src/components/chat/FileChangesCard.test.tsx
Comment thread wework/src/lib/external-links.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx`:
- Around line 153-171: The Promise.all coupling of getLocalExecutorDeviceId and
localPathExists operations causes both probes to fail together, incorrectly
clearing executorDeviceId when only localPathExists fails. Decouple these two
probes by calling them separately instead of with Promise.all, and handle each
with its own error handling. This way, getLocalExecutorDeviceId can still
populate executorDeviceId even if localPathExists fails, and vice versa. Each
probe should independently update the corresponding field in the
setLocalTerminalCheck call (deviceId from getLocalExecutorDeviceId and
pathExists from localPathExists), with sensible defaults in their respective
error handlers.

In `@wework/src/components/settings/ConnectionsSettingsPage.tsx`:
- Around line 534-537: The window.open call in the startTerminal result handler
bypasses the centralized external-link guard and security validation. Replace
the window.open call with openExternalUrl(result.url) to ensure proper http(s)
validation, Tauri opener integration, and standardized security attributes
(noopener,noreferrer). Ensure openExternalUrl is imported from the appropriate
utility module if not already available in the file.
🪄 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: 620a9237-24b2-4fde-b14d-bba9da650be7

📥 Commits

Reviewing files that changed from the base of the PR and between b053bb4 and c6cd7e9.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • wework/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • wework/package.json
  • wework/src-tauri/Cargo.toml
  • wework/src-tauri/capabilities/default.json
  • wework/src-tauri/src/lib.rs
  • wework/src-tauri/tauri.conf.json
  • wework/src/components/chat/FileChangesCard.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelCards.test.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx
  • wework/src/components/settings/ConnectionsSettingsPage.tsx
✅ Files skipped from review due to trivial changes (1)
  • wework/src-tauri/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (2)
  • wework/src-tauri/capabilities/default.json
  • wework/src-tauri/src/lib.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 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 `@wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx`:
- Around line 153-171: The Promise.all coupling of getLocalExecutorDeviceId and
localPathExists operations causes both probes to fail together, incorrectly
clearing executorDeviceId when only localPathExists fails. Decouple these two
probes by calling them separately instead of with Promise.all, and handle each
with its own error handling. This way, getLocalExecutorDeviceId can still
populate executorDeviceId even if localPathExists fails, and vice versa. Each
probe should independently update the corresponding field in the
setLocalTerminalCheck call (deviceId from getLocalExecutorDeviceId and
pathExists from localPathExists), with sensible defaults in their respective
error handlers.

In `@wework/src/components/settings/ConnectionsSettingsPage.tsx`:
- Around line 534-537: The window.open call in the startTerminal result handler
bypasses the centralized external-link guard and security validation. Replace
the window.open call with openExternalUrl(result.url) to ensure proper http(s)
validation, Tauri opener integration, and standardized security attributes
(noopener,noreferrer). Ensure openExternalUrl is imported from the appropriate
utility module if not already available in the file.
🪄 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: 620a9237-24b2-4fde-b14d-bba9da650be7

📥 Commits

Reviewing files that changed from the base of the PR and between b053bb4 and c6cd7e9.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • wework/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • wework/package.json
  • wework/src-tauri/Cargo.toml
  • wework/src-tauri/capabilities/default.json
  • wework/src-tauri/src/lib.rs
  • wework/src-tauri/tauri.conf.json
  • wework/src/components/chat/FileChangesCard.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelCards.test.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx
  • wework/src/components/settings/ConnectionsSettingsPage.tsx
✅ Files skipped from review due to trivial changes (1)
  • wework/src-tauri/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (2)
  • wework/src-tauri/capabilities/default.json
  • wework/src-tauri/src/lib.rs
🛑 Comments failed to post (2)
wework/src/components/layout/workspace-panels/WorkspacePanelCards.tsx (1)

153-171: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Decouple executor-id and local-path probes to avoid false negative local terminal blocking.

A failure in localPathExists(projectLocalPath) currently clears executorDeviceId too, because both probes are tied by Promise.all. That can disable local terminal even when executor IDs already match.

Suggested fix
-    Promise.all([getLocalExecutorDeviceId(apiBaseUrl), localPathExists(projectLocalPath)])
-      .then(([deviceId, pathExists]) => {
-        if (!cancelled) {
-          setLocalTerminalCheck({
-            key: localTerminalCheckKey,
-            executorDeviceId: deviceId,
-            pathExists,
-          })
-        }
-      })
-      .catch(() => {
-        if (!cancelled) {
-          setLocalTerminalCheck({
-            key: localTerminalCheckKey,
-            executorDeviceId: null,
-            pathExists: false,
-          })
-        }
-      })
+    Promise.allSettled([getLocalExecutorDeviceId(apiBaseUrl), localPathExists(projectLocalPath)]).then(
+      ([deviceIdResult, pathExistsResult]) => {
+        if (cancelled) return
+        setLocalTerminalCheck({
+          key: localTerminalCheckKey,
+          executorDeviceId: deviceIdResult.status === 'fulfilled' ? deviceIdResult.value : null,
+          pathExists: pathExistsResult.status === 'fulfilled' ? pathExistsResult.value : false,
+        })
+      }
+    )
🤖 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/workspace-panels/WorkspacePanelCards.tsx` around
lines 153 - 171, The Promise.all coupling of getLocalExecutorDeviceId and
localPathExists operations causes both probes to fail together, incorrectly
clearing executorDeviceId when only localPathExists fails. Decouple these two
probes by calling them separately instead of with Promise.all, and handle each
with its own error handling. This way, getLocalExecutorDeviceId can still
populate executorDeviceId even if localPathExists fails, and vice versa. Each
probe should independently update the corresponding field in the
setLocalTerminalCheck call (deviceId from getLocalExecutorDeviceId and
pathExists from localPathExists), with sensible defaults in their respective
error handlers.
wework/src/components/settings/ConnectionsSettingsPage.tsx (1)

534-537: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use openExternalUrl here instead of raw window.open.

This path bypasses the centralized external-link guard (http(s) validation + Tauri opener) and drops the standardized noopener,noreferrer behavior.

Suggested fix
       const result = await createSettingsDeviceApi().startTerminal(device.device_id)
       if (result.url) {
-        window.open(result.url, '_blank', 'noopener')
+        const opened = await openExternalUrl(result.url)
+        if (!opened) {
+          throw new Error('Terminal session URL must be http(s)')
+        }
       }
📝 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.

      const result = await createSettingsDeviceApi().startTerminal(device.device_id)
      if (result.url) {
        const opened = await openExternalUrl(result.url)
        if (!opened) {
          throw new Error('Terminal session URL must be http(s)')
        }
      }
🤖 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/settings/ConnectionsSettingsPage.tsx` around lines 534
- 537, The window.open call in the startTerminal result handler bypasses the
centralized external-link guard and security validation. Replace the window.open
call with openExternalUrl(result.url) to ensure proper http(s) validation, Tauri
opener integration, and standardized security attributes (noopener,noreferrer).
Ensure openExternalUrl is imported from the appropriate utility module if not
already available in the file.

@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 (3)
executor/services/turn_file_changes.py (3)

499-506: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Parse diff --git headers without splitting on every space.

line.split(" ") misparses valid paths like my file.txt, so file_changes.files can report the wrong path for artifacts containing spaces in filenames.

Suggested parser adjustment
     `@staticmethod`
     def _paths_from_diff_header(line: str) -> tuple[Optional[str], Optional[str]]:
-        parts = line.split(" ")
-        if len(parts) < 4:
+        prefix = "diff --git "
+        if not line.startswith(prefix):
             return None, None
-        old_path = TurnFileChangeArtifactStore._strip_diff_prefix(parts[2])
-        new_path = TurnFileChangeArtifactStore._strip_diff_prefix(parts[3])
+        payload = line.removeprefix(prefix)
+        if not payload.startswith("a/") or " b/" not in payload:
+            return None, None
+        old_raw, new_raw = payload.rsplit(" b/", 1)
+        old_path = TurnFileChangeArtifactStore._strip_diff_prefix(old_raw)
+        new_path = TurnFileChangeArtifactStore._strip_diff_prefix(f"b/{new_raw}")
         return new_path, old_path
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/services/turn_file_changes.py` around lines 499 - 506, The
_paths_from_diff_header method uses line.split(" ") which incorrectly parses
diff headers when file paths contain spaces. Instead of splitting on every space
character, parse the diff header by locating the positions of " a/" and " b/"
prefixes to properly extract the old_path and new_path components, since the
standard diff --git format places these prefixes before the file paths and
splitting indiscriminately corrupts paths with spaces in their names.

677-695: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the first baseline for a tool-use ID.

The same tracker is wired from Claude hooks and response-stream recording, so pre_tool_use() can run twice for one tool_use_id before post_tool_use(). The second call currently overwrites the earlier snapshot, which can make the captured patch empty or partial if it fires later.

Suggested guard
         if tool_use_id in self._completed_tool_use_ids:
             logger.info(
                 "Claude edit baseline skipped because tool already completed: task_id=%s subtask_id=%s tool_use_id=%s",
                 self.task_id,
                 self.subtask_id,
                 tool_use_id,
             )
             return {}
+        if self._has_baseline(tool_use_id):
+            logger.info(
+                "Claude edit baseline skipped because baseline already exists: task_id=%s subtask_id=%s tool_use_id=%s",
+                self.task_id,
+                self.subtask_id,
+                tool_use_id,
+            )
+            return {}
         try:
🤖 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/services/turn_file_changes.py` around lines 677 - 695, The
pre_tool_use() method can be called twice for the same tool_use_id before
post_tool_use() executes, causing the second call to overwrite the baseline
snapshot. Add a guard condition before capturing the baseline in both branches
(git snapshots and non-git file snapshots) to check if the baseline has already
been recorded for this tool_use_id. Specifically, check if tool_use_id already
exists in self._before_tree_by_tool_use_id (for the git snapshots branch) or
self._before_file_by_tool_use_id (for the file snapshots branch), and skip the
baseline capture if it has already been recorded.

811-818: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Summarize the net change, not the rollback-ordered patch.

finalize() reverses per-tool patches before summarizing, while _changed_paths_from_patch() and _numstat_by_path() collapse duplicate paths by overwrite. If the same file is edited multiple times, or modified then deleted, the user-facing change_type and additions/deletions can reflect the wrong diff section rather than the final workspace state.

Persist the rollback-ordered patch if that is needed for revert, but compute file_changes from a chronological/net patch or aggregate duplicate paths with final-state precedence.

Also applies to: 409-430, 469-469, 523-526

🤖 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/services/turn_file_changes.py` around lines 811 - 818, The
finalize() method reverses patches before computing file changes via
completion_fields_from_patch(), which causes issues when files are edited
multiple times or modified then deleted—the summarized change_type and
additions/deletions reflect intermediate states rather than the final workspace
state. Keep the rollback-ordered patch if needed for revert functionality, but
compute file_changes from chronological (non-reversed) patches or modify
_changed_paths_from_patch() and _numstat_by_path() to aggregate duplicate paths
using final-state precedence so that when the same file appears multiple times
in the patch, the last occurrence determines the reported change_type and
statistics. This fix applies across the finalize() method at lines 811-818
(anchor), and related patch processing code at lines 409-430, line 469, and
lines 523-526 (siblings).
🤖 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/base.py`:
- Around line 154-155: The unguarded `await tracker.start()` call can raise
exceptions during Git baseline capture, causing the entire turn to fail even
though tracking is an optional feature. Wrap the `await tracker.start()` call in
a try-except block to catch any exceptions, log the error appropriately, and
ensure `started` is set to False when startup fails so the turn can continue
executing without tracking.

In `@executor/services/turn_file_changes.py`:
- Line 19: Add `ClassVar` to the imports from the typing module on the import
line. Then locate the `EDIT_TOOL_MATCHER` and `EDIT_TOOL_NAMES` class attributes
and annotate both with `ClassVar` type hints to indicate they are class
variables. Additionally, convert the `EDIT_TOOL_NAMES` set literal to a
`frozenset` to make it immutable and resolve the RUF012 warning about mutable
default values.

In `@executor/tests/services/test_turn_file_changes.py`:
- Line 245: Add type hints to the async test functions as required by the
repository's Python coding guidelines. In
executor/tests/services/test_turn_file_changes.py at line 245, add type hints to
the test_tracker_allows_concurrent_turns_in_same_workspace function by
annotating the tmp_path parameter with the appropriate pathlib type and adding a
return type hint of None. Apply the same changes to the other async test
function at line 352 in the same file, ensuring both functions have complete
type annotations for their parameters and return types to comply with repository
standards.

---

Outside diff comments:
In `@executor/services/turn_file_changes.py`:
- Around line 499-506: The _paths_from_diff_header method uses line.split(" ")
which incorrectly parses diff headers when file paths contain spaces. Instead of
splitting on every space character, parse the diff header by locating the
positions of " a/" and " b/" prefixes to properly extract the old_path and
new_path components, since the standard diff --git format places these prefixes
before the file paths and splitting indiscriminately corrupts paths with spaces
in their names.
- Around line 677-695: The pre_tool_use() method can be called twice for the
same tool_use_id before post_tool_use() executes, causing the second call to
overwrite the baseline snapshot. Add a guard condition before capturing the
baseline in both branches (git snapshots and non-git file snapshots) to check if
the baseline has already been recorded for this tool_use_id. Specifically, check
if tool_use_id already exists in self._before_tree_by_tool_use_id (for the git
snapshots branch) or self._before_file_by_tool_use_id (for the file snapshots
branch), and skip the baseline capture if it has already been recorded.
- Around line 811-818: The finalize() method reverses patches before computing
file changes via completion_fields_from_patch(), which causes issues when files
are edited multiple times or modified then deleted—the summarized change_type
and additions/deletions reflect intermediate states rather than the final
workspace state. Keep the rollback-ordered patch if needed for revert
functionality, but compute file_changes from chronological (non-reversed)
patches or modify _changed_paths_from_patch() and _numstat_by_path() to
aggregate duplicate paths using final-state precedence so that when the same
file appears multiple times in the patch, the last occurrence determines the
reported change_type and statistics. This fix applies across the finalize()
method at lines 811-818 (anchor), and related patch processing code at lines
409-430, line 469, and lines 523-526 (siblings).
🪄 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: 927fa2e6-ea58-4aca-b47f-3b539e07a43b

📥 Commits

Reviewing files that changed from the base of the PR and between f63b0cc and 19427c1.

📒 Files selected for processing (8)
  • executor/agents/base.py
  • executor/agents/claude_code/claude_code_agent.py
  • executor/agents/claude_code/response_processor.py
  • executor/services/turn_file_changes.py
  • executor/tests/agents/test_claude_code_config_security.py
  • executor/tests/agents/test_claude_response_processor.py
  • executor/tests/agents/test_codex_event_mapper.py
  • executor/tests/services/test_turn_file_changes.py
💤 Files with no reviewable changes (1)
  • executor/tests/agents/test_codex_event_mapper.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • executor/tests/agents/test_claude_code_config_security.py

Comment thread executor/agents/base.py
Comment thread executor/services/turn_file_changes.py
Comment thread executor/tests/services/test_turn_file_changes.py
@qdaxb
qdaxb merged commit c202663 into wecode-ai:main Jun 17, 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