[Alerting V2] bulk get alert actions#258353
Conversation
|
Pinging @elastic/response-ops (Team:ResponseOps) |
08e9e28 to
2989159
Compare
|
Why introducing a _bulk_get? Couldn't we have a filter on |
There was a problem hiding this comment.
Pull request overview
Adds an internal bulk-get API to retrieve the latest action “state” per alert episode, enabling the alert episodes table to render episode status.
Changes:
- Introduces a new internal route for bulk-getting alert actions by
episode_ids. - Implements an ES|QL query + client method to fetch latest ack/deactivate/snooze actions and fill in missing episodes with null defaults.
- Adds schemas and unit tests to support the new bulk-get behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| x-pack/platform/plugins/shared/alerting_v2/tsconfig.json | Adds a project reference needed for server-side test/mocks compilation. |
| x-pack/platform/plugins/shared/alerting_v2/server/setup/bind_routes.ts | Registers the new bulk-get alert actions route. |
| x-pack/platform/plugins/shared/alerting_v2/server/routes/alert_actions/bulk_get_alert_actions_route.ts | New internal endpoint wiring + validation for bulk-getting alert actions. |
| x-pack/platform/plugins/shared/alerting_v2/server/lib/alert_actions_client/queries.ts | Adds ES |
| x-pack/platform/plugins/shared/alerting_v2/server/lib/alert_actions_client/fixtures/query_responses.ts | Adds ES |
| x-pack/platform/plugins/shared/alerting_v2/server/lib/alert_actions_client/alert_actions_client.ts | Adds bulkGet() implementation, including “not found” default records. |
| x-pack/platform/plugins/shared/alerting_v2/server/lib/alert_actions_client/alert_actions_client.test.ts | Adds unit tests for bulkGet(). |
| x-pack/platform/packages/shared/response-ops/alerting-v2-schemas/src/alert_action_schema.ts | Adds request/response schemas + types for the new bulk-get API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
.../platform/plugins/shared/alerting_v2/server/lib/alert_actions_client/alert_actions_client.ts
Show resolved
Hide resolved
x-pack/platform/plugins/shared/alerting_v2/server/lib/alert_actions_client/queries.ts
Show resolved
Hide resolved
x-pack/platform/packages/shared/response-ops/alerting-v2-schemas/src/alert_action_schema.ts
Outdated
Show resolved
Hide resolved
...tform/plugins/shared/alerting_v2/server/routes/alert_actions/bulk_get_alert_actions_route.ts
Show resolved
Hide resolved
| export class BulkGetAlertActionsRoute implements RouteHandler { | ||
| static method = 'post' as const; | ||
| static path = `${INTERNAL_ALERTING_V2_ALERT_API_PATH}/action/_bulk_get`; | ||
| static security: RouteSecurity = { | ||
| authz: { | ||
| requiredPrivileges: [ALERTING_V2_API_PRIVILEGES.alerts.read], | ||
| }, | ||
| }; | ||
| static options = { access: 'internal' } as const; | ||
| static validate = { | ||
| request: { | ||
| body: buildRouteValidationWithZod(bulkGetAlertActionsBodySchema), | ||
| }, | ||
| response: { | ||
| 200: { | ||
| body: buildRouteValidationWithZod(bulkGetAlertActionsResponseSchema), | ||
| }, | ||
| }, | ||
| } as const; |
kdelemme
left a comment
There was a problem hiding this comment.
LGTM! Just a question about POST _bulk_get vs GET actions but I think it makes sense since we won't have the need for a wider GET /actions (it's not like we are going to have a UI on the actions).
| request: { | ||
| body: buildRouteValidationWithZod(bulkGetAlertActionsBodySchema), | ||
| }, | ||
| response: { |
There was a problem hiding this comment.
nice I need to start adding these into the other routes 👍🏻
Also we probably wants to rename /action to /actions but that's out of scope for this PR
There was a problem hiding this comment.
nice I need to start adding these into the other routes
I will keep in mind and update whenever I need to change something too 👌
I had actually forgotten initially, but then, when working on a different PR, I needed to know the types and noticed they were missing.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
💔 Build Failed
Failed CI StepsMetrics [docs]
History
cc @adcoelho |
Since this returns a more customized response instead of all the actions, I wanted to use a different route. Tbh I am not convinced this is the best approach. I think after I am done with the actions UI I will refactor it to just use esql directly. |
commit 057ff6c9966789dfabbfdc37922fc6487c41594b
Merge: c475a483262a b937b8b2cc9d
Author: Anna Davydova <ana.davydova@elastic.co>
Date: Thu Mar 26 11:52:37 2026 +0100
Merge branch 'alerting_v2' into alertingv2-add-filtering
commit b937b8b2cc9df002bc65cfbc53e2899f5923949e
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Thu Mar 26 10:49:13 2026 +0100
Remove array membership logic from `evaluateKql`.
commit c475a483262a3c32fad1e878bdfee6d2931f838b
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Thu Mar 26 09:52:25 2026 +0000
Changes from node scripts/lint.js --fix
commit 544704f3db115ada3a85a71b50abd60996ff4aba
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Thu Mar 26 09:47:33 2026 +0000
Changes from node scripts/lint.js --fix
commit 2dee2c7255415f3b1532f25f4354236ec64795bc
Merge: ddbbc64fff7d 4075e0b0191d
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Thu Mar 26 10:43:18 2026 +0100
Merge remote-tracking branch 'upstream/alerting_v2' into alertingv2-add-filtering
commit ddbbc64fff7dae7f0da0ec32659fb66d510afc11
Merge: 30c1ce9d4788 b6f91968f3fa
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Thu Mar 26 10:37:46 2026 +0100
Merge branch 'alertingv2-add-filtering' of https://github.com/ana-davydova/kibana into alertingv2-add-filtering
commit 30c1ce9d4788d4a8dd888e585ed822474693cbb2
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Thu Mar 26 10:37:34 2026 +0100
small fix
commit b6f91968f3fabf5473841bf089098378a6b491d5
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Wed Mar 25 21:32:33 2026 +0000
Changes from node scripts/eslint_all_files --no-cache --fix
commit 3bc8b549c5f7b0bdc8a47af52acbf689ae7cb313
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Wed Mar 25 21:04:50 2026 +0000
Changes from node scripts/check_mappings_update --fix
commit 4075e0b0191d25c0ae409c1a6ef79afddda13911
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Wed Mar 25 22:03:37 2026 +0100
kbn boostrap changes
commit f369498e3b5b8a6275b032a9777065927e96dc06
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Wed Mar 25 21:47:18 2026 +0100
small change in the normalizer
commit ec478ebaf0afa69c37db3094a71cc0ee10f0da22
Merge: 5312ee5baea7 c313b9f5684f
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Wed Mar 25 21:44:16 2026 +0100
Merge remote-tracking branch 'upstream/alerting_v2' into alertingv2-add-filtering
commit 5312ee5baea7e36b6096af1ce1fe2b33d77fef06
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Wed Mar 25 21:34:05 2026 +0100
updating the sorting and filters
commit 016edbf8019d37a4cdce8a0074d996d0de7faaea
Merge: 125c6f706b6e 1b4475f2d63e
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Wed Mar 25 21:29:35 2026 +0100
Merge remote-tracking branch 'upstream/main' into alerting_v2
commit 125c6f706b6ebd022ed3a479f83b96cb37728936
Merge: c313b9f5684f fad3ee4bc867
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Wed Mar 25 18:53:54 2026 +0100
Resolve merge conflict in task_runner.ts
Keep upstream/main formatting and merge logCancelEvent addition
from the feature branch into the task cancel handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit c313b9f5684ffc03e3b9df24900bc90ab036701e
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Wed Mar 25 12:59:03 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit e9a8b0d676e5104012741b6347935cd7928de8cc
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Wed Mar 25 13:36:21 2026 +0100
Add OAS descriptions to rule routes (#259367)
- Add `summary` and `tags` to all rule route `options` for OAS
generation
- Add `meta: { description }` to all route schema fields (params, query)
- Extract shared `ruleIdParamsSchema` to avoid duplicating the rule ID
path param schema across get, update, and delete routes
- Add `findRulesResponseSchema` and `bulkOperationResponseSchema` to
`@kbn/alerting-v2-schemas`
- Add response validation blocks with body schemas and status code
descriptions to all rule routes
Closes https://github.com/elastic/rna-program/issues/88
1. Add `server.oas.enabled: true` to `config/kibana.dev.yml`
2. Start Elasticsearch and Kibana
3. Fetch the generated OAS spec:
```bash
curl -s -u elastic:changeme 'http://localhost:5601/api/oas?pathStartsWith=%2Finternal%2Falerting%2Fv2%2Frule&access=internal' | jq '.paths'
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
commit 9ea250f826557df798d7e8c4da81be36f24ad11a
Merge: 40d43500a7da d155afaadfee
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Wed Mar 25 13:35:30 2026 +0100
Merge remote-tracking branch 'upstream/main' into alerting_v2
commit 40d43500a7da64259fcd49ddaf78f0a2463ac2db
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Tue Mar 24 19:53:16 2026 -0400
[Alerting v2] Add update API key for notification policies (#259390)
commit 144fdae3e7f3f09faeafc7f1986b5540fd26a193
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue Mar 24 17:13:56 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit c5b8c460f1abb9531893b8de76eeeec1841a091c
Author: Dominique Clarke <dominique.clarke@elastic.co>
Date: Tue Mar 24 12:50:34 2026 -0400
[Alerting v2] adjust rule flyout export (#258200)
This PR adjusts how the `@kbn/alerting-v2-rule-form` package is consumed
by moving the lazy-loaded, stateful wrapper into the `alerting_v2`
plugin itself, and exposing it to consumers via the plugin's **start
contract**.
When Discover imported `@kbn/alerting-v2-rule-form` directly — even with
`React.lazy()` — the package's code was still bundled into Discover's
chunk, increasing its bundle size. Additionally, each consuming plugin
had to manually assemble and pass the required services (`http`, `data`,
`dataViews`, `notifications`, `application`) to the rule form
components.
Following the established pattern used by Discover (`DiscoverContainer`)
and Lens (`EmbeddableComponent`, `SaveModalComponent`) — which expose
lazy-loaded components via their plugin start contracts — this PR:
1. **Creates a service layer in the `alerting_v2` plugin**
(`kibana_services.ts`) using a `BehaviorSubject` to store
`RuleFormServices` and expose them via
`untilPluginStartServicesReady()`.
2. **Creates a stateful wrapper component**
(`create_rule_form_flyout.tsx`) that uses `useAsync` to concurrently
load the services and dynamically import `@kbn/alerting-v2-rule-form`,
then renders the underlying `DynamicRuleFormFlyout` with services
injected.
3. **Defines a typed start contract** (`AlertingV2PublicStart`) and
binds it via `bind(Start)` in the Inversify `ContainerModule`, following
the [documented `@kbn/core-di`
pattern](https://github.com/elastic/kibana/blob/main/src/core/packages/di/common/README.md).
4. **Simplifies the Discover integration** — Discover declares
`alertingVTwo` as an `optionalPlugin` and accesses
`DynamicRuleFormFlyout` from the start contract
(`plugins.alertingVTwo.DynamicRuleFormFlyout`), threaded through
`DiscoverServices`. No direct package imports, no manual service wiring.
* **Bundle size**: The heavy `@kbn/alerting-v2-rule-form` package is
bundled with the `alerting_v2` plugin and lazily loaded on demand,
rather than inflating every consuming plugin's bundle.
* **Decoupled consumers**: Consuming plugins no longer need direct
dependencies on `data`, `dataViews`, `notifications`, etc. just to
render the rule form — services are managed centrally by the owning
plugin.
* **Consistent pattern**: Follows the same approach as Discover's
`DiscoverContainer` and Lens's `EmbeddableComponent` — exposing a
lazy-loaded component via the plugin start contract.
| File | Change |
| --- | --- |
| `alerting_v2/public/types.ts` | New — `AlertingV2PublicStart`
interface with `DynamicRuleFormFlyout` |
| `alerting_v2/public/kibana_services.ts` | New — BehaviorSubject-based
service provider |
| `alerting_v2/public/create_rule_form_flyout.tsx` | New — Stateful
wrapper with lazy loading + service injection |
| `alerting_v2/public/index.ts` | Binds start contract via
`bind(Start)`; exports types |
| `alerting_v2/public/kibana_services.test.ts` | New — Tests for service
readiness (sync + async resolution) |
| `alerting_v2/public/create_rule_form_flyout.test.tsx` | New — Tests
for wrapper (loading state, prop passing, service injection) |
| `discover/kibana.jsonc` | Added `alertingVTwo` to `optionalPlugins` |
| `discover/public/types.ts` | Added `alertingVTwo?:
AlertingV2PublicStart` to `DiscoverStartPlugins` |
| `discover/public/build_services.ts` | Added `alertingVTwo` to
`DiscoverServices` and `buildServices` |
| `discover/…/get_create_rule.tsx` | Consumes flyout from
`services.alertingVTwo` instead of direct import |
| `discover/…/get_create_rule.test.tsx` | Flyout provided via mock
services |
| `discover/public/__mocks__/services.ts` | Added `alertingVTwo` mock to
shared Discover service mock |
| `discover/tsconfig.json` | Added `@kbn/alerting-v2-plugin` reference
for type imports |
- [x] Any text added follows EUI's writing guidelines, uses sentence
case text and includes i18n support
- [x] Unit or functional tests were updated or added to match the most
common scenarios
- [x] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the guidelines
* **Low risk**: The `alerting_v2` plugin is behind a feature flag and
targeting the `alerting_v2` branch, so this change does not affect
production Discover behavior on `main`.
* **Service timing**: If `getStartServices()` hasn't resolved when the
flyout is opened, the wrapper will show a loading spinner until services
are ready. This matches the existing Discover/Lens pattern and is
expected behavior.
* **Optional plugin**: `alertingVTwo` is declared as an `optionalPlugin`
in Discover. The UI is already gated on
`services.capabilities.alertingVTwo` before rendering the flyout, so the
non-null assertion (`alertingVTwo!`) in `CreateESQLRuleFlyout` is safe.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Kevin Delemme <kevin.delemme@elastic.co>
commit 72109b841222b168cc45bddb1866e94a413a0112
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Tue Mar 24 12:17:32 2026 -0400
Minimize dispatcher integration test server setup (#259416)
- Adds `esArgs` to disable ML and Watcher at the Elasticsearch level
during dispatcher integration tests, reducing memory usage and startup
time.
- Disables unnecessary Kibana plugins (`reporting`, `canvas`, `uptime`,
`fleet`) that are not needed by the alerting_v2 dispatcher tests.
- [ ] Run `yarn test:jest_integration
x-pack/platform/plugins/shared/alerting_v2/server/lib/dispatcher/integration_tests/dispatcher.test.ts`
and verify all tests pass
- [ ] Confirm reduced startup time / resource usage compared to baseline
Made with [Cursor](https://cursor.com)
commit 3bf6e1e19c7562ca7cdd3c96fb6f44374e78f72f
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Tue Mar 24 16:42:53 2026 +0100
Set schema limits for rule artifacts, bulk get IDs, and notification policy destinations (#259292)
- `artifacts` array: `maxSize: 100`
- Bulk get `ids` query param: `maxSize: 1000`
- Notification policy `destinations` array: `minSize: 1, maxSize: 20`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
commit ddf83972558585d50e61989b50b41d349d531313
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Tue Mar 24 16:29:22 2026 +0100
commit node scripts/check_mappings_update --fix
commit bca2e2783598c81bfc0aa88a878c92751170a1ce
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Tue Mar 24 16:15:00 2026 +0100
revert: restore notification policy mappings to original state
The mapping changes (removing keyword multi-fields and explicit type: 'object')
broke notification policy tests. Reverting to the original mappings definition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit 4b5653f12753f0750dad8e064dc92a1fd45f87ab
Author: Antonio <antonio.coelho@elastic.co>
Date: Tue Mar 24 15:01:18 2026 +0100
[Alerting V2] Rename indices and ESQL views (#258651)
Closes https://github.com/elastic/rna-program/issues/179
Now that the Kibana system role privileges were updated, we can rename
these.
**Indices**
- `.alerting-events` -> `.rule-events`
- `.alerting-actions` -> `.alert-actions`
**Views**
- `$.alerting-episodes` -> `$.alert-episodes`
- `$.alerting-events` -> `$.rule-events`
- `$.alerting-actions` -> `$.alert-actions`
commit fa58a0f7c4750d7e82152b0b0c5b58bbd8215cd9
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Tue Mar 24 14:59:07 2026 +0100
fix to the rules list page
commit f98374e7f4ba4fac532e9780986fbead4edb8bb5
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue Mar 24 12:25:03 2026 +0000
Changes from node scripts/check_mappings_update --fix
commit 64b18f508aaa8eec1614cd253aa51caf9e82837c
Merge: b23d99eeda55 7566f5ccca9a
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Tue Mar 24 13:21:11 2026 +0100
Merge remote-tracking branch 'upstream/main' into alerting_v2
commit b23d99eeda55a7de1c95c809d339416e13f2040b
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Tue Mar 24 13:10:15 2026 +0100
fix: resolve SO type validation failures for initial model versions
- Remove mappings_addition from rule model version 1 (initial version can only have schemas)
- Remove keyword multi-fields from notification policy name/description mappings
- Remove explicit type: 'object' from destinations and auth mappings
- Simplify destination schema from oneOf to direct object for schema introspection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit aee85750dba7b7969f9a720bf70d3572a229ccf2
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Tue Mar 24 12:29:03 2026 +0100
updating the search and table with new filters
commit 3a67cd8690c1ccabdac8c88fb79d9a92b6f51352
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Tue Mar 24 12:16:48 2026 +0100
update rule_list page with basic ui for status and rule mode
commit 77a3ab04f9b99e41a2d00a89b250143e32b549e6
Author: Joana Cardoso <169058851+joana-cps@users.noreply.github.com>
Date: Tue Mar 24 10:53:12 2026 +0100
[Alerting V2] Restrict width on create/edit rule page (#259015)
Wraps `RuleFormPage` content in `EuiPageTemplate.Section` with
`restrictWidth={true}` for create, edit, and clone flows. The Management
app shell uses a full-width `KibanaPageTemplate`; this limits width only
for the rule form.
- [ ] Open Rules V2 → Create rule and confirm content is
width-restricted
- [ ] Edit an existing rule and confirm the same layout
- [ ] Rules list and rule details remain full width (unchanged)
Made with [Cursor](https://cursor.com)
Co-authored-by: Antonio <antonio.coelho@elastic.co>
commit 37e881e96b4a1a047795e3b7e1364ec9ff0b0c20
Author: ana.davydova@elastic.co <ana.davydova@elastic.co>
Date: Mon Mar 23 21:51:21 2026 +0100
updated the API with new filter fields enabled and rule mode
commit b503e23ba4e5dfa161cb1d32ffd48aabfd247760
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Mon Mar 23 16:41:21 2026 -0400
feat(alertingv2): make dispatcher space-aware (#259182)
Resolves https://github.com/elastic/rna-program/issues/180
- Derive `spaceId` from saved object namespaces for rules and
notification policies
- Enforce same-space matching between rules and notification policies in
the dispatcher
- Dispatch workflows in the notification policy's space instead of the
hardcoded `default` space
- **Types**: Add `spaceId` to `Rule`, `NotificationPolicy`, and
`NotificationGroup` interfaces
- **FetchRulesStep**: New `findByIds` method on
`RulesSavedObjectService` using PIT finder with `namespaces: ['*']` for
cross-space rule lookup (with optional `spaceId` scoping)
- **FetchPoliciesStep**: Preserve `namespaces` from `findAllDecrypted`
(now using `namespaces: ['*']`), derive `spaceId` via
`savedObjectNamespacesToSpaceId` helper. Fetch only enabled policies
- **EvaluateMatchersStep**: Pre-group policies by space, only match
episodes against policies in the same space as the rule
- **BuildGroupsStep**: Propagate `spaceId` from policy to
`NotificationGroup`
- **DispatchStep**: Use `group.spaceId` for workflow lookup and
scheduling instead of hardcoded `'default'`
- [x] All 109 dispatcher unit tests pass
- [x] TypeScript type check passes
- [x] ESLint passes
Set up two separate spaces (e.g. `space-a` and `space-b`) and verify
that rules, notification policies, and workflows are correctly isolated
per space.
```
node x-pack/scripts/data_forge.js --events-per-cycle 200 --lookback now-20m --dataset fake_stack --kibana-url http://localhost:5601 --install-kibana-assets
```
In each space, create a rule using an ES|QL query the admin console
data:
```
FROM kbn-data-forge-fake_stack.admin-console-* | STATS count = COUNT(*) WHERE http.response.status_code >= 500
```
- Use a grouping on "count"
- use a 10m lookback window
Create a simple workflow (e.g. a workflow execution logger is the
simpler) in each space.
```yaml
version: "1"
name: simple workflow execution log
description: log in workflow logs
enabled: true
triggers:
- type: manual
inputs:
properties:
id:
type: string
description: Unique identifier of the notification group
ruleId:
type: string
description: Identifier of the rule that produced this notification group
policyId:
type: string
description: Identifier of the notification policy that matched and dispatched the group
groupKey:
type: object
description: Grouping key for the notification group. This is currently generated by the dispatcher and may vary depending on grouping behavior.
additionalProperties: true
episodes:
type: array
description: Alert episodes included in this notification group
items:
type: object
properties:
last_event_timestamp:
type: string
format: date-time
description: Timestamp of the latest event seen for this episode
rule_id:
type: string
description: Identifier of the rule that produced this episode
group_hash:
type: string
description: Hash identifying the alert series/group this episode belongs to
episode_id:
type: string
description: Unique identifier of the alert episode
episode_status:
type: string
description: Current lifecycle status of the alert episode
enum:
- inactive
- pending
- active
- recovering
required:
- last_event_timestamp
- rule_id
- group_hash
- episode_id
- episode_status
additionalProperties: false
required:
- id
- ruleId
- policyId
- groupKey
- episodes
additionalProperties: false
steps:
- name: log_inputs
type: console
with:
message: "Rule {{ inputs.ruleId }} - Inputs: {{ inputs | json }}"
- name: log_group_summary
type: console
with:
message: "Notification group {{ inputs.id }} for policy {{ inputs.policyId }} has {{ inputs.episodes | size }} episode(s)"
- name: for_each_episode
type: foreach
foreach: "{{ inputs.episodes | json }}"
steps:
- name: log_active_episode
type: console
if: "${{ foreach.item.episode_status == 'active' }}"
with:
message: "ACTIVE alert for rule {{ inputs.ruleId }} - episode {{ foreach.item.episode_id }} - group {{ foreach.item.group_hash }} - last event {{ foreach.item.last_event_timestamp }}"
- name: log_recovered_episode
type: console
if: "${{ foreach.item.episode_status == 'inactive' }}"
with:
message: "RECOVERED alert for rule {{ inputs.ruleId }} - episode {{ foreach.item.episode_id }} - group {{ foreach.item.group_hash }} - last event {{ foreach.item.last_event_timestamp }}"
- name: log_other_episode
type: console
if: "${{ foreach.item.episode_status == 'pending' or foreach.item.episode_status == 'recovering' }}"
with:
message: "pending/recovering alert for rule {{ inputs.ruleId }} - episode {{ foreach.item.episode_id }} - group {{ foreach.item.group_hash }} - last event {{ foreach.item.last_event_timestamp }}"
```
In each space, create a notification policy that routes to the workflow
of that space (the search bar should not list the workflow from the
other space anyway)
- Wait for the rules to fire (data-forge data should trigger the
`status_code >= 500` condition)
- Verify that the rule in `space-a` only triggers the notification
policy in `space-a` and dispatches to the workflow in `space-a`
- Verify that the rule in `space-b` only triggers the notification
policy in `space-b` and dispatches to the workflow in `space-b`
- Confirm there is **no cross-space matching** — a notification policy
in `space-b` should never fire for a rule in `space-a`: look at the logs
(for the rule_id) of each workflow execution
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
commit ce70702091a513b02bf23ccfbc3e6c4251f8f441
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Mon Mar 23 19:14:11 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit d58816c384aa6915263ed85cc349dd640b5236c1
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Mon Mar 23 14:53:30 2026 -0400
[Alerting v2] Replace custom matcher input with KQL QueryStringInput (#258917)
Resolves https://github.com/elastic/rna-program/issues/186 and improve
form UX
- Replace the custom `MatcherInput` component with EUI's built-in
`QueryStringInput` from the `kql` plugin, providing proper KQL syntax
highlighting, autocomplete, and validation out of the box.
- Define a typed `MatcherContext` schema with field descriptors
(`MATCHER_CONTEXT_FIELDS`) so the KQL input auto-suggests valid field
names (e.g. `episode_status`, `rule.name`, `rule.labels`).
- Align the server-side `evaluateMatchers` step to build a flat
`MatcherContext` object matching the schema, instead of spreading raw
episode + rule objects.
- Disable auto focus on the KQL input to prevent unwanted scroll jumps
when the form mounts.
- [ ] Open the notification policy create/edit form and verify the
matcher input renders as a KQL bar with autocomplete suggestions for
context fields.
- [ ] Type a valid KQL expression (e.g. `episode_status : "active" and
rule.name : "my-rule"`) and confirm it is accepted.
- [ ] Verify language switcher is hidden (KQL only).
- [ ] Confirm the form does not auto-scroll to the matcher input on
mount.
- [ ] Run unit tests: `yarn test:jest
x-pack/platform/plugins/shared/alerting_v2/public/components/notification_policy/form/components/matcher_input/matcher_kql_input.test.tsx`
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit fac15106d048bfab9dd5903cc2500df9405a2303
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Mon Mar 23 16:27:22 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit 7073d1e82104200e053829154bb7f48e3ace6ca2
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Mon Mar 23 16:27:14 2026 +0000
Changes from node scripts/generate codeowners
commit 06b0d23f0828ebba88a4f4c5f26beb901fea8f50
Author: Antonio <antonio.coelho@elastic.co>
Date: Mon Mar 23 17:26:49 2026 +0100
[Alerting V2] Remove 'untag' alert action (#258643)
- Remove the untag alert action.
- Return `tags = LAST(tags, @timestamp) WHERE action_type IN ("tag")` by
`episode.id` in the bulk get alert actions query.
Start by posting some actions with tags:
```
POST .alerting-actions/_bulk
{"create":{}}
{"@timestamp":"2026-03-18T12:50:00.000Z","last_series_event_timestamp":"2026-03-18T12:20:00.000Z","actor":"user-3","action_type":"tag","group_hash":"gh-10","episode_id":"ep-1","rule_id":"rule-5", "tags": ["a","b"]}
{"create":{}}
{"@timestamp":"2026-03-18T12:50:00.000Z","last_series_event_timestamp":"2026-03-18T12:20:00.000Z","actor":"user-3","action_type":"tag","group_hash":"gh-10","episode_id":"ep-2","rule_id":"rule-5", "tags": ["foo","bar"]}
{"create":{}}
{"@timestamp":"2026-03-18T12:50:00.000Z","last_series_event_timestamp":"2026-03-18T12:20:00.000Z","actor":"user-3","action_type":"tag","group_hash":"gh-10","episode_id":"ep-2","rule_id":"rule-5", "tags": ["foo", "bar"]}
```
You can then use the query below to confirm the tags are returned.
There should be 2 results.
```
POST /_query
{
"query": """
FROM .alerting-actions
| WHERE action_type IN ("ack", "unack", "deactivate", "activate", "snooze", "tag", "unsnooze")
| STATS
tags = LAST(tags, @timestamp) WHERE action_type IN ("tag"),
last_ack_action = LAST(action_type, @timestamp) WHERE action_type IN ("ack", "unack"),
last_deactivate_action = LAST(action_type, @timestamp) WHERE action_type IN ("deactivate", "activate"),
last_snooze_action = LAST(action_type, @timestamp) WHERE action_type IN ("snooze", "unsnooze")
BY episode_id, rule_id, group_hash
| KEEP episode_id, rule_id, group_hash, last_ack_action, last_deactivate_action, last_snooze_action, tags"""
}
```
---------
Co-authored-by: Jason Rhodes <jason.matthew.rhodes@gmail.com>
commit 7830d9b3a1bd3881ca2389402f591f9d0965b9b0
Author: Umberto Pepato <umbopepato@users.noreply.github.com>
Date: Mon Mar 23 17:05:08 2026 +0100
[ResponseOps][RnA] Minimal alerting episodes o11y page (#257638)
Implements a minimal alerting episodes page in Observability,
introducing the foundational UI and data fetching infrastructure for
viewing alerting episodes.
- Creates a new package `@kbn/alerting-v2-episodes-ui` with reusable
components, hooks, and utilities for fetching and displaying alerting
episodes
- Adds new Observability navigation item and route for the alerting
episodes page (`/app/observability/alerts_v2`)
- Implements episode status badge component for visualizing episode
states
- Adds ES|QL-based data fetching with support for pagination
- Extends rules client to support bulk-fetching rules by ids
> [!important]
> This minimal initial implementation is based on initial requirements.
The final design is still WIP and will be integrated when ready either
here or in a separate PR.
<details>
<summary>
</summary>
Add the following flags to your `kibana.dev.yml` to enable alerting v2:
```yaml
xpack.alerting_v2.ui.enabled: true
xpack.alerting_v2.enabled: true
```
To generate test alert events you can:
- From the Stack Management → Rules v2 page, create one or more rules
that target a test dataset (i.e. Kibana Sample Web Logs TSDB)
- Seed a large number of test alerts (feel free to reach out if you
don't have an easy way to do so), this would be preferable to test
pagination and multiple episodes states.
1. Navigate to Observability → Alerts v2 in the side navigation
1.1. Verify that the page shows both in Classic space layout
1.2. and in Observability space layout, in this case in a sub-menu of
the Alerts sidebar entry
3. Verify the alerts episodes table loads with data
4. Verify episode status badges display correctly with appropriate
colors
5. Test "load more" pagination through episodes when scrolling to bottom
1. Navigate to the page when no episodes exist - verify appropriate
empty state or loading behavior
2. Test with a large number of episodes to verify pagination works
correctly
</details>
<details>
<summary>
</summary>
The Load more button appears only when scrolling to the bottom of the
table, and overflows the container when visible. I'm working to
understand if I can improve this without changes in Discover's side.
The ES|QL queries for the episodes are currently specified in extended
form, while waiting for @adcoelho's [episodes ES|QL view
PR](https://github.com/elastic/kibana/pull/257347) to be merged. Once
that is available, I'll integrate it here or in a separate PR.
Names of alerting v2 indices and views are being changed, that will need
to be integrated here too.
My intention was to leverage the new DI subsytem to load rule fetching
hooks directly from the alerting-v2 plugins, but whilte testing that
approach I realized that there was a bundle separation problem, which is
being addressed in [this
PR](https://github.com/elastic/kibana/pull/257432/changes). Once that's
merged, I'll revisit this approach and see if I can avoid hooks
proliferation outside of the main plugin, a problem we faced many times
in alerting v1.
</details>
<details>
<summary>
</summary>
The AlertsTable v1 ended up being perhaps one of the most complex
EuiDataGrid abstractions in the whole Kibana after Discover. Exposing
smaller components and utilities and relying more on Discover primitives
like `UnifiedDataTable` and its well-validated existing functionality
will allow us to:
- give users a more consistent experience (we should be able to share
cell renderers and other customizations with Discover too by means of
profiles),
- avoid rewriting a Discover-like experience from scratch (with the huge
complexity it brings),
- not restrict the Solution teams from customizing their alerting
experience.
Created a shared package containing:
- **Components**: `AlertingEpisodeStatusBadge` for displaying episode
status
- **Hooks**:
- `useFetchAlertingEpisodesQuery` - React Query hook for fetching
episodes via ES|QL
- `useAlertingEpisodesDataView` - Creates a data view for episodes data
- `useAlertingRulesIndex` - Fetches the alerting rules index pattern
- **APIs**: `fetchAlertingEpisodes` - Core fetching logic using ES|QL
- **Utils**: ES|QL query execution and data transformation utilities
- Added new navigation item for "Alerts (Episodes)" in the Observability
side nav
- Created `/app/observability/alerts_v2` route with basic table
implementation
- Integrated with existing Observability plugin infrastructure
</details>
<details>
<summary>
</summary>
<img width="1319" height="831" alt="image"
src="https://github.com/user-attachments/assets/0f8f0df3-3158-4e8f-87f2-796883b8bc43"
/>
</details>
Not backporting since this targets a feature branch
Closes #255737
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
commit 26cd9d9f3e6516580d32bbf718a9b554db299001
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Mon Mar 23 10:55:30 2026 -0400
feat(alertingv2): update list notification policies table (#259114)
Resolves https://github.com/elastic/rna-program/issues/202
- Reorder column
- Add description below name
- use text colour for icon
<img width="2426" height="571" alt="image"
src="https://github.com/user-attachments/assets/d66710f3-ca20-4035-95d6-c02fb75af50f"
/>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit 384ab3d9bd90365f5ac7bc4ad5a745f4dccb1336
Merge: 4d035ff452d6 01a86f3abbd8
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Mon Mar 23 14:52:48 2026 +0100
Merge remote-tracking branch 'upstream/main' into alerting_v2
commit 4d035ff452d6af8dc86ccd8b793c319086f03036
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Mon Mar 23 09:03:12 2026 -0400
Fix page reset (#258927)
Upon clicking on the next page on the notification policy table, we were
instantly redirected to the first page. Using useCallback on the search
and filter functions to prevent this from happening.
- Create at least 11 notification policies (1 then 10 clone)
- Select 10 per page
- Click on page 2
commit f15cffaf3623d0d503b27430ce697a58ea7d6d80
Author: Anna Davydova <ana.davydova@elastic.co>
Date: Mon Mar 23 11:08:32 2026 +0100
[Alerting V2] Add basic search to the rule list (#258906)
Closes [#256554](https://github.com/elastic/kibana/issues/256554)
This PR improves search in the alerting_v2 rules list adding a`
<EuiSearchBar>`
- adds debounced search input handling in the rules list UI
- updates the rules list query flow to include the search term in the
request/query key
- implements server-side prefix search for both rule names and labels
- combines search with any existing list query conditions in a single
Saved Objects query
- extracts the server-side search query builder into a dedicated helper
for readability and testability
- adds focused unit and API integration test coverage for the new search
behavior
**Search behavior**
- matches rule name prefixes, for example 'lim' matching 'Limit120'
- matches label prefixes, for example 'prod' matching a label like
'production'
- supports multiple search terms
- safely escapes user input before building the underlying KQL query
**Implementation details**
**Frontend:**
- added search state and debounce handling on the rules list page
- includes the search term in the fetch hook and React Query key
- avoids showing stale list results while the search term changes
**Backend:**
- stopped relying on Saved Objects native search alone for this flow
- builds a dedicated rules-list search query using:
`metadata.name: term*`
OR `metadata.labels: term*`
- combines the generated search query with any existing rule list filter
- extracted this logic into `build_rule_search.ts`
**Tests**
- debounced search behavior in the rules list UI
- query key / fetch parameter updates
- server-side search query construction
- combining search with an existing filter
- list-rules API behavior for name-prefix and label-prefix matches
<img width="1251" height="506" alt="image"
src="https://github.com/user-attachments/assets/c375bf04-c50b-4c6b-b0f9-effb8e27517b"
/>
<img width="1241" height="371" alt="image"
src="https://github.com/user-attachments/assets/1afd8663-ff0b-4281-b9be-843de45788cd"
/>
<img width="1244" height="453" alt="image"
src="https://github.com/user-attachments/assets/c631958d-edcf-455f-a9d1-a848612a2530"
/>
1. Enable:
```
xpack.alerting_v2.ui.enabled: true
xpack.alerting_v2.enabled: true
```
2. Start Kibana and open Discover in ES|QL mode.
3. Open V2 create a couple of rules.
4. Navigate to the list_rules Page and use searchbar.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
commit c0b099c6a170d79d509c88acd9a082d91f2b1491
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Fri Mar 20 22:27:27 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit 22b2860f7ae518e1eeaa5bf7ecdecd55c9619567
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Fri Mar 20 23:06:35 2026 +0100
[Alerting v2] Fix: scope latest alert state query to director-processed events (#258774)
- Fix director query (queries.ts): Filter on type == "alert" AND
episode.status IS NOT NULL so only director-processed events are
considered when looking up previous episode state. Without this, raw
signal events (which have no episode) could pollute LAST(episode.status,
@timestamp) and return null, causing the director to treat every
execution as the first run and endlessly emit pending.
- Fix pipeline recovery path (create_alert_events_step.ts,
execute_rule_query_step.ts): Ensure pipeline steps always `yield { type:
'continue' }` with an empty batch when no results are produced, instead
of yielding nothing. Previously, when no groups breached,
`CreateAlertEventsStep` would not yield, starving downstream steps
`CreateRecoveryEventsStep` never ran, so recovery events were never
generated.
- Fix `groupHashValues` in the recovery step to correctly resolve group
hashes for recovery event creation.
- Add unit tests for getLatestAlertEventStateQuery in the director,
verifying query structure, parameter binding, column selection, and
filtering.
- Add Scout API e2e tests for the episode lifecycle, covering:
- pending → active transition when source data keeps breaching
- active → inactive transition when source data stops breaching
(recovery)
- Independent multi-group tracking with partial recovery
- pending_count threshold delaying the pending → active transition
commit 294d9e575d022f6f7798720be4c9843326d3af8a
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Fri Mar 20 15:28:58 2026 -0400
[Alerting v2] Add bulk actions for notification policies (#258862)
commit 10ce1ab42574c39c77df7b8770d67c436258562c
Author: Anna Davydova <ana.davydova@elastic.co>
Date: Fri Mar 20 17:15:42 2026 +0100
Alertingv2 add runbook (#256492)
Closes #255650
This PR created an Attachments field in Create Rule flyout and has all
the needed ui components.
Right now there's no API, so I just added the fields and logic as it
should work.
**New runbook modal field component**
Implemented runbook support in the Alerting v2 rule form attachments
section by adding a dedicated metadata.runbook field, wiring
add/edit/delete flows through a markdown modal, and rendering saved
runbook summaries from the first non-empty line. Updated the attachments
UI to use a split-panel, collapsible container (closed by default), and
added unit tests for both attachment group interactions and runbook
modal save/cancel/reopen behavior.
<img width="379" height="650" alt="image"
src="https://github.com/user-attachments/assets/e7715f2e-24ce-44b3-a76f-fd6773cec6f9"
/>
<img width="378" height="662" alt="image"
src="https://github.com/user-attachments/assets/2899014f-1bba-4bd4-b863-6ae76c1c0003"
/>
<img width="815" height="499" alt="image"
src="https://github.com/user-attachments/assets/e226720a-d865-4f3e-a0ec-859aa37c2c2f"
/>
<img width="754" height="471" alt="image"
src="https://github.com/user-attachments/assets/f8e3f73e-5590-4cd3-ab56-8f38207edfff"
/>
<img width="384" height="192" alt="image"
src="https://github.com/user-attachments/assets/8376bfb1-bd05-43ba-bc12-9b54edc877a7"
/>
Also, this PR updates `create/update/response` schemas, saved object
mappings/model versions, and rules-client transforms to carry
`artifacts` end-to-end. It also wires rule form mappers/UI/YAML flows to
read/write runbook through `artifacts` (`type: "runbook"`) with
generated artifact ids and adds/updates tests to cover request mapping,
response mapping, and YAML serialization/parsing.
1. Enable:
```
xpack.alerting_v2.ui.enabled: true
xpack.alerting_v2.enabled: true
```
2. Start Kibana and open Discover in ES|QL mode.
3. Open V2 create rule flow.
4. Verify:
- there is an attachments component with a button 'Add Runbook'
- after pressing the button you can see the module to write a runbook
- save the runbook
- hit save the rule
The `artifacts` field can also be tested through devtools.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
commit 1f56de457ff4a4b19e7204cf03a7e7dbd0f1f8bc
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Fri Mar 20 15:05:16 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit 40b3b6b632600aef39349474a2831b7147967508
Merge: 06c700d54be4 575834023d80
Author: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Fri Mar 20 15:43:32 2026 +0100
Merge remote-tracking branch 'upstream/main' into alerting_v2
commit 06c700d54be41a6f6a6be25bb7975007c9516ef0
Author: Kevin Delemme <kevin.delemme@elastic.co>
Date: Fri Mar 20 10:34:31 2026 -0400
[Alerting v2] align notification policy casing to camelCase (#258237)
commit 180fe6e0d5d638ea5a5fdbc4b54c2c15f44258b1
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Fri Mar 20 01:03:32 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit 523d712caa1c0f84d146f8d5474a1bf589930e05
Author: Dominique Clarke <dominique.clarke@elastic.co>
Date: Thu Mar 19 20:43:55 2026 -0400
[Alerting v2] rule form - preview chart (#257324)
Adds a Lens-powered bar chart histogram to the rule preview panels in
the alerting v2 rule form. The chart shows the count of matching rows
over time for both the **rule evaluation** and **recovery** previews,
giving users immediate visual feedback on the temporal distribution of
their query results.
<img width="829" height="728" alt="Screenshot 2026-03-11 at 9 51 01 PM"
src="https://github.com/user-attachments/assets/ef0b58af-3db8-4254-9e8c-81f5147bd43a"
/>
- Renders a Lens embeddable bar chart inside the existing preview panel,
above the data grid
- X-axis: time (auto-bucketed using ES|QL's `BUCKET` function with a
target of ~20 bars)
- Y-axis: count of matching rows per bucket
- Accepts `query`, `timeField`, and `lookback` as props, making it
reusable across both rule and recovery previews
- Renders only chart content (no panel wrapper) — layout is managed by
the parent `QueryResultsGrid`
- Shows a loading spinner while Lens attributes are being built, an
error callout on failure, and returns `null` when inputs are
insufficient
- Builds an ES|QL chart query from the base query, time field, and
lookback window
- Constructs an absolute `WHERE` time filter and uses `BUCKET(timeField,
20, start, end)` for auto-interval bucketing
- Validates the base query via `validateEsqlQuery` before augmenting it
- Builds Lens attributes asynchronously via `LensConfigBuilder`
- Debounces the query input (2s) to stay in sync with the preview grid
- Properly handles cancellation to avoid stale state updates
- Now returns `query`, `timeField`, and `lookback` alongside existing
result fields, enabling downstream components to pass them to the chart
- Accepts optional `query`, `timeField`, and `lookback` props
- Internally renders `PreviewChart` above the data grid when results are
available (`rows.length > 0`)
- Displays a read-only time range label (e.g. "Last 5m") in the panel
header, to the right of the title
- Consolidates the empty/error/no-results states into a single indicator
(the chart is hidden when there are no results)
- Pass `query`, `timeField`, and `lookback` from their respective hooks
through to `QueryResultsGrid`
- Both previews now show the chart when results are available
- Added comprehensive unit tests for `PreviewChart` and
`usePreviewChart`
- Updated existing tests for `QueryResultsGrid`, `RuleResultsPreview`,
`RecoveryResultsPreview`, and `RulePreviewPanel` to mock the new chart
component and include the new `query`/`timeField`/`lookback` fields in
mock data
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.
Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.
Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.
- **Low**: The chart executes a separate ES|QL aggregation query
(independent from the grid query). This means two queries run per
preview. Mitigated by debouncing both queries with the same 2s delay and
only executing when inputs are valid.
- **Low**: `LensConfigBuilder.build()` is async and could fail for
unexpected query shapes. Mitigated by catching errors gracefully and
showing an error callout instead of breaking the form.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
commit 2bbff6e6b1467ee3b74be2d9647b4e14a2537a8e
Author: Dominique Clarke <dominique.clarke@elastic.co>
Date: Thu Mar 19 20:32:19 2026 -0400
[Alerting v2] rule list bulk enable disable delete select all (#258341)
Adds bulk operations (enable, disable, delete) and "select all"
functionality to the Alerting V2 rules list.
[Alerting V2 -
Elastic.webm](https://github.com/user-attachments/assets/b3541924-8db6-4d10-ae8f-4a62b9a0e618)
**Frontend**
- **Refactored `RulesListPage`**: Extracted the table rendering, row
actions, and selection logic into two new components:
- `RulesListTable` — a presentational component responsible for
rendering the table with selection checkboxes, bulk action toolbar, and
per-row action menus.
- `RulesListTableContainer` — a stateful container that wires up hooks
for bulk selection, delete/enable/disable mutations, and single-rule
actions (edit, clone, delete, toggle enabled).
- **`useBulkSelect` hook**: Manages selection state using a reducer
pattern with support for:
- Individual row selection (inclusion set)
- Page-level select/deselect via header checkbox
- "Select all" across all pages (switches to an exclusion-set model)
- Clear selection
- `getBulkParams()` — returns the appropriate `BulkOperationParams` (`{
ids }` for explicit selection, `{ filter }` for select-all with optional
exclusions)
- **`useBulkDeleteRules` / `useBulkEnableRules` / `useBulkDisableRules`
hooks**: React Query `useMutation` wrappers that call the corresponding
API endpoints, show success/warning/error toasts, and invalidate the
rule list cache on success.
- **`DeleteConfirmationModal`**: Extended to support a bulk delete mode
(`ruleCount` prop) that shows "Delete N rules" instead of the
single-rule name. Uses a discriminated union for props to enforce that
either `ruleName` (single) or `ruleCount` (bulk) is provided.
- **`RulesApi` service**: Added `bulkDeleteRules`, `bulkEnableRules`,
and `bulkDisableRules` methods along with `BulkOperationParams` and
`BulkOperationResponse` types.
- **Query key factory**: Added `bulkDelete`, `bulkEnable`, and
`bulkDisable` mutation keys.
**Backend**
- **3 new API routes** (`_bulk_delete`, `_bulk_enable`,
`_bulk_disable`): Internal POST endpoints under
`/internal/alerting_v2/rule/` that accept a request body validated by
`bulkOperationParamsSchema`. Each requires the `rules.write` privilege.
- **`bulkOperationParamsSchema`** (Zod, in `@kbn/alerting-v2-schemas`):
Validates that exactly one of `ids` (array of 1–100 strings) or `filter`
(KQL string) is provided.
- **`RulesClient` bulk methods** (`bulkDeleteRules`, `bulkEnableRules`,
`bulkDisableRules`):
- Use a shared `resolveRuleIds` method that either returns the provided
IDs directly or paginates through all rules matching the filter.
- `bulkDelete`: Removes Task Manager tasks (best-effort), then
bulk-deletes saved objects.
- `bulkEnable`: Fetches rules, skips already-enabled ones, bulk-updates
the rest, and schedules executor tasks for newly enabled rules.
- `bulkDisable`: Fetches rules, skips already-disabled ones,
bulk-updates the rest, and disables executor tasks.
- All methods return `{ rules, errors }` with per-rule error reporting.
- **`buildRuleSoFilter`**: A KQL AST rewriter that translates clean API
field names (e.g. `id`, `kind`, `enabled`, `metadata.name`) to their
saved-object KQL paths (e.g. `alerting_rule.attributes.kind`). Validates
that only allowed fields are used and throws on unknown fields.
- **`RulesSavedObjectService`**: Added `bulkUpdate`, `bulkDelete`, and
`filter` support to `find`.
- **`findRules`**: Now accepts an optional `filter` parameter (also
exposed on the `GET /rules` query string), passing it through
`buildRuleSoFilter` before querying.
- Unit tests for `useBulkSelect`, `useBulkDeleteRules`,
`useBulkEnableRules`, `useBulkDisableRules` hooks
- Unit tests for `RulesListTable` (presentational) and
`RulesListTableContainer` (integration with hooks)
- Extended `RulesListPage` tests for selection, bulk action menu, and
bulk operations
- Unit tests for `DeleteConfirmationModal` bulk mode
- Extensive `RulesClient` tests covering bulk delete/enable/disable with
IDs and filters, pagination, error handling, and the `resolveRuleIds`
validation
- Unit tests for `buildRuleSoFilter` covering field mapping, compound
expressions, validation, and edge cases
- API integration test file added
Check the PR satisfies following conditions.
Reviewers should verify this PR satisfies this list as well.
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [x] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.
- **Bulk delete is irreversible**: Once confirmed, rules and their
associated tasks are permanently removed. Mitigation: A confirmation
modal shows the count and requires explicit confirmation.
- **Filter-based operations may affect a large number of rules**: When
"Select all" is used, the backend paginates through all matching rules.
For very large rule sets this could be slow. Mitigation: The `ids` path
has a 100-rule cap; the `filter` path processes in pages of 100.
- **Task Manager failures are non-fatal**: If task removal/disable
fails, the saved object changes still proceed. This means a rule's SO
state and task state could briefly diverge. Mitigation: Errors are
logged; task state will reconcile on next scheduler check.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
commit 924a9e6e22df10d48604d66600f9440182daf9db
Author: adcoelho <antonio.coelho@elastic.co>
Date: Thu Mar 19 21:20:32 2026 +0100
Fix types.
commit aad963648f56ea114869a4009f6cd0d7b251fe21
Author: Antonio <antonio.coelho@elastic.co>
Date: Thu Mar 19 20:48:32 2026 +0100
[Alerting V2] bulk get alert actions (#258353)
Closes https://github.com/elastic/kibana/issues/258317
The alert episodes table needs to display episode status for each row.
To build that UI, we needed a bulk-get API for alert actions.
<details> <summary>Start by posting some mock action data.</summary>
```
POST .alerting-actions/_bulk
{"create":{}}
{"@timestamp":"2026-03-18T08:00:00.000Z","last_series_event_timestamp":"2026-03-18T07:55:00.000Z","actor":"user-1","action_type":"ack","group_hash":"gh-1","episode_id":"ep-001","rule_id":"rule-1"}
{"create":{}}
{"@timestamp":"2026-03-18T08:10:00.000Z","last_series_event_timestamp":"2026-03-18T07:55:00.000Z","actor":"user-1","action_type":"snooze","group_hash":"gh-1","episode_id":"ep-001","rule_id":"rule-1"}
{"create":{}}
{"@timestamp":"2026-03-18T08:30:00.000Z","last_series_event_timestamp":"2026-03-18T07:55:00.000Z","actor":"user-2","action_type":"deactivate","group_hash":"gh-2","episode_id":"ep-002","rule_id":"rule-1","reason":"Known maintenance window"}
{"create":{}}
{"@timestamp":"2026-03-18T08:45:00.000Z","last_series_event_timestamp":"2026-03-18T07:55:00.000Z","actor":"user-2","action_type":"ack","group_hash":"gh-2","episode_id":"ep-002","rule_id":"rule-1"}
{"create":{}}
{"@timestamp":"2026-03-18T09:00:00.000Z","last_series_event_timestamp":"2026-03-18T08:50:00.000Z","actor":"user-1","action_type":"ack","group_hash":"gh-3","episode_id":"ep-003","rule_id":"rule-2"}
{"create":{}}
{"@timestamp":"2026-03-18T09:15:00.000Z","last_series_event_timestamp":"2026-03-18T08:50:00.000Z","actor":"user-1","action_type":"unack","group_hash":"gh-3","episode_id":"ep-003","rule_id":"rule-2"}
{"create":{}}
{"@timestamp":"2026-03-18T09:30:00.000Z","last_series_event_timestamp":"2026-03-18T09:20:00.000Z","actor":"user-3","action_type":"snooze","group_hash":"gh-4","episode_id":"ep-004","rule_id":"rule-2"}
{"create":{}}
{"@timestamp":"2026-03-18T09:50:00.000Z","last_series_event_timestamp":"2026-03-18T09:20:00.000Z","actor":"user-3","action_type":"unsnooze","group_hash":"gh-4","episode_id":"ep-004","rule_id":"rule-2"}
{"create":{}}
{"@timestamp":"2026-03-18T10:00:00.000Z","last_series_event_timestamp":"2026-03-18T09:50:00.000Z","actor":"user-2","action_type":"deactivate","group_hash":"gh-5","episode_id":"ep-005","rule_id":"rule-3","reason":"Duplicate alert"}
{"create":{}}
{"@timestamp":"2026-03-18T10:20:00.000Z","last_series_event_timestamp":"2026-03-18T09:50:00.000Z","actor":"user-1","action_type":"activate","group_hash":"gh-5","episode_id":"ep-005","rule_id":"rule-3","reason":"Re-enabled after investigation"}
{"create":{}}
{"@timestamp":"2026-03-18T10:30:00.000Z","last_series_event_timestamp":"2026-03-18T10:25:00.000Z","actor":"user-1","action_type":"ack","group_hash":"gh-6","episode_id":"ep-006","rule_id":"rule-3"}
{"create":{}}
{"@timestamp":"2026-03-18T10:45:00.000Z","last_series_event_timestamp":"2026-03-18T10:25:00.000Z","actor":"user-1","action_type":"snooze","group_hash":"gh-6","episode_id":"ep-006","rule_id":"rule-3"}
{"create":{}}
{"@timestamp":"2026-03-18T10:55:00.000Z","last_series_event_timestamp":"2026-03-18T10:25:00.000Z","actor":"user-2","action_type":"deactivate","group_hash":"gh-6","episode_id":"ep-006","rule_id":"rule-3","reason":"Root cause fixed"}
{"create":{}}
{"@timestamp":"2026-03-18T11:00:00.000Z","last_series_event_timestamp":"2026-03-18T10:55:00.000Z","actor":"user-3","action_type":"ack","group_hash":"gh-7","episode_id":"ep-007","rule_id":"rule-4"}
{"create":{}}
{"@timestamp":"2026-03-18T11:30:00.000Z","last_series_event_timestamp":"2026-03-18T11:20:00.000Z","actor":"user-2","action_type":"snooze","group_hash":"gh-8","episode_id":"ep-008","rule_id":"rule-4"}
{"create":{}}
{"@timestamp":"2026-03-18T11:45:00.000Z","last_series_event_timestamp":"2026-03-18T11:20:00.000Z","actor":"user-2","action_type":"ack","group_hash":"gh-8","episode_id":"ep-008","rule_id":"rule-4"}
{"create":{}}
{"@timestamp":"2026-03-18T12:00:00.000Z","last_series_event_timestamp":"2026-03-18T11:50:00.000Z","actor":"user-1","action_type":"deactivate","group_hash":"gh-9","episode_id":"ep-009","rule_id":"rule-5","reason":"Alert storm - suppressing"}
{"create":{}}
{"@timestamp":"2026-03-18T12:10:00.000Z","last_series_event_timestamp":"2026-03-18T11:50:00.000Z","actor":"user-1","action_type":"snooze","group_hash":"gh-9","episode_id":"ep-009","rule_id":"rule-5"}
{"create":{}}
{"@timestamp":"2026-03-18T12:30:00.000Z","last_series_event_timestamp":"2026-03-18T12:20:00.000Z","actor":"user-3","action_type":"ack","group_hash":"gh-10","episode_id":"ep-010","rule_id":"rule-5"}
{"create":{}}
{"@timestamp":"2026-03-18T12:40:00.000Z","last_series_event_timestamp":"2026-03-18T12:20:00.000Z","actor":"user-3","action_type":"unack","group_hash":"gh-10","episode_id":"ep-010","rule_id":"rule-5"}
{"create":{}}
{"@timestamp":"2026-03-18T12:50:00.000Z","last_series_event_timestamp":"2026-03-18T12:20:00.000Z","actor":"user-3","action_type":"ack","group_hash":"gh-10","episode_id":"ep-010","rule_id":"rule-5"}
```
</details>
There are up to 10 episodes with actions, all with ids like `ep-001`.
Query the new route and confirm that the results are as expected.
```
POST kbn:/internal/alerting/v2/alerts/action/_bulk_get
{
"episode_ids": ["ep-001", "ep-002", "ep-003", "foobar"]
}
```
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
commit fa3dd0b998948fddda8d0afe0e699528520e140b
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Thu Mar 19 18:57:24 2026 +0000
Changes from node scripts/regenerate_moon_projects.js --update
commit 6817e2feb9acff21b381b680c8ab3fa2c5682de7
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Thu Mar 19 18:57:16 2026 +0000
Changes from node scripts/generate codeowners
commit ca5c75c2ffa70526664415c0ee18062208876bd0
Author: Jason Rhodes <jason.rhodes@elastic.co>
Date: Thu Mar 19 14:31:53 2026 -0400
Change v2 package owners to rna team (#258551)
We changed the main v2 alerting plugin previously but the packages we
use in v2, which are fully v2 only, are still owned by response ops.
This creates review bottlenecks for the rna project that we can
alleviate by using the rna-project-team owner while we are in active
development.
commit 76b8a03a577f1ff47113435c91fdbf821ad178bf
Author: Yiannis Nikolopoulos <yiannis.nikolopoulos@elastic.co>
Date: Thu Mar 19 17:18:23 2026 +0200
[Alerting v2] [Rule authoring] Add max value validation to recovering_count (#258413)
Applies the missing `.max(MAX_CONSECUTIVE_BREACHES)` constraint to the
`recovering_count` field in the server-side Zod schema, closing a
validation gap where a direct API call could submit an arbitrarily large
value that would pass server-side validation undetected.
**Problem:** `pending_count` correctly enforced
`.max(MAX_CONSECUTIVE_BREACHES)` (1000) in both the client form
validation and the server-side schema. `recovering_count` had the same
client-side max enforced, but the server-side schema was missing
`.max(MAX_CONSECUTIVE_BREACHES)`, meaning the constraint could be
bypassed entirely via a direct API call.
**Solution:** Add `.max(MAX_CONSECUTIVE_BREACHES)` to the
`recovering_count` field in `stateTransitionSchema` in
`rule_data_schema.ts`, making it consistent with `pending_count`. The
fix applies to both `createRuleDataSchema` and `updateRuleDataSchema`
since both share the same `stateTransitionSchema` definition.
- [x] Unit tests pass for all modified files (80 tests)
- [x] New test: `rejects recovering_count greater than 1000` added to
`createRuleDataSchema` suite
- [x] New test: `rejects recovering_count greater than 1000` added to
`updateRuleDataSchema` suite
commit c0fd6038d8b8d276f220bb64d0c5fa983aa5003c
Author: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Date: Wed Mar 18 16:00:38 2026 +0000
Changes from node s…
Closes #258317
Summary
The alert episodes table needs to display episode status for each row.
To build that UI, we needed a bulk-get API for alert actions.
Testing
Start by posting some mock action data.
There are up to 10 episodes with actions, all with ids like
ep-001.Query the new route and confirm that the results are as expected.