@checkstack/automation-frontend 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +664 -0
  2. package/package.json +38 -0
  3. package/src/components/AutomationMenuItems.tsx +37 -0
  4. package/src/editor/ActionEditor.tsx +367 -0
  5. package/src/editor/ActionListEditor.tsx +203 -0
  6. package/src/editor/AddActionDialog.tsx +225 -0
  7. package/src/editor/AutomationDefinitionContext.tsx +37 -0
  8. package/src/editor/AutomationDefinitionEditor.tsx +99 -0
  9. package/src/editor/ConditionEditor.tsx +218 -0
  10. package/src/editor/ConditionsEditor.tsx +89 -0
  11. package/src/editor/ItemPicker.tsx +147 -0
  12. package/src/editor/TriggersEditor.tsx +269 -0
  13. package/src/editor/action-composite-cards.tsx +390 -0
  14. package/src/editor/action-helpers.ts +365 -0
  15. package/src/editor/action-leaf-cards.tsx +426 -0
  16. package/src/editor/editor-validation.test.ts +95 -0
  17. package/src/editor/editor-validation.tsx +200 -0
  18. package/src/editor/registry-context.tsx +192 -0
  19. package/src/editor/template-completion.test.ts +412 -0
  20. package/src/editor/template-completion.ts +664 -0
  21. package/src/editor/template-helpers.test.ts +145 -0
  22. package/src/editor/template-helpers.ts +95 -0
  23. package/src/editor/trigger-helpers.test.ts +58 -0
  24. package/src/editor/trigger-helpers.ts +67 -0
  25. package/src/editor/useConnectionOptionResolvers.ts +80 -0
  26. package/src/editor/yaml-markers.ts +116 -0
  27. package/src/index.tsx +95 -0
  28. package/src/pages/AutomationEditPage.tsx +567 -0
  29. package/src/pages/AutomationListPage.tsx +304 -0
  30. package/src/pages/RunDetailPage.tsx +333 -0
  31. package/src/pages/RunsPage.tsx +233 -0
  32. package/src/pages/TemplatePlaygroundPage.tsx +224 -0
  33. package/src/script-context.test.ts +247 -0
  34. package/src/script-context.ts +218 -0
  35. package/tsconfig.json +29 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,664 @@
1
+ # @checkstack/automation-frontend
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e2d6f25: feat(automation): connection picker for integration actions + restore Integrations menu
8
+
9
+ Connection-backed automation actions (Jira, Teams, Webex) now render a
10
+ working connection picker plus cascading provider dropdowns in the
11
+ visual editor, and the Integrations entry is back in the user menu.
12
+
13
+ **Contract.** `ActionDefinition` gained an optional
14
+ `connectionProviderId` (and it is surfaced on `ActionInfoSchema` and
15
+ mapped in the `listActions` router). It carries the integration
16
+ provider's fully-qualified id, derived from the provider plugin's own
17
+ `pluginMetadata.pluginId` (never a hardcoded string), so the editor
18
+ knows which provider backs an action's dropdowns and it matches the
19
+ `qualifiedId` the integration provider registry assigns.
20
+
21
+ **Providers.** Jira, Teams and Webex each export
22
+ `*_PROVIDER_LOCAL_ID` / `*_PROVIDER_QUALIFIED_ID`, register their
23
+ provider with the local id, and add a `CONNECTION_OPTIONS`
24
+ (`"connectionOptions"`) resolver name. Their `post_message` /
25
+ issue actions set `connectionProviderId` and expose `connectionId`
26
+ as an `x-options-resolver` dropdown instead of a hidden field.
27
+
28
+ **Frontend bridge.** A new `useConnectionOptionResolvers` hook
29
+ (`@checkstack/automation-frontend`, which now depends on
30
+ `@checkstack/integration-common`) turns an action's
31
+ `x-options-resolver` schema fields into live data: the
32
+ `connectionOptions` resolver lists the provider's connections via
33
+ `listConnections`, and every other resolver name is forwarded to
34
+ `getConnectionOptions` for the selected `connectionId`, passing the
35
+ live form values as `context` for dependent fields. `ProviderActionBody`
36
+ now passes this map to `DynamicForm` (it was previously missing
37
+ entirely, so connection-backed actions had no working dropdowns).
38
+
39
+ **frontend-api.** `usePluginClient` procedures now also expose a typed
40
+ imperative `.call(input)` alongside `.useQuery` / `.useMutation`, for
41
+ async callbacks that cannot host a hook (such as a `DynamicForm`
42
+ options resolver). Additive, non-breaking.
43
+
44
+ **Integrations menu.** Re-added `IntegrationMenuItem` and a new
45
+ `IntegrationsLandingPage`, wired into `integration-frontend` as a list
46
+ route and a `UserMenuItemsSlot` entry under the "Configuration" group.
47
+
48
+ **Action card polish.** The action editor's secondary metadata (id,
49
+ description, failure behaviour) is now grouped into one quiet settings
50
+ panel with consistent small uppercase "eyebrow" labels, so the action's
51
+ own configuration stays the focal point. The raw failure checkbox was
52
+ replaced with the standard `Checkbox` control, and the provider action
53
+ picker / configuration sections gained consistent section headers and a
54
+ divider. The per-step "type" dropdown was removed: an action's kind is
55
+ fixed at creation, so changing it now means adding a new step and
56
+ deleting the old one (avoids the surprising full-config reset that
57
+ switching kinds used to trigger).
58
+
59
+ **Add-step picker.** Adding a step now opens a Home-Assistant-style
60
+ dialog where the operator decides the step type up front: an "Actions"
61
+ tab lists the registered provider actions grouped by category
62
+ (searchable; picking one presets the step's `action`), and a "Blocks"
63
+ tab lists the structural building blocks (choose / parallel / repeat /
64
+ etc.). Because the concrete action is chosen here, the in-card action
65
+ switcher was removed - a step's action is fixed once created. Composite
66
+ blocks now start with an empty child list (filled via the nested
67
+ add-step picker) instead of seeding an unconfigurable empty action.
68
+
69
+ - 41c77f4: feat(automation): deep + live definition validation surfaces invalid values, keys and ids — marked inline
70
+
71
+ Previously `validateDefinition` only checked the structural shape via
72
+ `AutomationDefinitionSchema`, where an action's `config` is typed as
73
+ `z.record(z.unknown())`. So a bad config value (e.g. `level:
74
+ debugthisiswrong` on `automation.log`) passed validation, and switching
75
+ to the visual editor just showed an empty dropdown with no explanation.
76
+
77
+ **Backend — deep validation.** New `collectDefinitionIssues` walker
78
+ validates the whole definition semantically, not just structurally:
79
+
80
+ - unknown trigger `event` / action `action` ids,
81
+ - each provider action's `config` against the registered action's own
82
+ schema (wrong enum value, missing required field, wrong type),
83
+ - each trigger's `config` against the trigger's `configSchema`,
84
+ - **unknown / typo'd config keys** — object configs are validated in
85
+ strict mode, so `levle: "info"` is reported rather than silently
86
+ stripped,
87
+ - recurses through `choose` / `parallel` / `repeat` / `sequence` so
88
+ nested action configs are covered too.
89
+
90
+ Issues come back with a dot-joinable `path` (e.g.
91
+ `actions.0.config.level`, `triggers.1.event`). The `validateDefinition`
92
+ RPC now returns these.
93
+
94
+ **Frontend — live + inline.** The automation editor re-validates on
95
+ every edit (debounced ~400ms) in BOTH tabs, and marks the offending
96
+ content in place rather than in a separate alert panel:
97
+
98
+ - **YAML tab** — issues (and YAML syntax errors) are squiggled at the
99
+ exact node. `@checkstack/ui`'s `CodeEditor` gained a `markers` prop;
100
+ the editor maps each issue's `path` onto the YAML document's node
101
+ range via a new `computeYamlMarkers` helper (walking up to the
102
+ nearest existing ancestor when a key is absent, e.g. a missing
103
+ required field).
104
+ - **Visual tab** — the specific card carrying an issue is marked: a
105
+ destructive border + warning icon + the field-level messages. A
106
+ `ValidationProvider` context partitions issues by owner (action card
107
+ / trigger card / condition / top-level) using the action-node path
108
+ grammar, so a nested action's config error attaches to the nested
109
+ card, and a `choose`'s own `when` error attaches to the choose card.
110
+ `ActionCard` gained an `errors` prop. So importing YAML with a bad
111
+ value (the empty-dropdown case) now visibly flags the card instead of
112
+ being silent.
113
+
114
+ The big error alert is gone; the only residual panel is a slim fallback
115
+ for the rare top-level issue that can't attach to any card.
116
+
117
+ Note: strict config validation means an action whose config schema
118
+ intentionally allowed extra keys would now flag them; action configs
119
+ across the platform declare all their fields, so this only catches
120
+ genuine typos.
121
+
122
+ - 41c77f4: fix(automation): editor UI fixes — action-config autocomplete, popup edge clamping + scroll, de-misleading action icon
123
+
124
+ Four fixes to the automation editor's visual mode:
125
+
126
+ - **Template autocomplete on action config fields.** A provider
127
+ action's config form (e.g. `automation.log`'s `message`) rendered
128
+ plain string fields with no `{{ … }}` autocomplete — only the
129
+ condition/expression fields had it. `DynamicForm` gains a
130
+ `templateCompletionProvider` prop; when supplied, default single-line
131
+ string fields render a `TemplateValueInput` wired to it instead of a
132
+ bare `Input`. The automation editor passes the staged template-mode
133
+ provider, so config fields now get the same field / comparator / value
134
+ / filter completion as conditions. Other `DynamicForm` consumers are
135
+ unaffected (the prop is opt-in; without it string fields stay plain).
136
+
137
+ - **Autocomplete popup no longer overflows the window.** The popup is
138
+ now edge-aware: it flips above the input when there isn't room below,
139
+ anchors to the input's right edge when a left-anchored popup would
140
+ spill past the right edge, and caps its height to the available space
141
+ (the list scrolls within it). The placement decision is extracted into
142
+ a pure, unit-tested `computePopupPlacement` helper.
143
+
144
+ - **Keyboard navigation scrolls the popup.** Arrowing through a list
145
+ taller than the popup now scrolls the highlighted row into view
146
+ (`scrollIntoView({ block: "nearest" })`) instead of leaving the
147
+ selection off-screen.
148
+
149
+ - **Action card icon no longer looks like a run button.** The "action"
150
+ kind used a `Play` triangle, which reads as a test/run control but
151
+ actually sits inside the card's expand toggle (so clicking it just
152
+ collapsed the card). Swapped to `Zap`, the conventional
153
+ automation-action glyph, which carries no "click to run" affordance.
154
+
155
+ - **Inline-script actions get their typed runtime context.** The Monaco
156
+ editor for `Run Script (TypeScript)` was falling back to an untyped
157
+ default context because the editor never received type definitions.
158
+ `useVariableScope` now also returns the `declare const context: …`
159
+ declarations from `generateAutomationContextTypes` (already built, but
160
+ never wired), and the provider action body forwards them to
161
+ `DynamicForm` so `context.trigger.payload` is typed as the discriminated
162
+ union over the automation's subscribed triggers, with
163
+ `context.artifacts` / `context.var` / `context.repeat` in scope at the
164
+ action's position. Shell scripts get their context the same way every
165
+ other config string does: `{{ … }}` templates are expanded by the
166
+ dispatch engine (`renderValue`) before the script runs, with the same
167
+ field autocomplete as other template fields.
168
+
169
+ - e1a2077: feat(automation): reference artifacts by explicit action id (`artifacts.<id>.<name>`)
170
+
171
+ Multiple actions of the same type (e.g. two "create Jira issue" steps) used
172
+ to collide: both produced the artifact type `integration-jira.issue`, so a
173
+ template could only ever reach "the most recent one of that type". Artifacts
174
+ are now addressed by the producing action's instance `id` instead.
175
+
176
+ - Templates reference a produced artifact solely as
177
+ `{{ artifacts.<actionId>.<localArtifactName>.<field> }}`, e.g.
178
+ `{{ artifacts.open_jira.issue.issueKey }}`. The local artifact name is the
179
+ producing action's `produces` id with the owning plugin prefix stripped
180
+ (`integration-jira.issue` -> `issue`).
181
+ - `@checkstack/automation-backend`: the dispatch engine nests each produced
182
+ artifact under `artifacts[actionId][localName]` in the template scope and
183
+ records the `actionId` on the artifact row. `validate-definition` now
184
+ enforces that action ids are unique within an automation and that every
185
+ artifact-producing action carries an id.
186
+ - `@checkstack/automation-common`: action `id` is constrained to an
187
+ identifier (`/^[a-zA-Z_][a-zA-Z0-9_]*$/`) so it is always usable as a
188
+ plain template segment. The variable-scope resolver surfaces
189
+ `artifacts.<id>.<name>` (with full field completion) in the editor.
190
+ - `@checkstack/automation-frontend`: the action editor now has editable `Id`
191
+ and `Description` inputs (previously settable only via the YAML view), and
192
+ new steps get an auto-assigned, unique, log-friendly default id that the
193
+ operator can rename. Action ids are recorded on every run step, so run
194
+ logs are parseable by id regardless of kind.
195
+
196
+ **BREAKING (beta):** the previous flat, type-keyed scope form
197
+ `{{ artifacts["integration-jira.issue"] }}` is removed. Reference artifacts
198
+ by the producing action's id instead. Action ids may no longer contain
199
+ hyphens or dots (identifier characters only). Artifacts are per-run and
200
+ ephemeral, so no stored-data migration is needed.
201
+
202
+ - 41c77f4: feat(automation): native per-editor context for script actions (typed `context` for TS, `$ENV` for shell)
203
+
204
+ Script action editors had a confusing dual system: the TypeScript editor
205
+ type-checked `{{ }}` template text as code (so `{{ artifact.x }}` errored
206
+ with "Cannot find name"), and the runtime never actually populated the
207
+ `context` object. This standardises on a single, native context-access
208
+ mechanism per editor kind.
209
+
210
+ **Run scope reaches actions.** `ActionExecutionContext` gains a `scope`
211
+ (`{ trigger, artifacts, vars, repeat? }`), populated by the dispatch
212
+ engine from the same scope it already uses for `{{ }}` rendering. Actions
213
+ that need broad context (the script actions) read from it instead of
214
+ having to declare every artifact type in `consumes`. Additive and
215
+ optional, so existing actions are unaffected.
216
+
217
+ **TypeScript / JavaScript → typed `context`.** `run_script` now builds
218
+ `context` from the run scope, so `context.trigger.payload`,
219
+ `context.artifacts`, `context.var`, `context.repeat`, and
220
+ `context.automation` are populated at run time (previously
221
+ `context.trigger` was always empty). The editor types match via
222
+ `generateAutomationContextTypes`.
223
+
224
+ **Shell → `$CHECKSTACK_*` env vars.** `run_shell` flattens the run scope
225
+ into environment variables (e.g. `$CHECKSTACK_TRIGGER_PAYLOAD_TITLE`,
226
+ `$CHECKSTACK_ARTIFACT_INTEGRATION_JIRA_ISSUE_ISSUEKEY`). Arrays become a
227
+ single newline-separated var (iterate with `while IFS= read -r x; do …;
228
+ done <<< "$VAR"`). Every value is a plain string — no JSON blob, since
229
+ the container has no `jq` to parse one. A shared `toShellEnvKey`
230
+ helper (in `@checkstack/automation-common`) derives the names so the
231
+ shell editor's `$` autocomplete lists exactly what the runtime injects.
232
+
233
+ **One syntax per field kind (editor + runtime).** `MultiTypeEditorField`
234
+ no longer offers `{{ }}` autocomplete in `typescript` / `javascript` /
235
+ `shell` editors, and the dispatch engine no longer template-renders
236
+ native-code config fields (those whose `x-editor-types` is a code type) —
237
+ so `{{ }}` can't be used in a script by accident. Text / markup editors
238
+ (`raw`, `json`, `yaml`, `xml`, `markdown`, `formdata`) and plain string
239
+ fields keep `{{ }}` as before. Because both the automation and
240
+ health-check editors share `MultiTypeEditorField`, they behave
241
+ identically.
242
+
243
+ **Script-editor IntelliSense polish.** The code editors got a few
244
+ ergonomic fixes so the typed context is actually usable: the suggestion
245
+ **details panel auto-opens** (so long completion names are legible
246
+ on-focus, not hidden behind the chevron); word-based keyword noise is
247
+ disabled in favour of language-service + provider completions; and a
248
+ TS/JS completion provider makes `context.artifacts.` list the in-scope
249
+ artifact ids and **auto-convert the dot to bracket notation** —
250
+ `context.artifacts["integration-jira.issue"]` — since those ids aren't
251
+ valid identifiers. (Driven by a new opt-in `dottedKeyCompletions` prop on
252
+ the editor / `DynamicForm`.)
253
+
254
+ **BREAKING (beta):** `{{ }}` interpolation inside a script action's
255
+ `script` field (shell or TypeScript) is no longer expanded at run time —
256
+ read run data via the typed `context` object (TS) or `$CHECKSTACK_*` env
257
+ vars (shell) instead. Non-script config fields are unchanged.
258
+
259
+ Also fixes: switching a provider action in the visual editor now resets
260
+ its config, so the validator no longer reports the previous action's keys
261
+ as unrecognised.
262
+
263
+ - 41c77f4: feat(automation): Phase 11 — editor primitives + context type generation
264
+
265
+ Lays the UI + type-generation groundwork for Phase 12's visual automation
266
+ editor. Every primitive reuses the existing Monaco wrapper / template
267
+ engine / `jsonSchemaToTypeScript` helper rather than building parallel
268
+ infrastructure.
269
+
270
+ **`@checkstack/automation-common` — `resolveVariableScope`**
271
+
272
+ Pure walker that returns the in-scope `{{ … }}` paths at a given action
273
+ position. Conservative scoping rules: linear-upstream variables /
274
+ artifacts only (no leaking across `choose` / `parallel` / `repeat`
275
+ branches), `repeat.index` / `repeat.item` exposed only inside a `repeat`,
276
+ and trigger.payload modelled as a **discriminated union over
277
+ `trigger.event`** — every payload field surfaces; ones that come from a
278
+ subset of subscribed triggers carry a `conditionalOnTriggers` annotation
279
+ so the picker can render an "Only when …" hint. Earlier draft used
280
+ schema-intersection; switched to discriminated unions per review
281
+ feedback so Monaco can narrow correctly inside event-gated branches.
282
+
283
+ **Condition-aware narrowing.** When the path descends through a
284
+ `choose-when`, the resolver parses the branch's `when:` expression and
285
+ statically pins `trigger.event` to the set the condition allows —
286
+ patterns covered are `trigger.event == "X"` (either operand order),
287
+ `trigger.event != "X"`, `||`/`&&` of those, and `{ and: [...] }` /
288
+ `{ or: [...] }` combinators. So an action inside
289
+ `when: 'trigger.event == "incident.created"'` sees only the
290
+ `incident.created` variant in scope, the `conditionalOnTriggers`
291
+ annotation disappears, and other-trigger fields drop out entirely.
292
+ Nested choose branches compound (intersection). Anything outside the
293
+ covered patterns falls back to the full union — better to show every
294
+ field than guess wrong.
295
+
296
+ **`@checkstack/template-engine`**
297
+
298
+ The expression AST (`Expr`, `BinaryExpr`, `MemberExpr`, etc.) is now a
299
+ public export — the resolver's condition-narrowing walker needs to
300
+ inspect parsed condition trees. `ParsedCondition.root` is tightened
301
+ from `unknown` to `Expr` so consumers don't need to cast.
302
+
303
+ **`@checkstack/automation-frontend` — `generateAutomationContextTypes`**
304
+
305
+ Consumes `resolveVariableScope`'s output + the trigger / artifact
306
+ registries and emits the `declare const context: { … }` TS declaration
307
+ that `integration-script.run_script`'s Monaco editor injects via
308
+ `addExtraLib`. The emitted shape:
309
+
310
+ ```ts
311
+ type AutomationTrigger =
312
+ | { event: "incident.created"; payload: { … } }
313
+ | { event: "incident.resolved"; payload: { … } };
314
+
315
+ declare const context: {
316
+ trigger: AutomationTrigger;
317
+ artifacts: { "jira.issue"?: { key: string; … }; … };
318
+ var: { foo?: string; … };
319
+ repeat: { index: number; item: unknown }; // only when inside a repeat
320
+ };
321
+ ```
322
+
323
+ `jsonSchemaToTypeScript` from `@checkstack/ui` is reused via a deep
324
+ import (rather than the barrel) so the bun test runner doesn't try to
325
+ load Monaco's Vite-only `?worker` modules during unit tests.
326
+
327
+ **`@checkstack/ui` — new editor primitives**
328
+
329
+ - `TemplateValueInput` — single-line `{{ }}` autocomplete input.
330
+ Extracted from `DynamicForm/KeyValueEditor`'s previously-private
331
+ `TemplateInput` so other editor surfaces can share it without
332
+ rebuilding the picker UX. `KeyValueEditor` is now a one-line
333
+ delegation; `detectTemplateContext` is also exported.
334
+ - `VariablePicker` — hierarchical popover for the explicit "fx" /
335
+ "Insert variable" workflow. Renders a filterable tree of
336
+ `VariableNode`s with type chips and `Only when …` hints sourced from
337
+ the resolver's `conditionalOnTriggers`. Defaults to a small "fx" pill
338
+ trigger; callers can pass a custom one.
339
+ - `TemplateInput` — high-level mode switcher: `text` mode delegates to
340
+ `TemplateValueInput`, all other modes (`code` / `bash` / `json` /
341
+ `yaml`) delegate to `CodeEditor` with the matching language so the
342
+ action editor can swap widgets purely from the action's
343
+ `x-editor-types` annotation without touching the consuming code.
344
+ - `TemplateInputToggle` — the small "fx" pill that flips a typed input
345
+ (number / select / date / …) into template mode and back. Auto-infers
346
+ template mode when the saved value already starts with `{{`, so
347
+ round-tripping a previously-templated automation works out of the
348
+ box. Render-prop API for the typed editor so consumers keep control
349
+ over their own input shape.
350
+ - `ActionCard` — collapsible card that hosts a single action in the
351
+ visual editor. Decoupled from `DynamicForm` so container blocks
352
+ (`ChooseBlock` / `ParallelBlock` / `RepeatBlock` in Phase 12) can use
353
+ it as a structural shell over their own children. Toggle / delete /
354
+ drag handle are conditionally rendered on their callback's presence.
355
+
356
+ Storybook stories shipped for each of the new primitives.
357
+
358
+ **`@checkstack/integration-script-backend`**
359
+
360
+ `ScriptContext` docstring and the `scriptRunConfigSchema.script` field
361
+ description now point at `generateAutomationContextTypes` so the Phase
362
+ 12 editor wiring is unambiguous — the runtime payload type stays
363
+ `Record<string, unknown>` (the runner can't know the trigger schema),
364
+ but the **editor** narrows it per-automation from the subscribed
365
+ triggers' payload schemas.
366
+
367
+ - 41c77f4: feat(automation): Phase 12 — frontend plugin (Visual + YAML)
368
+
369
+ Ships the complete operator-facing surface for the automation platform:
370
+
371
+ **Pages**
372
+
373
+ - `AutomationListPage` — paginated table of every automation. Inline
374
+ enable / disable toggle, status filter, "Runs" deep-link per row,
375
+ trash-button delete with a confirmation modal. Rows themselves
376
+ navigate to the edit page on click; toggle / delete cells
377
+ `stopPropagation` to avoid the navigation.
378
+ - `AutomationEditPage` — **Visual ↔ YAML** tab switcher; both tabs
379
+ read/write the same canonical `definition` state, switching tabs
380
+ first commits the active tab's edits (parsing YAML on YAML→Visual)
381
+ so neither side ever wins by accident. Top-level metadata form
382
+ (name, description, status toggle, mode, max_runs) sits in a side
383
+ column. Save flow: commit active tab → `validateDefinition` RPC →
384
+ `createAutomation` / `updateAutomation`. Parse + validation errors
385
+ render as a destructive Alert. The "Run now" action fires
386
+ `manualRun` with the first declared trigger and navigates to the
387
+ resulting run detail.
388
+
389
+ **Visual tab** ships the full editor. `AutomationDefinitionEditor`
390
+ composes three sections — triggers, pre-run conditions, actions —
391
+ using the Phase 11 UI primitives (`ActionCard`,
392
+ `TemplateValueInput`, `VariablePicker`) plus a new `editor/`
393
+ module:
394
+
395
+ - `TriggersEditor` — per-trigger card with combobox event picker
396
+ (`ItemPicker`), optional `id` and `filter`, and a `DynamicForm`
397
+ for trigger config when the selected trigger declares a
398
+ `configSchema`.
399
+ - `ConditionsEditor` + recursive `ConditionEditor` — top-level
400
+ pre-run gating and the same recursive editor reused inside
401
+ `choose: when` clauses. Each level picks `expression` /
402
+ `and` / `or` / `not`; `and` / `or` host child conditions with
403
+ add/remove buttons; expression mode uses `TemplateValueInput`
404
+ with inline `VariablePicker`.
405
+ - `ActionListEditor` — drag-to-reorder via `@dnd-kit/core` +
406
+ `@dnd-kit/sortable`. Maintains a parallel stable-id array so
407
+ in-place edits don't churn React keys but reorders do. Add-step
408
+ popover offers all 10 action kinds with their icons.
409
+ - `ActionEditor` — dispatch component that picks the right
410
+ per-kind body and wraps it in a shared `ActionCard` (icon,
411
+ title, category badge, enable toggle, delete, drag handle).
412
+ Header exposes a kind-swap `<Select>` that preserves
413
+ operator-set metadata (id, description, enabled,
414
+ continue_on_error).
415
+ - Per-kind bodies covering every primitive — Provider (with
416
+ `DynamicForm` over the action's `configJsonSchema`), Variables
417
+ (KeyValueEditor with JSON-or-template parsing), Stop, Delay
418
+ (seconds vs template toggle), WaitForTrigger (event picker +
419
+ filter + timeout + context_key), ConditionGuard (reuses
420
+ `ConditionEditor`), Choose (recursive when-branches + optional
421
+ else), Parallel, Sequence, Repeat (count / for_each / while /
422
+ until + nested sequence + max_iterations safety net).
423
+ - **Scope-aware autocomplete.** A
424
+ `useVariableScope({ definition, path })` hook drives template
425
+ properties for every field — each action card knows its
426
+ `ActionPath`, so the `{{` autocomplete + `VariablePicker` only
427
+ ever offers paths actually in scope at that position,
428
+ including condition-narrowed `trigger.payload.*` inside `when:`
429
+ branches. Reuses Phase 11's `resolveVariableScope`.
430
+
431
+ **YAML tab** — Monaco `yaml` editor round-tripping the full schema
432
+ via `yaml.parse` / `yaml.stringify`.
433
+
434
+ - `RunsPage` — run history for a single automation. Status filter
435
+ buttons across the canonical `RunStatus` enum
436
+ (`pending|running|waiting|success|failed|cancelled|skipped`), rows
437
+ link to the run detail.
438
+ - `RunDetailPage` — single run drill-down. Shows the run header (status
439
+ - duration), a destructive Alert with `errorMessage` when the run
440
+ failed, a per-step timeline with status icon + attempts + inline
441
+ error message + collapsible result payload, the trigger payload as
442
+ read-only JSON in Monaco, and an artifacts panel listing every
443
+ produced artifact keyed by `artifactType`. Cancel-run button when the
444
+ run is `running` or `waiting`.
445
+ - `TemplatePlaygroundPage` — left/right editors for template body and
446
+ sample JSON context, mode switcher between `template` and
447
+ `condition`, "Render" button that calls `renderTemplate` RPC and
448
+ shows either the rendered string (template mode) or the boolean
449
+ result (condition mode). Parse errors come back with line/column
450
+ info shown alongside the error message — Monaco inline markers come
451
+ in a later polish pass.
452
+
453
+ **Plugin entry + slot extensions**
454
+
455
+ - `createFrontendPlugin({...})` wires every route, all access-gated
456
+ through `automationAccess.read` and `automationAccess.manage`:
457
+ - `/automation/` → list (read)
458
+ - `/automation/new` → blank edit page (manage)
459
+ - `/automation/:automationId` → edit (read, save gated on manage)
460
+ - `/automation/:automationId/runs` → run history (read)
461
+ - `/automation/:automationId/runs/:runId` → run drill-down (read)
462
+ - `/automation/playground` → playground (read)
463
+ - `AutomationMenuItems` slot extension on `UserMenuItemsSlot` adds an
464
+ "Automations" entry to the user menu for any user with
465
+ `automation.read`. Mirrors `incident-frontend`'s pattern of hiding
466
+ the menu link from unauthorised users even though the route itself
467
+ is also access-gated.
468
+ - No `foreignSignals` declared: every signal the automation domain
469
+ emits (`AUTOMATION_DEFINITION_CHANGED`, `AUTOMATION_RUN_*`) is owned
470
+ by this plugin, so the auto-invalidator wires it for free.
471
+
472
+ **Reused components, no duplication**
473
+
474
+ Every page is built from `@checkstack/ui` primitives: `PageLayout`,
475
+ `Card`, `Table`, `Badge`, `Toggle`, `Button`, `Select`,
476
+ `LoadingSpinner`, `QueryErrorState`, `EmptyState`,
477
+ `ConfirmationModal`, `Alert`, `CodeEditor`, `Tabs`, `DynamicForm`,
478
+ `KeyValueEditor`, `Popover`, `ActionCard`, `TemplateValueInput`,
479
+ `VariablePicker`. The visual editor's only new components are the
480
+ ones the existing UI library deliberately doesn't ship (combobox
481
+ `ItemPicker`, plus the automation-domain editors themselves) —
482
+ everything else is composition.
483
+
484
+ **New deps**: `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`
485
+ (drag-to-reorder), already in the monorepo via `catalog-frontend`'s
486
+ core/utilities usage; we add `sortable` because the action list needs
487
+ the higher-level sortable abstraction.
488
+
489
+ - 4832e33: fix(automation): insert runtime-parseable `templateRef` from editor autocomplete + variable picker, with array indexing
490
+
491
+ The automation editor's `{{ }}` autocomplete and the `fx` variable picker
492
+ previously inserted the canonical dotted path (e.g.
493
+ `artifact.integration-jira.issue.issueKey`), which the template engine
494
+ cannot parse when an artifact id contains dots or hyphens, and which used
495
+ the singular `artifact`/`var` namespaces the runtime template context does
496
+ not expose. They now insert the runtime-parseable `templateRef` form -
497
+ plural top-level namespace (`artifacts`/`variables`) plus bracket notation
498
+ for non-identifier segments, e.g. `artifacts["integration-jira.issue"].issueKey`.
499
+
500
+ - `@checkstack/automation-common`: `VariableEntry` gains `templateRef`
501
+ (runtime-parseable insertion form) and `referenceable`, alongside the
502
+ unchanged canonical `path`. New exported helpers `isTemplateIdentifier`,
503
+ `appendTemplateSegment`, and `appendArrayIndex` build the form. Scope
504
+ derivation now descends into `array` schemas, offering both the whole
505
+ array and a representative element subtree (`tags[0]`, `comments[0].author`,
506
+ nested `matrix[0][0]`).
507
+ - `CompletionField` / `TemplateProperty` / `VariableNode` carry a
508
+ `templateRef` alongside the canonical `path`.
509
+ - The staged completion provider's field label, filter/match, insert text,
510
+ and value-stage field lookup all operate in `templateRef` space. The
511
+ expression tokenizer now emits bracket tokens and reconstructs the full
512
+ `foo["bar"].baz` / `foo["bar"].list[0]` access chain (normalising single
513
+ quotes to the stored double-quoted form, and supporting bare numeric array
514
+ indices) so value-stage enum suggestions resolve for bracket-notation and
515
+ indexed fields.
516
+ - `VariablePicker` and the `DynamicForm` template inserters write the
517
+ `templateRef` (falling back to `path` when absent).
518
+ - Shell-env (`$CHECKSTACK_*`) name derivation deliberately keeps using the
519
+ canonical dotted `path`, so the suggested env names stay byte-identical
520
+ to the backend's path-based injection. Script-context type generation is
521
+ unchanged.
522
+ - `@checkstack/integration-script-backend`: shell-script actions now also
523
+ expose array elements as indexed `$CHECKSTACK_*_<i>` env vars (and
524
+ `$CHECKSTACK_*_<i>_<field>` for object elements), alongside the existing
525
+ whole-array newline-joined var, so the runtime injects exactly the
526
+ array-element names the editor now suggests.
527
+
528
+ - 6d52276: feat(automation): expose `trigger.actor` so automations can filter on who/what caused an event
529
+
530
+ Every platform event now carries an **actor** - the user, application (API
531
+ client), service (backend-to-backend), or `system` (background /
532
+ unauthenticated) that caused it - and the automation engine surfaces it to
533
+ automations as `trigger.actor`. This lets a trigger filter gate on the
534
+ origin of the event it reacts to:
535
+
536
+ ```text
537
+ {{ trigger.actor.type == "system" }} # auto-created by the platform
538
+ {{ trigger.actor.type == "user" }} # a human
539
+ {{ trigger.actor.id == "app-deploybot" }} # a specific application
540
+ ```
541
+
542
+ `trigger.actor` is available on **every** trigger - it is injected by the
543
+ platform, not declared per trigger - and editor autocomplete + Run Script
544
+ context types include `trigger.actor.{type,id,name}`.
545
+
546
+ How it works:
547
+
548
+ - **`@checkstack/common`** adds the canonical `Actor` type / `ActorSchema`
549
+ and `SYSTEM_ACTOR`.
550
+ - **`@checkstack/backend-api`** adds `resolveActor(user)` and a
551
+ `HookEventMeta` envelope. The hook listener / `onHook` signature gains an
552
+ optional second `meta` argument (additive, backward compatible).
553
+ - **`@checkstack/backend`** wraps emitted hooks in an envelope so the actor
554
+ travels with the payload through the distributed queue, unwrapping it
555
+ before delivery. The RPC emit path captures the authenticated caller;
556
+ background emits default to the system actor. Raw/legacy queue data is
557
+ treated as a system-actor payload, so delivery stays backward compatible.
558
+ - **`@checkstack/automation-backend`** threads the actor into the dispatch
559
+ scope (`trigger.actor`), available to trigger filters, top-level
560
+ conditions, and all run templates, and persisted in the run's scope
561
+ snapshot. Manual runs are attributed to the invoking user.
562
+ - **`@checkstack/automation-common`** / **`@checkstack/automation-frontend`**
563
+ expose `trigger.actor` in the editor variable scope and the generated
564
+ Run Script `context.trigger.actor` types.
565
+
566
+ No database migration and no per-trigger schema changes: the actor rides as
567
+ event-envelope metadata and in the run scope snapshot.
568
+
569
+ - 6d52276: feat(automation): expose `trigger.id` and reconcile the trigger scope so multiple triggers are distinguishable
570
+
571
+ Automations with more than one trigger could not tell which trigger fired:
572
+ the trigger id wasn't queryable, and scripts only received `trigger.event`
573
+ (so two triggers on the same event were indistinguishable). This exposes a
574
+ consistent trigger contract everywhere - `trigger.id`, `trigger.event`,
575
+ `trigger.actor`, `trigger.payload` - in templates, shell, and TypeScript
576
+ scripts.
577
+
578
+ - **`trigger.id` is now available** in templates (`{{ trigger.id }}`) and in
579
+ the script context (`context.trigger.id`). It is typed as the **literal
580
+ union** of the automation's trigger ids, so it discriminates triggers -
581
+ including two subscribed to the same `event`.
582
+ - **Auto-generated trigger ids.** The editor now assigns a unique, log-
583
+ friendly id to every trigger (derived from its event, e.g.
584
+ `incident_created`, deduped as `incident_created_2`), mirroring action ids:
585
+ seeded on the starter automation, assigned on add, and re-filled on blur.
586
+ - **Scripts now receive `trigger.id` and `trigger.actor`.** The
587
+ `ActionRunScope` projection previously dropped both (it only forwarded
588
+ `event` + `payload`), so `context.trigger.actor` was typed but never
589
+ populated - that gap is fixed.
590
+ - **Scope key reconciled.** The internal dispatch scope now exposes
591
+ `trigger.event` as the canonical key (matching the editor and script
592
+ contract) instead of leaking `trigger.eventId`; `trigger.eventId` is kept
593
+ as a back-compat alias, so `{{ trigger.event }}` now resolves in template
594
+ fields where it previously returned `undefined`.
595
+
596
+ No database migration: the actor and id ride in the run scope snapshot. A
597
+ shared `deriveTriggerId` is exported from `@checkstack/automation-common` so
598
+ the editor, generated script types, and the runtime all agree on derived ids.
599
+
600
+ - 35bc682: feat(healthcheck): expose check + system run-context to script collectors
601
+
602
+ Script health checks can now read which check and system a run is for.
603
+ Previously shell scripts got only a curated env whitelist and inline
604
+ scripts only `context.config`, so a script had no built-in way to know
605
+ its own check name or the system it was checking.
606
+
607
+ - `@checkstack/backend-api`: new `CollectorRunContext` type
608
+ (`{ check: { id, name, intervalSeconds }, system: { id, name } }`) and
609
+ an optional `runContext` param on `CollectorStrategy.execute`. Optional,
610
+ so existing collector implementations are unaffected.
611
+ - Shell-script collector: injects reserved `CHECKSTACK_CHECK_ID`,
612
+ `CHECKSTACK_CHECK_NAME`, `CHECKSTACK_CHECK_INTERVAL_SECONDS`,
613
+ `CHECKSTACK_SYSTEM_ID`, `CHECKSTACK_SYSTEM_NAME` env vars (user-supplied
614
+ `env` still wins on collision).
615
+ - Inline-script collector: exposes `context.check` and `context.system`
616
+ alongside `context.config`; the inline-script editor now types them for
617
+ autocomplete.
618
+ - Shell editors (health-check collectors and automation shell actions) now
619
+ also suggest the user's own `env` (JSON) keys as `$NAME` completions, via
620
+ the new exported `customShellEnvVars` helper. Keys that aren't valid shell
621
+ identifiers are omitted.
622
+ - Fix: the Typefox `CodeEditor` captured a stale `onChange` at editor start,
623
+ so editing one `DynamicForm` field reverted sibling fields changed since
624
+ mount (e.g. typing in a shell `script` field wiped an unsaved `env` value,
625
+ or deleted a sibling automation action added after mount). The change
626
+ handler now routes through a ref to the current `onChange`.
627
+ - Fix: focusing a JSON editor threw "LanguageStatusService.addStatus is not
628
+ supported" because the standalone service set omitted `ILanguageStatusService`.
629
+ That one service is now registered via `serviceOverrides`.
630
+ - Fix: the automation trigger card nested a `<Badge>` (a `<div>`) inside a
631
+ `<p>`, producing a `validateDOMNesting` warning. Switched the wrapper to a
632
+ `<div>`.
633
+ - Local runs (`queue-executor`) and satellite runs both populate the
634
+ context. `SatelliteAssignment` (and the `getAssignmentsForSatellite`
635
+ RPC output) gained optional `configName` / `systemName` so the metadata
636
+ reaches satellite-side execution; `HealthCheckService` resolves the
637
+ system name via the catalog client.
638
+
639
+ BREAKING CHANGE: `createHealthCheckRouter` now requires a `catalogClient`
640
+ option (used to resolve system names for satellite assignments). Update
641
+ call sites to pass the catalog RPC client.
642
+
643
+ ### Patch Changes
644
+
645
+ - Updated dependencies [e2d6f25]
646
+ - Updated dependencies [41c77f4]
647
+ - Updated dependencies [41c77f4]
648
+ - Updated dependencies [e1a2077]
649
+ - Updated dependencies [41c77f4]
650
+ - Updated dependencies [41c77f4]
651
+ - Updated dependencies [41c77f4]
652
+ - Updated dependencies [41c77f4]
653
+ - Updated dependencies [4832e33]
654
+ - Updated dependencies [6d52276]
655
+ - Updated dependencies [6d52276]
656
+ - Updated dependencies [35bc682]
657
+ - Updated dependencies [c39ee69]
658
+ - @checkstack/automation-common@0.2.0
659
+ - @checkstack/frontend-api@0.6.0
660
+ - @checkstack/ui@1.11.0
661
+ - @checkstack/template-engine@0.2.0
662
+ - @checkstack/integration-common@0.6.0
663
+ - @checkstack/common@0.12.0
664
+ - @checkstack/signal-frontend@0.1.5