feat(shared,trpc): save triggers as a set with the automation
Replaces the per-trigger CRUD approach in #6531 (now draft). Triggers are edited as part of the automation, so the surface is a replace-the-set on automation.create/update.
No migration.
Shared validation
Lives in @superset/shared/automation-triggers, imported by both the editor and the API, so the form blocks Save on exactly what the server would reject rather than reimplementing the rules.
It deliberately has two levels:
draftTriggerSchemaaccepts a half-configured trigger. The editor has to be able to hold "Draft opened in [Select Repos]" with nothing chosen yet — if the schema refused to represent that, the form couldn't hold its own in-progress state.describeTriggerProblemsreports what blocks Save, per trigger and per field, so a message can sit next to the control it belongs to.summarizeTriggerProblemsproduces the banner.
The messages match the mockups: "Some triggers need additional configuration" and "Specify at least one repository."
It also catches more than one schedule per automation, which the partial unique index enforces in the database — better as a message next to the row than a constraint violation at save time.
Matched by id, not cleared and reinserted
A trigger row carries state the editor never sees: a webhook's signing key, and a schedule's next run. Clearing and reinserting would silently roll a key someone had already configured upstream, and reschedule an automation that was only renamed.
So rows present in the set are updated in place, rows absent are deleted, and the next run is only recomputed when the recurrence actually changed.
GitHub configs are a union on the event
The filters differ per event — "Any Comment by [Anyone] on a PR by [Anyone] in [Repos]" carries two independent actor scopes where "Draft opened" carries one. A single flat config with one actor can't express that, so the config discriminates on event.
includeForks stays the literal false: fork payloads carry attacker-controlled content into a checkout the agent runs in, so enabling it should be a schema change with a threat model attached.
Verified against a real database
The actual saveTriggerSet executed against Postgres 16, not reasoned about:
| Initial save creates the set | 2 triggers |
| Webhook signing key survives an unrelated save | preserved |
| Schedule not rescheduled by an unrelated save | preserved |
| Row ids stable across saves | stable |
| Trigger dropped from the set is deleted | deleted |
| Recurrence change does reschedule | reschedules |
| Unconfigured GitHub trigger rejected server-side | rejected |
Plus the shared validation itself: a half-configured trigger parses but reports a problem; a comment event requires both actors; includeForks: true, unknown events, and an unknown kind are all rejected.
Compatibility
Top-level rrule still works and folds into a single schedule trigger, so the CLI, SDK, MCP and desktop keep working until they move to sending a set. Same expand-contract shape as the column migration.
Typecheck, lint and the full suite pass.
Next
The editor UI, then webhook ingestion so event triggers actually fire.
Summary by cubic
Saves automation triggers as a replace-the-set on create/update and makes event-only automations first‑class. Previously list/get assumed every automation had a schedule; now schedule is optional and responses reflect what was actually saved.
- API:
createAutomation/updateAutomationaccept optionaltriggers: DraftTrigger[](max 25). Create requires eithertriggersorrrule; ifrruleis provided,timezoneis required. Iftriggersare absent, the legacy top‑levelrrulefolds into a single schedule trigger. Invalid sets return 400 with per‑field problems. - Schedule handling: list/get/getAutomationForUser use a left join; schedule fields in responses are nullable. Create/update now build schedule fields from saved triggers, not the input.
- Validation (
@superset/shared/automation-triggers): shared draft+save checks; enforces at most one schedule; GitHub config discriminates onevent; Slackreaction_addedrequires an emoji; actor isanyone|me| specific people. - Persistence: new
saveTriggerSetmatches rows by id, deletes dropped rows, and only recomputesnextRunAtwhen the schedule’s recurrence changes. Webhook signing keys and existingnextRunAtare preserved across unrelated edits. - Packages:
packages/sharedexports./automation-triggers. No migration.
Summary by CodeRabbit
- New Features
- Added support for configuring up to 25 automation triggers, including schedules and event-driven integrations.
- Automation creation and updates now save, update, and remove trigger configurations reliably.
- Event-only automations can be created without a schedule.
- Existing schedule configurations remain supported for compatibility.
- Added validation for trigger requirements and provider-specific settings, with clear summaries of configuration issues.