Skip to content

[Nightshift] Add the Detections Events tab and header redesign - #299123

Merged
achyutjhunjhunwala merged 22 commits into
elastic:mainfrom
achyutjhunjhunwala:nightshift/detection-events-tab
Oct 21, 2026
Merged

achyutjhunjhunwala merged 22 commits into
elastic:mainfrom
achyutjhunjhunwala:nightshift/detection-events-tab

Conversation

@achyutjhunjhunwala

@achyutjhunjhunwala achyutjhunjhunwala commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds the Events tab to the Nightshift Detections page and updates the page header to the new design. Part of nightshift-program#1789 and nightshift-program#1787.

  • The page moves to /app/nightshift/detections. It's still unlinked, so there's no redirect from /detection.
  • The header shows Sources and Settings, and a Detection engine panel with five counts.
  • The Events tab lists significant events with search, Severity/Status/Investigation filters and sorting. The event flyout keeps Management's actions. A shared ?eventId= link opens that event's flyout whatever the time range; if the event no longer exists, the page drops the id and shows the table with a warning toast.
  • The events route gets sort_field, sort_order and investigation_state, plus a new _facets endpoint for the filter counts.
  • The significant event UI moves to components/entities/significant_event/. Management is unchanged.

How to test

  1. Open /app/nightshift/detections, change the time range, sort and filter.
  2. Open an event, run an investigation, then close the flyout.
  3. Check that the Management Significant events tab still works.

Known gaps

  • After you start or restart an investigation, the event shows "Not started" in the Investigation column and the flyout until the next refresh. The refetch right after the request runs before the investigation is recorded. Management's significant events page has the same gap. The fix lands with the flyout redesign in elastic/nightshift-program#1940.

Screenshot

The design takes inspiration from the Kate's Design present here. This does not matches 1:1 of-course specially we are only catering SigEvents and not Discoveries

image

@achyutjhunjhunwala achyutjhunjhunwala self-assigned this Oct 20, 2026
@achyutjhunjhunwala achyutjhunjhunwala added release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Feature: Nightshift Detection Engine Raw telemetry → Knowledge Indicators → Significant Events → alert Team:nightshift-sre-agent labels Oct 20, 2026
@achyutjhunjhunwala
achyutjhunjhunwala force-pushed the nightshift/detection-events-tab branch 5 times, most recently from eba95ae to e676ffe Compare October 20, 2026 13:14
achyutjhunjhunwala and others added 3 commits October 20, 2026 15:16
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Fall back to the default time range when the URL range is malformed, and skip duplicate history entries on refresh.
- Show an error callout with retry when the events list fails to load.
- Tab links carry only the time range.
- Keep flyout investigation completions in the Detection list cache, not only the landing cache.
- Share status labels, severity colours and query keys instead of copying them; label the flyout timestamp as last updated.
- Remove the unused chat footer and the showTitle props.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@achyutjhunjhunwala
achyutjhunjhunwala force-pushed the nightshift/detection-events-tab branch from e676ffe to 4005687 Compare October 20, 2026 13:33
achyutjhunjhunwala and others added 6 commits October 20, 2026 15:53
- Rename the page to Detections at /detections; /detection links redirect.
- Show Sources and Settings in the header; Settings opens the detection settings tab.
- Restyle the overview as the Detection engine panel: Monitoring/Paused badge and icon tiles for knowledge indicators, sources, rules, discoveries and significant events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the events list

- GET /internal/significant_events/events accepts sort_field (created_at, severity, status), sort_order and investigation_state.
- New GET /internal/significant_events/events/_facets counts the latest event states per severity, status and investigation state; each dimension ignores its own filter.
- Investigation state comes from the stored investigations entries: none, at least one without completed_at, or all completed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Columns: event (title and two-line summary), time, status, severity, investigation and a single action.
- Sort by time, status or severity (default: most severe first), stored in the URL and done on the server.
- Severity, Status and Investigation filters show per-option counts from the new facets endpoint.
- The row action starts an investigation when none exists, otherwise opens the event; restart and mark inactive stay in the event flyout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Use chevronSingleDown on the filter buttons; arrowDown isn't an EUI icon.
- Move Automations into the overflow menu on Detections so Sources and Settings stay visible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Event column: plain bold title, two-line summary and a Significant event badge.
- Time shows short numeric ages ("6 min. ago") with the exact time in a tooltip.
- Severity uses regular text size; one action opens the event (Investigate or View investigation).
- Compact date picker and compressed search and filters.
- Overview tiles: tinted icon squares and small one-line labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Avatar

- Each tile is an EuiAvatar icon square plus an EuiStat count and label, with no tooltips.
- Tiles take their natural width so labels stay on one line.
- Drop the Significant event badge from the events table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@achyutjhunjhunwala achyutjhunjhunwala changed the title [Nightshift] Add the Detection Events tab [Nightshift] Add the Detections Events tab and header redesign Oct 20, 2026
achyutjhunjhunwala and others added 2 commits October 20, 2026 16:38
The page is still unlinked, so no /detection links need to keep working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Reset the page when the time range changes, and move back to the last page when the URL points past it.
- Write the search to the URL only from typing, so Back and Forward keep their search.
- Drop a dead ?eventId= link with replace, so Back doesn't reopen it.
- Use FormattedRelativeTime and reactRouterNavigate instead of local versions.
- Share the investigation state rule, sort guard and search limit.
- Keep EBT tracking on meaningful actions only, and remove low-value tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@achyutjhunjhunwala
achyutjhunjhunwala marked this pull request as ready for review October 20, 2026 16:33
@achyutjhunjhunwala
achyutjhunjhunwala requested a review from a team as a code owner October 20, 2026 16:33
kibanamachine and others added 4 commits October 20, 2026 16:43
- Describe the second investigation run as a queued follow-up, which is what the server does.
- Scope pending investigation completions to a round, so a re-run doesn't show as completed.
- Hide facet counts when they're unavailable instead of showing zeroes.
- Cancel a pending search write on Back/Forward.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…time

- Render the Monitoring dot as an inline icon in the badge, so it centres at the standard badge height.
- Explain in the Time column header that the range shows events live during it.
- Widen the Status column so "Recovering" stays on one line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
achyutjhunjhunwala and others added 3 commits October 21, 2026 11:19
…ader and tab

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions header and tab"

This reverts commit 1429e11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@crespocarlos crespocarlos left a comment

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.

Left a few comments

