Skip to content

[STA-6940] Prepare file drops for element owners (PR 2 of 6) - #25748

Merged
brennanb2025 merged 5 commits into
mainfrom
brennanb2025/sta-6940-pr2-drop-plumbing
Oct 7, 2026
Merged

brennanb2025 merged 5 commits into
mainfrom
brennanb2025/sta-6940-pr2-drop-plumbing

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1206 $\color{#cf222e}{\Huge{\mathbf{−}}}$​21 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1185
Prod 19 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​642 $\color{#cf222e}{\Huge{\mathbf{−}}}$​187 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​455

ELI5

An OS file drop can reach a hidden chat, another terminal split, or the active workspace because Orca intercepts the real drop and broadcasts a reconstructed destination. This is PR 2 of 6 for STA-6940: it adds the preparation and ownership foundation while keeping desktop OS file drops on their existing path. Independent review found that the new web navigation guard overwrote feedback’s accepting drag cursor, so dragging a screenshot onto feedback could stop working; the unused owner hook also silently lost files with no readable path, sequenced sibling roots independently, and lacked a synchronous destination snapshot.

CI then failed at 2e3f19a7b0d37ae97a8c21a268b5ee48d69a5627 because the new file-path function was added at the top level of the web API, breaking its existing namespace list. The desktop bridge now lives under window.api.fs; web omits it, and the owner hook checks that it actually exists before resolving paths. This correction adds no user-visible behavior.

What Changed

  • Expose synchronous file-path resolution as optional window.api.fs.getPathForFile on desktop and a window-local fs:prepareDroppedPaths request returning successful paths and failures. The request and legacy relay reuse one screenshot-copy queue, with the existing byte budgets, eight-pending limit, two-minute timeout, reload cancellation, and seven-day expiry. Agent consumers cannot receive an uncopied temporary original; main-process readers may receive budget-limited originals.
  • Add an unused useOsFileDropOwner(ref, options) hook. Its callback ref registers the marker and native capture listeners together; the nearest registered root claims and resolves paths synchronously after the file-count cap. A required sequence created once by the destination is passed to each sibling root, keeping preparation results and asynchronous application in drop order while independent destinations continue. Arbitration and detach suppression stay local to each root.
  • Retain every successfully resolved path and report the count of unresolved files, including empty or throwing path resolutions, together with preparation failures in one ordered reply. Rejections go to the destination’s existing feedback without new UI.
  • Add an optional synchronous captureDestination(event) callback. It runs during the drop before preparation starts and passes the captured value to onDrop, so PR5 can retain an explorer folder even if the target row is recycled while preparation waits.
  • Let preload release OS-file events only when the nearest migrated-or-legacy boundary is migrated. No production surface is marked migrated here. Legacy markers still block release.
  • Install navigation-cancellation guards in desktop, dashboard popout, and web entry points. Preserve a claim made before the guard so the legacy desktop copy cursor stays unchanged. The existing feedback handler stops propagation after accepting dragover, preserving its web copy cursor and one image attachment; unowned web chrome remains silently refused. The guard does not treat its own capture cancellation as a destination claim.
  • Disable Monaco's built-in drop insertion at all five creation paths. Restrict both sidebar status readers through their existing shared reader to Orca's typed drag data; internal writers already provide it. Web omits the optional file-path bridge and path preparation reports unsupported. The owner hook checks the concrete filesystem namespace before using the bridge because the web wrapper otherwise invents fallback methods for missing properties.

There is no change to desktop OS-file destinations in this PR. Web feedback continues to accept screenshot attachments, unowned web file drops cannot navigate the browser away, and external plain text cannot impersonate a workspace-status drag. No notices were added.

Why

Keeping preparation separate from delivery lets the element under the cursor own its destination without losing screenshot copying. Adding another window-wide subscriber would retain the destination-guessing problem. The feedback fix belongs to its existing accepting handler, keeping web refusal elsewhere unchanged. A destination owns its sequence because a pane’s title and content, or an editor’s tab strip and group, represent the same destination even though their roots are siblings. Capturing a folder value during the event avoids reading a changed row after file preparation.

PR3 migrates chats, workspace composers, and feedback; PR4 migrates terminals and title bars; PR5 migrates explorer, sidebar, and editor groups; PR6 removes the legacy broadcast and markers.

Differences from the common pattern:

  • Intended: capture listeners and nearest-root arbitration protect nested owners from embedded editor handlers.
  • Intended: desktop paths and main-process screenshot copies preserve existing execution-host behavior; agents cannot open the temporary originals.
  • Intended: a cancellation-only document guard protects the web build from file navigation.
  • Temporary: preload release markers and the legacy broadcast remain until PR6.
  • Temporary: web path-based drops remain unsupported pending the separate file-byte attachment delivery follow-up.

Linked Issue

STA-6940 — PR 2 of 6; the full issue is not closed by this plumbing change.

Visual Proof

This PR should change nothing about how desktop drops behave. A separate QA agent ran it as a hidden Orca dev window (head 2e3f19a7b0d, isolated profile) and simulated OS file drops through the Chrome DevTools Protocol. Every existing drop path still works as before. The later namespace move and main merges do not touch desktop delivery.

1. File dropped on a terminal pane. The path is pasted on the prompt line, not run.

Terminal drop pastes the path

2. File dropped on a folder row in the file explorer. It is imported into that folder (inbox/notes2.txt).

Explorer import into a folder

3. File dropped on the editor content area. It opens as its own tab; its text is not inserted into the file already open, so Monaco's own drop-into-editor stayed off.

Editor area drop opens the file

4. File dropped on the editor tab strip. It opens as a tab.

Tab strip drop opens the file

5. Image dropped on the feedback dialog. It attaches to the feedback form.

Feedback dialog image drop

Known and unchanged here: a non-image file dropped on the feedback dialog still opens in the editor behind it; PR 3 (#25781) fixes that. Not covered: the web client in a real browser (DOM-level tests only), Linux, Windows, SSH and WSL hosts.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Maintenance validation on macOS with Node 26.5.0 and Vitest 5.0.3, after merging main as fetched for this update (35e173a5b745684e79e8c1f74107e0feb45d0aa6) into this branch at 83919811ffe568bc72c3c48811c197f0975406ea: 45 Vitest files passed, 367 tests passed. The original 44-file PR2 suite passed all 365 tests; the additional web API composition file passed both tests. All named files were checked for existence and the array was checked for a non-empty, exact 44-file list before the suite ran.

pnpm install --frozen-lockfile passed. Deleted both config/tsconfig.*.tsbuildinfo caches, then ran pnpm tc:node and pnpm tc:web one at a time; both passed. Full node_modules/.bin/oxlint on all 31 files changed from the merged main commit passed. pnpm run check:code-quality:changed passed with zero new findings in all six scans and the assertion rationale check. pnpm run check:react-doctor:changed passed with the same two existing editor complexity warnings. git diff --check and the staged whitespace check passed. The install and later pnpm commands added a local @pnpm/exe block; the lockfile was restored and has no difference from the merged main commit.

Main merged without conflicts or manual resolutions. This maintenance update adds no user-visible change and makes no feature edits.

CI attempt 1 on this head: full-unit run 37546923187 did not go green. Repository guards, static analysis and the full project typecheck, Linux and Windows packaging, relay integration, and unit shards 2/5, 3/5, and 4/5 passed. Shards 1/5 and 5/5 failed on issues inherited from the merged main snapshot:

  • Shard 1/5: config/scripts/vitest-sqlite-runtime-boundary.test.ts:70 found three database-backed tests missing from the list assigned to Node: orchestration-party-location.test.ts, runtime-orchestration-sender-names.test.ts, and structured-agent-session-mobile-restore.test.ts. Its test and runtime lists are unchanged from merged main. Main advanced while CI ran and fixed exactly these entries in #26010, commit faed899cd3088eb740adfa789d141fe0be97fd6e; that later commit is not in this head.
  • Shard 5/5: all 2,240 test files and 21,638 tests passed, but Vitest exited 1 for an uncaught EEXIST from src/main/skills/skill-bundle-extraction.ts:183. The originating test is skill-bundle-creation.test.ts, “rejects conflicting staging roots without changing their imported namespace.” The test, extraction, tar implementation, and CI runtime files are byte-identical to the merged main commit. This attribution is based on the CI error and unchanged sources; a separate clean-main run was not performed locally.

Neither failed job reports a runner shutdown, cancellation, or artifact download failure. No source patch or CI rerun was made for these main-side failures; the coordinator will handle a separate full rerun and any follow-up to main. Mobile Checks and PR test LoC passed; the Headless Node server workflow was skipped.

Final verification failed because the two unit shards failed; unit-selection evidence passed.

Final CI result for every full-unit workflow job
Job Result
detect changes and check repository guards success
static analysis and typecheck success
real IME skipped
mobile web app bundle skipped
real WSL terminal skipped
package success
package (windows) success
test / relay integration node 24 success
test / tests node 24 5/5 failure
test / tests node 24 2/5 success
test / tests node 24 3/5 success
test / tests node 24 1/5 failure
test / tests node 24 4/5 success
managed hooks on Node 18 skipped
Git compatibility skipped
shell contracts skipped
xterm patch sync skipped
Codex index-heal contract skipped
e2e skipped
orcad browser provider skipped
cross-version wire compatibility skipped
verify failure
unit_selection_evidence / selection_evidence success

No application or Electron window was launched. Real OS drops, rendered Electron behavior, browser drag enforcement, and Linux/Windows/SSH/WSL runtime flows were not verified locally. Packaging and the full repository suite were not run locally. No new user-visible strings were added, so localization checks were not rerun for this maintenance update.

Exact 44-file suite, run through Bash so the file array expands correctly:

bash -c '
  set -euo pipefail
  files=(
    src/main/window/native-file-drop-relay.test.ts
    src/main/window/dropped-path-preparation.test.ts
    src/main/window/dragged-temp-file-copy.test.ts
    src/main/window/attach-main-window-services.test.ts
    src/main/ipc/dropped-path-resolution.test.ts
    src/shared/native-file-drop.test.ts
    src/preload/preload-native-file-drop-release.test.ts
    src/preload/app-restart-checkpoint-routing.test.ts
    src/preload/pty-snapshot-capability-ipc.test.ts
    src/preload/ssh-authority-forwarding.test.ts
    src/preload/updater-package-recovery.test.ts
    src/renderer/src/hooks/use-os-file-drop-owner.test.tsx
    src/renderer/src/lib/os-file-drop-cancellation-guard.test.ts
    src/renderer/src/lib/os-file-drop-preload-integration.test.tsx
    src/renderer/src/hooks/composer-native-file-drop.test.ts
    src/renderer/src/hooks/composer-drop-result.test.ts
    src/renderer/src/hooks/composer-drop-owner.test.ts
    src/renderer/src/hooks/composer-drop-failure-toast.test.ts
    src/renderer/src/hooks/composer-state/composer-drop-listener.test.ts
    src/renderer/src/hooks/composer-state/attachment-drop-failure.test.tsx
    src/renderer/src/hooks/composer-state/attachment-drop-lifetime.test.tsx
    src/renderer/src/hooks/useGlobalFileDrop.test.ts
    src/renderer/src/hooks/useGlobalFileDrop.escaping-link.test.tsx
    src/renderer/src/components/automations/automation-editor-prompt-options.test.ts
    src/renderer/src/components/sidebar/workspace-status.test.ts
    src/renderer/src/components/sidebar/WorktreeList.status-lane-lineage-drop.test.tsx
    src/renderer/src/components/sidebar/sidebar-project-drop.test.ts
    src/renderer/src/components/sidebar/use-feedback-image-drop.test.tsx
    src/renderer/src/components/sidebar/use-workspace-status-drop.test.ts
    src/renderer/src/components/right-sidebar/useFileExplorerDragDrop.test.ts
    src/renderer/src/components/native-chat/native-chat-pane-file-drop.test.tsx
    src/renderer/src/components/native-chat/native-chat-composer-workspace-file-drop.test.tsx
    src/renderer/src/components/native-chat/native-chat-view-file-drop.test.tsx
    src/renderer/src/components/native-chat/native-chat-composer-drop-scope.test.tsx
    src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-file-drop.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-internal-handler.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-image-path.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-upload-report.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-shell.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-handler.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-runtime-owner.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-path-writer.test.ts
    src/renderer/src/components/terminal-pane/terminal-drop-write-failure.test.ts
    src/renderer/src/web/web-preload-api-filesystem.test.ts
  )
  (( ${#files[@]} == 44 ))
  for file in "${files[@]}"; do
    [[ -f "$file" ]] || { printf "Missing test file: %s\n" "$file" >&2; exit 1; }
  done
  ORCA_BACKGROUND_LAUNCH=1 node_modules/.bin/vitest run --config config/vitest.config.ts --maxWorkers=4 "${files[@]}"
'

Additional namespace regression:

ORCA_BACKGROUND_LAUNCH=1 node_modules/.bin/vitest run --config config/vitest.config.ts --maxWorkers=4 \
  src/renderer/src/web/web-preload-api-composition.test.ts

Review

Self-reviewed the preparation boundary, sender validation, global copy serialization, preserved legacy delivery, migration barriers, and guard interaction. Addressed all independent-review findings plus the synchronous destination snapshot without migrating any desktop surface.

Agent skill upstream boundary

  • Not applicable; no upstream skill code or resources copied.

Notes

Preparation remains local to the dropping desktop window and is not added to remote procedure calls. Existing downstream host uploads and path mapping remain in place. No active-pane or focused-runtime fallback was added. No documentation or lockfile changes are included.

Checklist

  • This PR is focused on the approved PR2 plumbing scope
  • I explained the problem, user-facing behavior, mechanism, and approach
  • Visual proof is N/A with the verification limitation stated
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, folder-workspace, and path impact considered
  • Targeted tests, project typechecks, lint, and localization checks pass; build and real-app checks remain unverified

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review summary

This PR went through an independent code review, a fix round, a second review of the fixes, a CI fix, and a rendered check in a real Orca window. The reviews were done by separate agents that did not write the code. Their reports are summarized here because they were kept off GitHub while the work was in progress.

First review (head 80f0623b0ab): changes requested, four items

  1. Web regression (serious). The new navigation guard runs last on the web client and overwrote the feedback dialog's "copy" drag effect with "none", so dragging a screenshot onto the feedback dialog in the web client would stop working. The guard's own tests did not install the real feedback handler, so they missed it.
  2. A partially unresolvable drop silently lost files. If some dropped items could not be turned into paths, the owner hook delivered the rest and reported nothing about the ones it dropped.
  3. Delivery order was per DOM element, not per destination. A terminal pane's title bar and body (or an editor's tab strip and content) are two elements for one destination, but each kept its own order, so a later drop on one could overtake an earlier one on the other.
  4. No synchronous snapshot of the drop target. The file explorer (PR 5) needs to read its target folder from the dropped-on row before any await, because rows can be recycled while a drop is being prepared.

The reviewer confirmed the rest: no surface is switched over yet, the preload release rule handles nested and legacy boundaries correctly, the main-process preparation shares one queue with the existing relay and keeps its limits, Monaco's own drop-into-editor is off at all five editor creation sites, and Orca's own workspace-card drags still move workspace status.

Fixes (head 2e3f19a7b0d)

  1. The feedback dialog's accepting drag handler now stops the event once it has accepted, so the guard leaves it alone; drops on empty web chrome are still refused silently. A test installs the real feedback handler and the guard together.
  2. Files that cannot be resolved are reported once, alongside the ones that succeed.
  3. One delivery sequence is shared by all elements of a destination; independent destinations still progress independently.
  4. A synchronous captureDestination step runs before any await.

Second review of the fixes: approved

All four items closed. Restoring the reviewed code made the six new tests fail for the intended reasons; with the fixes, the focused suites passed.

CI fix (head 7f5a7033a3f)

The path bridge was added as a lone top-level window.api.getPathForFile, which broke the web build's test that pins the exact set of window.api namespaces. It now lives in the filesystem namespace next to prepareDroppedPaths, and the web build simply does not provide it. Because the web build answers missing methods with stand-in functions, the owner hook checks that the namespace really has the key, with a test against the real stand-in wrapper.

Rendered check

A hidden Orca dev window on head 2e3f19a7b0d confirmed desktop drops behave exactly as before; screenshots are in the description. The later namespace move and main merges do not change desktop delivery.

Not verified

Real Linux and Windows machines, SSH and WSL hosts, and the web client in a real browser (covered by DOM-level tests only).

@brennanb2025
brennanb2025 marked this pull request as ready for review October 7, 2026 00:49
@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: df5f2f2e-a076-4b80-8d06-601727c67929
📥 Commits

Reviewing files that changed from the base of the PR and between 86d03ff and be26b1b.

📒 Files selected for processing (31)
  • src/main/window/dropped-path-preparation.test.ts
  • src/main/window/dropped-path-preparation.ts
  • src/main/window/native-file-drop-relay.test.ts
  • src/main/window/native-file-drop-relay.ts
  • src/preload/api/filesystem-api.ts
  • src/preload/api/fs-bridge.ts
  • src/preload/app-restart-checkpoint-routing.test.ts
  • src/preload/preload-native-file-drop-release.test.ts
  • src/preload/preload-runtime-support.ts
  • src/renderer/src/components/automations/automation-editor-prompt-options.test.ts
  • src/renderer/src/components/automations/automation-editor-prompt-options.ts
  • src/renderer/src/components/editor/DiffSectionBody.tsx
  • src/renderer/src/components/editor/DiffViewer.tsx
  • src/renderer/src/components/editor/IpynbCellEditor.tsx
  • src/renderer/src/components/editor/MonacoEditor.tsx
  • src/renderer/src/components/sidebar/WorktreeList.status-lane-lineage-drop.test.tsx
  • src/renderer/src/components/sidebar/use-feedback-image-drop.test.tsx
  • src/renderer/src/components/sidebar/use-feedback-image-drop.ts
  • src/renderer/src/components/sidebar/workspace-status-drag-data.ts
  • src/renderer/src/components/sidebar/workspace-status.test.ts
  • src/renderer/src/hooks/use-os-file-drop-owner.test.tsx
  • src/renderer/src/hooks/use-os-file-drop-owner.ts
  • src/renderer/src/lib/os-file-drop-cancellation-guard.test.ts
  • src/renderer/src/lib/os-file-drop-cancellation-guard.ts
  • src/renderer/src/lib/os-file-drop-preload-integration.test.tsx
  • src/renderer/src/main.tsx
  • src/renderer/src/popout.tsx
  • src/renderer/src/web/main.tsx
  • src/renderer/src/web/preload-api/web-filesystem-api.ts
  • src/renderer/src/web/web-preload-api-filesystem.test.ts
  • src/shared/native-file-drop-preparation.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

The change adds a shared main-process queue for preparing dropped paths, exposes it through the filesystem APIs, and adds renderer-owned drop handling. Preload and document-level handlers coordinate native file drops with renderer owners and legacy targets. The change also disables Monaco drop-into-editor behavior and narrows workspace drag recognition to typed payloads.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to be26b

No issue identified here needs to block merging; real OS-drop behavior on the reported untested platforms remains unverified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.04% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 46 functions across 31 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the file-drop preparation and ownership work, and includes the related issue and PR sequence.
Description check ✅ Passed The description covers the change, rationale, linked issue, visual proof, testing, review, scope, and checklist. It also states what remains unverified.
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@brennanb2025
brennanb2025 merged commit 5b1bb78 into main Oct 7, 2026
56 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/sta-6940-pr2-drop-plumbing branch October 8, 2026 02:03
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.

1 participant