Skip to content

feat(desktop-gpui): run saved automation rules - #4059

Open
richiemcilroy wants to merge 9 commits into
mainfrom
feat/gpui-run-automations
Open

richiemcilroy wants to merge 9 commits into
mainfrom
feat/gpui-run-automations

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

What this does

The desktop app saves automation rules in Settings > Automations but never ran them. The old Tauri app did, so anyone with a saved rule (for example compress-then-delete) silently lost it when they updated. This PR runs those rules again in the GPUI app, using the same engine the CLI already uses (crates/automation), so there is still one rule model and one runner.

Existing users: rules they already saved start running after this update. Disabled rules stay disabled (the engine skips enabled: false, and a test reads rules written by the settings page through the runner to prove it).

Where it hooks in

Trigger GPUI point
Recording started RecordingSession::activate, once the capture is live
Studio recording finished Stop completion, after Studio finalization (remux, preset, thumbnail kick-off) succeeded
Instant recording finished Stop completion, where the share link is opened and copied
Upload completed Upload queue, right after the upload is verified and marked complete, before the optional local cleanup
Video imported End of run_video_import, after the bundle and thumbnail are written
Recording deleted upload::queue::delete_recording (every user delete: library, settings, editor)
Screenshot taken Screenshot capture completion, before the editor decision

SkipEditor / OpenEditor override the post-recording editor choice at stop time and the screenshot editor choice, the same way 0.6.0 did.

How it runs

  • Every hook does one cheap check first: read the cached store's automations key and look for an enabled rule with that trigger. With no rules saved, nothing else happens, so record, stop, finalize, export and upload behave as on main.
  • Matching rules run on the tokio runtime, never on the UI thread. Context that needs disk reads (Studio duration for duration conditions, share links) is built in spawn_blocking. Actions that need the UI (clipboard, open editor, reveal, open URL, library refresh) are sent to a main-thread channel and awaited.
  • Each event runs once: a small (trigger, path) ledger refuses a second run for the same event.
  • Failures are logged per action (automation action did not complete, with rule id and reason), as 0.6.0 did. Nothing panics or blocks.

Guards on destructive actions

DeleteLocalFiles now runs only when all of these hold:

  1. The engine's existing upload checks pass (no pending, failed or unverifiable upload in the run). As in 0.6.0, the desktop host does not claim remote verification, so delete after an Upload action is still refused.
  2. New AutomationHost::verify_before_delete hook (default allows, so the CLI is unchanged): the GPUI host requires the action right before the delete to be an Export or Save to location that succeeded, and its output file to exist, be non-empty and sit outside the folder being deleted. An export to the project folder therefore never lets the recording be deleted.
  3. The recording is not being recorded or finalized (RecordingInProgress, fix(desktop-gpui): refuse deleting the recording in progress #3969) and not open in an editor (ProjectBeingDeleted / ProjectOpenInEditor, fix(desktop-gpui): delete a recording as a unit, closing the editor that has it open #4001). The delete itself goes through the same delete_recording path as the library, so it is also limited to the recordings folders and cancels nothing silently.
  4. The recording has no instant upload still in flight.
  5. Screenshots are deleted on the main thread after checking no screenshot editor has the bundle open, so an editor cannot open in between.

A delete action that is refused leaves every file in place and logs why.

Not covered

  • Notify and Recognize text are not available in the GPUI app (it has no native notifications or OCR). Those actions are skipped with a logged reason; "Check compatibility" in Settings now says so for rules that use them.
  • Copy to clipboard (rendered) on a fresh screenshot needs the screenshot editor's renderer; it returns an error asking for the original capture instead of copying the wrong image.
  • A standalone delete rule, or a delete after Run command / Webhook, no longer deletes: there is no output to verify. This is the stricter guard asked for.
  • Imports appended into an open editor project (0.6.0 also fired "video imported" for those) are not a GPUI flow.
  • Two of the built-in templates (text from screenshots, upload notification) use those unsupported actions; Check compatibility says so.

Settings copy

The page no longer says automations do not run, in all seven locales. The command warning drops "In the Cap CLI" because the app runs those commands now.

Proof

Real app on macOS (Apple Silicon), debug build of this branch, sandboxed data folder, real 5 second Studio recordings started by the app's auto-record hook. Media committed at bee9626fe on scratch/automations-proof. The saved store had three rules: "Compress then delete" (Studio finished: Export MP4, Potato, 720p, then Delete local files), a disabled rule that would touch a marker file, and "On start" (Recording started: run a command).

1. Rule fires, delete refused because the editor has the recording open. Export wrote a 507 KB, 4.4 s H.264 file; the recording is still in the library and open in the editor; the disabled rule's marker was never created; the start rule wrote its file.

Editor open on the kept recording
Log: export then refused delete

2. Safe compress-then-delete. With Skip editor added, the export lands outside the recording and is checked (exists, non-empty), then the recording is removed through the library delete path. The editor never opened, so Skip editor overrides the "open editor" setting.

Log: export then delete

3. Delete refused because the export is inside the recording. Export to the project folder succeeds, but the delete is refused and the recording is kept.

Log: export inside folder, refused delete

Settings copy with the saved rules (the disabled one shows as Disabled):

Settings automations page

The editor preview is blurred because it shows the test machine's desktop. Windows and Linux: covered by the GPUI test suite in CI (the actions use the same cross-platform code as before; Run command uses cmd /C on Windows as 0.6.0 did).

Tests

  • crates/automation: the new hook sees the action before the delete and whether it succeeded; a refusal keeps the files and reports the reason; the hook runs only after the upload checks pass. cap-automation tests now run in CI.
  • apps/desktop-gpui/src/automation/tests.rs: rules written by the settings page keep their enabled state through the runner; no rules means nothing listens; one event runs once; SkipEditor/OpenEditor/duration overrides; delete refused with no prior action, a failed or non-file action, a missing, empty or in-folder output; delete refused for a recording in progress, open in an editor, with an unfinished upload, or outside the library; a failed export keeps the recording end to end; Save to location, export paths and compression, clipboard source, templates, environment, Run command, Webhook (local server, 200 and 500), Apply preset, and the two unsupported actions.

RetriggerView in GreptileConfidence Score: 0/5

This PR is not ready to merge because automatic sharing, upload events, editor behavior, and repeated screenshots still have unresolved failures.

Findings

  1. P1 Automatic Studio sharing fails ▶
  2. P1 Studio uploads skip saved actions ▶
  3. P1 Cleanup races saved actions ▶
  4. P1 Editor overwrites the applied preset ▶
  5. P1 Skip rules still open editors ▶
  6. P1 Screenshot uploads ignore the team ▶
  7. P1 Later screenshots skip saved actions ▶
  8. P2 Field comment repeats the code ▶
Fix with agent prompt
### Issue 1
apps/desktop-gpui/src/automation.rs:593-599
The shipped `upload-share` template runs only `Upload`, but this call requires an existing rendered video. Studio finalization does not create one, so a fresh recording fails with “Rendered video not found” instead of producing a share link. Export the recording before uploading when no rendered file exists.

### Issue 2
apps/desktop-gpui/src/upload/queue.rs:undefined-858
`UploadCompleted` rules run only for uploads handled by the Instant queue. Studio editor uploads call `upload_exported_video` directly and finish without reaching this hook. A saved webhook or other supported action on `UploadCompleted` therefore never runs after a Studio share. Fire the event from the successful Studio upload path too, without duplicating events for queued uploads.

### Issue 3
apps/desktop-gpui/src/upload/queue.rs:858-860
This call starts `UploadCompleted` actions without waiting for them, then the queue can immediately remove the recording folder. With `deleteInstantRecordingsAfterUpload` enabled, a command that uses `CAP_PROJECT_PATH` to copy or inspect the recording can find its input missing. Wait for the matching actions to finish before optional cleanup.

### Issue 4
apps/desktop-gpui/src/automation.rs:772-775
`ApplyPreset` changes only the file on disk, even when the post-recording editor has already loaded the project. For example, a `StudioRecordingFinished` rule that runs a command before applying a preset lets the editor load the old settings first. The preset then stays invisible, and the next editor save overwrites it. Apply the preset through the open editor, or finish these actions before the editor loads the project.

### Issue 5
apps/desktop-gpui/src/automation.rs:744-752
When `SkipEditor` and `OpenEditor` rules both match a Studio recording, `studio_editor_behaviour_for` correctly chooses `DoNothing`. But the runner later executes `OpenEditor` through this method anyway, so the editor still opens. Screenshots have the same problem.

Honor the matching `SkipEditor` choice when running `OpenEditor` for these events.

### Issue 6
apps/desktop-gpui/src/automation.rs:586-587
When a saved screenshot `Upload` action selects an `organization_id`, this branch calls `upload_screenshot` without passing it. The upload helper then sends no organization to `create_or_get_video`, so the screenshot uploads without selecting the team the user chose. Pass the saved organization through the screenshot upload path.

### Issue 7
apps/desktop-gpui/src/automation.rs:166-170
`FiredEvents::claim` treats a path as an event ID, but screenshot paths can be reused. When a `ScreenshotTaken` rule saves the image elsewhere and deletes the bundle, another capture of the same target within the same minute gets the same path. The runner then skips every saved action because it still remembers the first capture.

Use a capture-specific ID, or limit duplicate tracking to the lifetime of each event.

### Issue 8
apps/desktop-gpui/src/session.rs:438-439
The new `studio_behaviour` comment repeats what the field stores. `AGENTS.md` requires comments to explain non-obvious context, not restate fields or describe the change. Remove this comment or explain an ordering constraint that readers cannot see from the code. This repository requirement must be satisfied before merging.

```suggestion

```

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR connects saved automation rules to the GPUI desktop app.

  • Cap runs enabled automation rules when recording and media events happen.
  • Matching rules can change which editor opens after a capture.
  • Automated deletion waits for a successful action and a checked output.
  • Automation settings explain which saved actions run in Cap.

Reviews (6) · Last reviewed commit: "feat(desktop-gpui): update automations s..." · Reviewed by Greptile

@sandchest-com

sandchest-com Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Sandchest

Sandchest reviewed 42f3773: nothing exploitable found in this change.

Sandchest reviews every change for vulnerabilities an attacker could use · 13 credits · Review details

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Capy found 4 potential issues (1 high, 3 medium).

Open in Capy

Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/upload/queue.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/upload/queue.rs Resolved
Comment thread apps/desktop-gpui/src/upload/queue.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/automation.rs Resolved
Comment thread apps/desktop-gpui/src/session.rs Resolved
@vercel

vercel Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
cap-status Ignored Ignored Preview Nov 14, 2026 7:22am UTC

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Capy found no issues.

View 2 other findings in Capy.

Open in Capy

This branch was successfully deployed

1 active deployment
Preview – cap-web — 42f37733 Deployed Nov 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants