Skip to content

Gap reason UI#260095

Merged
nkhristinin merged 60 commits intoelastic:mainfrom
nkhristinin:gap-reason-ui
Apr 2, 2026
Merged

Gap reason UI#260095
nkhristinin merged 60 commits intoelastic:mainfrom
nkhristinin:gap-reason-ui

Conversation

@nkhristinin
Copy link
Copy Markdown
Contributor

@nkhristinin nkhristinin commented Mar 27, 2026

Gap Reason UI

Overview

This PR adds API-level filtering by gap reason and the Rule Settings UI for controlling which gap reasons are included in gap monitoring and auto-fill. The gap reason detection logic and the "Reason" column in the gaps table were shipped in a previous PR (#258231).

CleanShot.2026-03-29.at.20.48.11.mp4

Feature Flag

Gated behind gapReasonDetectionEnabled (default: false). When disabled:

  • The "Include disabled gaps" checkbox is hidden from the Rule Settings modal
  • API filtering by excludedReasons still works at the schema level but has no practical effect since no reasons are written

Changes

Rule Settings Modal

  • New "Gap detection scope" section with a checkbox to include/exclude gaps caused by disabled rules (hidden when feature flag is off)
  • Saves excludedReasons to both the gap auto-fill scheduler saved object and securitySolution:excludedGapReasons UI setting. The value is stored in two places because the gap auto-fill scheduler can be available for people with free tiers.

Bulk Fill Modal

  • Shows an info callout when rule_disabled gaps are excluded: "Gaps caused by disabled rules will not be filled. You can change this in Rule Settings."

Gap table

Add reason filter which by default get values from the rule setting modal

Screenshot 2026-04-01 at 13 31 44

API Filtering

  • getRuleIdsWithGaps — accepts excluded_reasons parameter to filter out gaps by reason type
  • getGapsSummaryByRuleIds — accepts excluded_reasons parameter for summary calculations
  • findRules route — reads EXCLUDED_GAP_REASONS_KEY from UI settings and passes it to gap filtering
  • Bulk fill gaps — respects excludedReasons when scheduling gap fills
  • Gap auto-fill scheduler — stores and applies excludedReasons (persisted in saved object)
  • buildGapsFilter — extended to support reason-based filtering in ES queries

UI Settings

  • securitySolution:excludedGapReasons — new advanced setting (readonly, array type) controlling which gap reasons are excluded from monitoring and auto-fill. Default: ['rule_disabled']

How to Test

Prerequisites

  1. Enable the feature flag in kibana.dev.yml:
    xpack.securitySolution.enableExperimental: ['gapReasonDetectionEnabled']

Test 1: rule_disabled reason

  1. Create a detection rule with 1 minute interval and 1 second lookback
  2. Enable the rule and let it run successfully at least once
  3. Disable the rule
  4. Wait 5 minutes
  5. Enable the rule
  6. Go to the rule details page → Gaps tab
  7. Expected: A gap appears with reason "Rule was disabled"

Test 2: rule_did_not_run reason (Kibana downtime)

  1. Create a detection rule with 1 minute interval and 1 second lookback
  2. Enable the rule and successfully at least once
  3. Kill Kibana (stop the process)
  4. Wait 5 minutes
  5. Start Kibana again
  6. Go to the rule details page → Gaps tab
  7. Expected: A gap appears with reason "Rule did not run"

@nkhristinin
Copy link
Copy Markdown
Contributor Author

/ci

@nkhristinin
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@elasticmachine
Copy link
Copy Markdown
Contributor

There are no new commits on the base branch.

@nkhristinin
Copy link
Copy Markdown
Contributor Author

/ci

jeramysoucy pushed a commit to jeramysoucy/kibana that referenced this pull request Apr 1, 2026
…mportable/exportable SO types (elastic#260280)

## Summary

Closes elastic/kibana-team#3162

The \`validateNameTitleFieldTypes\` check in
\`kbn-check-saved-objects-cli\` enforces that any SO type with a
\`name\` or \`title\` mapping field must use \`type: text\`, citing
Search API compatibility. There are two distinct cases where the check
was incorrect:

### Case 1: Types not searchable via the management page

The check was applied unconditionally to all SO types, but the Saved
Objects management page search only queries types where
\`management.importableAndExportable === true\`. The registry treats the
absence of that flag as \`false\`, so types without it set are never
included in management search and the full-text requirement does not
apply to them.

Flagging such types also creates pressure to change \`keyword\` →
\`text\`, which is a **breaking Elasticsearch mapping change** (reindex
required) that is entirely unnecessary.

Note: the \`hidden\` flag alone is **not** the right exemption criterion
— the management find route explicitly includes hidden types that are
also \`importableAndExportable: true\` (via \`includedHiddenTypes\`), so
those remain subject to the check.

### Case 2: Pre-existing keyword fields from shipped model versions

Even for importable/exportable types, if \`name\` or \`title\` was
already shipped as \`keyword\` in a previous model version, **there is
nothing the developer can do to fix it** without a full reindex. Failing
the CI check in this case blocks the PR with no actionable resolution.

## Changes

- Split \`validateNameTitleFieldTypes\` into two focused functions:
- \`validateNameTitleFieldTypesNewType\` (in \`new_type_utils.ts\`) —
throws for any wrong field type on importable/exportable types; no
baseline exists for new types so the full error always applies
- \`validateNameTitleFieldTypesExistingType\` (in
\`existing_type_utils.ts\`) — warns for pre-existing wrong field types,
fails only for newly introduced ones
- Shared helpers \`getInvalidNameTitleFields\` and
\`isSearchableViaManagement\` extracted to \`common_utils.ts\`
- Exemption condition uses \`!== true\` (not \`=== false\`) to also
cover types where \`management\` is absent, matching the registry's \`??
false\` default
- \`createMockType\` in tests now defaults to \`importableAndExportable:
true\` so the positive (participates-in-management-search) path is
exercised correctly
- Tests updated: exemption test covers \`importableAndExportable:
false\`; a new test confirms that hidden types with
\`importableAndExportable: true\` are still subject to the check

## Context

This was first surfaced by
elastic#260095, which adds model version
2 to \`gap_auto_fill_scheduler\` — a type with
\`importableAndExportable\` not set to \`true\` and a pre-existing
\`name: keyword\` mapping from model version 1.

## Test plan

- [ ] Existing tests in \`new_type.test.ts\` still pass
- [ ] Tests in \`existing_type.test.ts\` confirm: warning for
pre-existing keyword fields, fail for newly introduced ones, exempt for
non-importable/exportable types, and fail for hidden types that are
importable/exportable

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@darnautov darnautov left a comment

Choose a reason for hiding this comment

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

Overall LGTM, just one concern about duplicating excludedReasons schema

Comment on lines +51 to +58
excludedReasons: schema.maybe(
schema.arrayOf(
schema.oneOf([
schema.literal(gapReasonType.RULE_DISABLED),
schema.literal(gapReasonType.RULE_DID_NOT_RUN),
]),
{ maxSize: Object.values(gapReasonType).length }
)
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.

I noticed it's been duplicated in other schemas, should we consolidate it?

@nkhristinin nkhristinin requested a review from denar50 April 2, 2026 08:23
Copy link
Copy Markdown
Contributor

@maximpn maximpn left a comment

Choose a reason for hiding this comment

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

@nkhristinin Thanks for addressing my comments 🙏

I've tested the functionality locally. It works as expected.

There are a couple of comments related to my previous comments regarding exclude reasons schema and deduplication. These should be easy to address. Besides that it looks good so approving.

schema.oneOf([
schema.literal(gapReasonType.RULE_DISABLED),
schema.literal(gapReasonType.RULE_DID_NOT_RUN),
])
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.

Suggested change
])
]),
{
maxSize: Object.values(gapReasonType).length,
}

Comment on lines +70 to +75
const excludedReasonsFilter =
excludedReasons && excludedReasons.length > 0
? `NOT (${excludedReasons
.map((reason) => `${GAP_REASON_TYPE_FIELD}: "${reason}"`)
.join(' OR ')})`
: null;
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.

Let's have excluded reasons deduplication. Typing doesn't prevent having an array with the same values.

Suggested change
const excludedReasonsFilter =
excludedReasons && excludedReasons.length > 0
? `NOT (${excludedReasons
.map((reason) => `${GAP_REASON_TYPE_FIELD}: "${reason}"`)
.join(' OR ')})`
: null;
const excludedReasonsSet = new Set(excludedReasons ?? []);
const excludedReasonsFilter =
excludedReasonsSet.size > 0
? `NOT (${Array.from(excludedReasonsSet)
.map((reason) => `${GAP_REASON_TYPE_FIELD}: "${reason}"`)
.join(' OR ')})`
: null;

paulinashakirova pushed a commit to paulinashakirova/kibana that referenced this pull request Apr 2, 2026
…mportable/exportable SO types (elastic#260280)

## Summary

Closes elastic/kibana-team#3162

The \`validateNameTitleFieldTypes\` check in
\`kbn-check-saved-objects-cli\` enforces that any SO type with a
\`name\` or \`title\` mapping field must use \`type: text\`, citing
Search API compatibility. There are two distinct cases where the check
was incorrect:

### Case 1: Types not searchable via the management page

The check was applied unconditionally to all SO types, but the Saved
Objects management page search only queries types where
\`management.importableAndExportable === true\`. The registry treats the
absence of that flag as \`false\`, so types without it set are never
included in management search and the full-text requirement does not
apply to them.

Flagging such types also creates pressure to change \`keyword\` →
\`text\`, which is a **breaking Elasticsearch mapping change** (reindex
required) that is entirely unnecessary.

Note: the \`hidden\` flag alone is **not** the right exemption criterion
— the management find route explicitly includes hidden types that are
also \`importableAndExportable: true\` (via \`includedHiddenTypes\`), so
those remain subject to the check.

### Case 2: Pre-existing keyword fields from shipped model versions

Even for importable/exportable types, if \`name\` or \`title\` was
already shipped as \`keyword\` in a previous model version, **there is
nothing the developer can do to fix it** without a full reindex. Failing
the CI check in this case blocks the PR with no actionable resolution.

## Changes

- Split \`validateNameTitleFieldTypes\` into two focused functions:
- \`validateNameTitleFieldTypesNewType\` (in \`new_type_utils.ts\`) —
throws for any wrong field type on importable/exportable types; no
baseline exists for new types so the full error always applies
- \`validateNameTitleFieldTypesExistingType\` (in
\`existing_type_utils.ts\`) — warns for pre-existing wrong field types,
fails only for newly introduced ones
- Shared helpers \`getInvalidNameTitleFields\` and
\`isSearchableViaManagement\` extracted to \`common_utils.ts\`
- Exemption condition uses \`!== true\` (not \`=== false\`) to also
cover types where \`management\` is absent, matching the registry's \`??
false\` default
- \`createMockType\` in tests now defaults to \`importableAndExportable:
true\` so the positive (participates-in-management-search) path is
exercised correctly
- Tests updated: exemption test covers \`importableAndExportable:
false\`; a new test confirms that hidden types with
\`importableAndExportable: true\` are still subject to the check

## Context

This was first surfaced by
elastic#260095, which adds model version
2 to \`gap_auto_fill_scheduler\` — a type with
\`importableAndExportable\` not set to \`true\` and a pre-existing
\`name: keyword\` mapping from model version 1.

## Test plan

- [ ] Existing tests in \`new_type.test.ts\` still pass
- [ ] Tests in \`existing_type.test.ts\` confirm: warning for
pre-existing keyword fields, fail for newly introduced ones, exempt for
non-importable/exportable types, and fail for hidden types that are
importable/exportable

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@denar50 denar50 left a comment

Choose a reason for hiding this comment

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

Thanks for addressing my comments, I tested locally. LGTM!

"items": {
"type": "keyword",
"_meta": {
"description": "Non-default value of setting."
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can you improve this description? We hope to one day use these descriptions as documentation in Telemetry.

@nkhristinin nkhristinin requested a review from leathekd April 2, 2026 14:02
Copy link
Copy Markdown

@leathekd leathekd left a comment

Choose a reason for hiding this comment

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

Schema LGTM

@nkhristinin nkhristinin enabled auto-merge (squash) April 2, 2026 15:03
@nkhristinin
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@elasticmachine
Copy link
Copy Markdown
Contributor

elasticmachine commented Apr 2, 2026

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
securitySolution 9297 9298 +1

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/management-settings-ids 149 150 +1

Any counts in public APIs

Total count of every any typed public API. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats any for more detailed information.

id before after diff
alerting 1 2 +1

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
securitySolution 11.6MB 11.6MB +3.3KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
alerting 20.1KB 20.2KB +57.0B
securitySolution 121.2KB 121.3KB +53.0B
total +110.0B
Unknown metric groups

API count

id before after diff
@kbn/management-settings-ids 150 151 +1
alerting 899 901 +2
total +3

History

cc @nkhristinin

@nkhristinin nkhristinin merged commit 63785b2 into elastic:main Apr 2, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting ci:project-deploy-security Create a Security Serverless Project release_note:feature Makes this part of the condensed release notes v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.