refactor(automations): one save state for the automation page

Merged
SA
2mo ago
+695-538

Follows #6709. The automation detail page had two save habits: the trigger set waited for a Save while the title, scope chips, agent and prompt each wrote the moment they changed. The trigger set is the one that cannot autosave — a row is invalid the instant it's added and the API rejects the whole set — so the rest of the page moved onto its habit rather than the other way round.

What changed

One draft for the page. useAutomationDraft replaces useTriggerDrafts: a single dirty flag, one adopt-when-clean, one Save. TriggersEditor is now presentational; useProviderOptions moved up to the page, which is what lets the Slack refetch happen where the save is instead of through a counter-and-effect workaround.

Save and Discard sit on the title line, appearing only when something is dirty.

Two deliberate exclusions. The Active switch still writes immediately — it's the kill switch, and a pause that waits for a second click is a pause that didn't happen. The prompt joins the draft but keeps its versioning (below).

The save is now atomic. It was two calls — automation.update, then automation.setPrompt — so an update that landed followed by a failed setPrompt left a half-saved automation. update now accepts a prompt and records its version inside the same transaction, only when it changed, so a scope tweak doesn't mint an identical version. setPrompt stays for the CLI, MCP and SDK callers.

The prompt editor no longer double-saves. MarkdownEditor fires onSave on blur, so clicking Save blurred the editor and saved twice — once from the blur, once from the button. The prompt is in the draft on every keystroke, so the blur had nothing left to commit.

Switching project or device drops the workspace pin. A pin belongs to one project on one host; carrying it across a switch left a trigger that couldn't run and a picker showing two near-identical main rows — the stale pin in amber next to the real workspace. It now falls back to "New workspace", which is always valid. Re-picking the same value leaves a good pin alone.

Fixes found while testing this branch

The first cut converted the writes to a draft and left the reads on the saved row, so the page ran half-edited: picking a workspace wrote the saved project back and snapped the project chip, a switched device still listed the old host's workspaces, file search was scoped to the old project, and the agent warning judged the saved agent. All four now read the draft.

The title also only entered the draft on blur, which raced the click on Save and could drop a rename silently. It updates per keystroke now, trimming on blur.

Verification

Driven against the running app over CDP, with the database checked after each step:

StepResult
Edit a scope chipSave/Discard appear; DB unchanged
SaveExactly one request, carrying name, scope, tags, agent, prompt and the trigger set
Edit title + prompt + tag, then SaveAll three persisted from a single call
Save with a changed promptPrompt persisted, version count 2 → 3, name untouched
Save with an unchanged promptNo version minted
Save refused by the serverEdits survive, page stays dirty
DiscardEverything reverts in one action
Switch projectPin clears to "New workspace", no amber row
Re-pick the same projectPin survives
Invalid trigger + SaveBanner and amber chip; nothing written

Suites: desktop 3286, trpc 125, shared 920, api 21. Typecheck 39/39, lint clean.

Not here

The useProviderOptions finding from #6709 (a provider outage renders as "nothing to choose yet"), the EmojiNameChip minor, and the 129 dangling workspace pins in prod (56 orgs, 91 enabled, 5.4% dispatch-failure rate against a 4.3% baseline) — this stops new ones but doesn't clean those up.

https://claude.ai/code/session_01JZADY3P3hZ7PitayKpGKLg


Summary by cubic

Unifies the automation detail page onto one draft and a single Save button. The trigger set already required an explicit save — a row is invalid the instant it's added — and now the title, scope, agent, and prompt save the same way instead of writing on every change; the Active switch still writes immediately as the kill switch.

Bug Fixes

  • Save no longer fires twice when the prompt editor has focus: the blur handler and the button previously each committed.
  • The title enters the draft on every keystroke, so a blur that raced the Save click could no longer drop a rename.
  • Switching project or device now clears the workspace pin to "New workspace" instead of carrying a pin that belongs to the old project or host.
  • Page reads now come from the draft, so a switched device lists the new host's workspaces and the agent warning judges the draft agent.

Migration

  • automation.update now accepts an optional prompt and records a version in the same transaction, only when the prompt changed; setPrompt stays for CLI, MCP, and SDK callers.

Summary by CodeRabbit

  • New Features

    • Automation editing now uses a unified draft for names, prompts, agents, targets, tags, workspaces, projects, and triggers.
    • Added Save and Discard controls for unsaved changes, with saving and confirmation feedback.
    • Added warnings and validation feedback for trigger configuration problems.
    • Automation prompts are now saved and versioned with other automation updates.
  • Bug Fixes

    • Automation changes are saved atomically, preventing partial updates.
    • Trigger and scope changes now remain together until explicitly saved or discarded.
    • Updated translations support the new automation editing workflow across supported languages.