// A missing alert.status decodes as active (see decodeSignificantEvent).
return query.pipe`EVAL ${esql.col(SORT_RANK_COLUMN)} = CASE(${esql.col(
'alert.status'
)} == ${esql.str('inactive')}, 1, ${esql.col('alert.status')} == ${esql.str(

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.

We have constants for these statuses in ALERT_EPISODE_STATUS

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.

Good call, switched the status CASE and the status facet COALESCE to ALERT_EPISODE_STATUS.* in 453c042.

}: {
timeRange: DetectionTimeRange;
onTimeRangeChange: (timeRange: DetectionTimeRange) => void;
}): React.ReactElement => {

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.

Do we need this ReactElement here?

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.

Kept it for consistency: the repo guidelines ask for explicit return types on exported functions, and every component in pages/detection declares React.ReactElement.

},
}}
/>
) : !eventsQuery.isLoading && eventsQuery.data && events.length === 0 && !isPastLastPage ? (

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.

Should we add a loading indicator here?

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.

Good call. EuiBasicTable's loading only draws a thin line, so the first load looked empty. Added a "Loading events…" noItemsMessage in 453c042, the same pattern SLO and Synthetics use.

}, [event.event_id]);

useEffect(() => {
if (latestInvestigation == null || latestInvestigation.completed_at != 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.

Are we treating possible failures the same way as no investigations and complete investigations here?

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.

Today a failed or unreadable investigation is closed out in the cache so the row doesn't spin "In progress" forever, since the server has no failed state yet (moved unchanged from main). #299456 fixes this properly: failed runs are recorded on the event and read errors no longer count as finished.

Comment on lines +263 to +279
const investigationActionProps = {
label: investigationRunning ? FOLLOW_UP_INVESTIGATION_LABEL : RUN_INVESTIGATION_LABEL,
iconType: 'inspect' as const,
tooltip:
activityBlockTooltip ?? (investigationRunning ? FOLLOW_UP_INVESTIGATION_TOOLTIP : undefined),
onClick: () => triggerInvestigation(event.event_id),
isDisabled: blocksActivity || triggeringEventId != null,
isLoading: triggeringEventId === event.event_id,
'data-test-subj': 'nightshiftEventFlyoutRunInvestigationButton',
...getEbtProps({
action: NIGHTSHIFT_EBT_ACTIONS.START_INVESTIGATION,
element: NIGHTSHIFT_EBT_ELEMENTS.EVENT_FLYOUT,
detail: isNeedsActionStatus(event.status)
? NIGHTSHIFT_EBT_DETAILS.NEEDS_ACTION
: NIGHTSHIFT_EBT_DETAILS.RESOLVED,
}),
};

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.

nit: you could do it the other way around:

Conditionally pick either <FlyoutTemplate.Footer.SecondaryAction> or <FlyoutTemplate.Footer.PrimaryAction> and pass the props once. IMHO, it would make it easier to understand, but it's up to you.

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.

Fair. This footer is replaced by a single Actions menu in #299456, so I'll leave it as is here.

Comment on lines +34 to +41
const SEARCH_QUERY_PARAM = 'search';
const STATUS_QUERY_PARAM = 'status';
const SEVERITY_QUERY_PARAM = 'severity';
const INVESTIGATION_QUERY_PARAM = 'investigation';
const SORT_FIELD_QUERY_PARAM = 'sortField';
const SORT_ORDER_QUERY_PARAM = 'sortOrder';
// The design leads with the most severe events.
const DEFAULT_SORT: EventsSort = { field: 'severity', order: 'desc' };

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.

Don't we have a query param for the current page?

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.

We do, PAGE_QUERY_PARAM is imported at the top. It lives in pages/detection/constants.ts because the time range hook also resets it; added a comment pointing to it in 453c042.

achyutjhunjhunwala and others added 3 commits October 21, 2026 12:28
- Use ALERT_EPISODE_STATUS for the status sort and facet count.
- Show "Loading events…" in the table while the first page loads.
- Point to the shared page query param.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@crespocarlos crespocarlos left a comment

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.

LGTM!

…-events-tab

# Conflicts:
#	x-pack/solutions/observability/plugins/significant_events/server/routes/internal/events/route.ts
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

Async chunks

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

id before after diff
nightshift 532.2KB 613.9KB +81.7KB
significantEventsApp 140.1KB 140.2KB +134.0B
total +81.8KB

Page load bundle

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

id before after diff
kbnUiSharedDeps-srcJs 5.4MB 5.4MB +4.0B
shared-plugins 12.5MB 12.5MB +352.0B
significantEvents 2.8KB 3.0KB +212.0B
total +568.0B
Unknown metric groups

async chunk count

id before after diff
nightshift 14 15 +1

shared chunks total size

id before after diff
all 7.5MB 7.5MB +16.0B

total optimizer output size

id before after diff
all 67.1MB 67.1MB +82.4KB

warm start memory

id before after diff
post forced gc heap baseline - 720978580 +720978580
post forced gc heap delta - -1883222 -1883222
post forced gc heap delta standard deviation - 2476720 +2476720
post forced gc heap target - 719095358 +719095358
tail heap delta - -16378556 -16378556
total +1424288880

History

cc @achyutjhunjhunwala

@achyutjhunjhunwala
achyutjhunjhunwala added this pull request to the merge queue Oct 21, 2026
Merged via the queue into elastic:main with commit ba9fe1f Oct 21, 2026
24 checks passed
@achyutjhunjhunwala
achyutjhunjhunwala deleted the nightshift/detection-events-tab branch October 21, 2026 13:01
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 Feature: Nightshift Detection Engine Raw telemetry → Knowledge Indicators → Significant Events → alert release_note:skip Skip the PR/issue when compiling release notes Team:nightshift-sre-agent v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants