Skip to content

[Alerting V2] bulk get alert actions#258353

Merged
adcoelho merged 6 commits intoelastic:alerting_v2from
adcoelho:alerting-v2-bulk-get-alert-actions
Mar 19, 2026
Merged

[Alerting V2] bulk get alert actions#258353
adcoelho merged 6 commits intoelastic:alerting_v2from
adcoelho:alerting-v2-bulk-get-alert-actions

Conversation

@adcoelho
Copy link
Copy Markdown
Contributor

@adcoelho adcoelho commented Mar 18, 2026

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.
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"}

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"]
}

@adcoelho adcoelho self-assigned this Mar 18, 2026
@adcoelho adcoelho added the Team:ResponseOps Platform ResponseOps team (formerly the Cases and Alerting teams) t// label Mar 18, 2026
@adcoelho adcoelho marked this pull request as ready for review March 18, 2026 13:51
@adcoelho adcoelho requested review from a team as code owners March 18, 2026 13:51
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/response-ops (Team:ResponseOps)

@adcoelho adcoelho force-pushed the alerting-v2-bulk-get-alert-actions branch from 08e9e28 to 2989159 Compare March 18, 2026 13:56
@kdelemme
Copy link
Copy Markdown
Contributor

Why introducing a _bulk_get? Couldn't we have a filter on GET /alerts/actions?episode_ids=[] ?

@kdelemme kdelemme requested a review from Copilot March 19, 2026 14:24
@kdelemme kdelemme added the reviewer:coderabbit PR review with CodeRabbit label Mar 19, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +23 to +41
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;
Copy link
Copy Markdown
Contributor

@kdelemme kdelemme left a comment

Choose a reason for hiding this comment

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

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: {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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>
@adcoelho adcoelho merged commit aad9636 into elastic:alerting_v2 Mar 19, 2026
10 of 14 checks passed
@elasticmachine
Copy link
Copy Markdown
Contributor

elasticmachine commented Mar 19, 2026

💔 Build Failed

Failed CI Steps

Metrics [docs]

‼️ ERROR: no builds found for mergeBase sha [04ac41c]

History

cc @adcoelho

@adcoelho
Copy link
Copy Markdown
Contributor Author

Why introducing a _bulk_get? Couldn't we have a filter on GET /alerts/actions?episode_ids=[]?

@kdelemme

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.

ana-davydova added a commit to ana-davydova/kibana that referenced this pull request Mar 30, 2026
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…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewer:coderabbit PR review with CodeRabbit Team:ResponseOps Platform ResponseOps team (formerly the Cases and Alerting teams) t//

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants