Repository navigation
Conversation
Replace TODO comments with explanatory comments that clarify why the current implementation of unique ID generation is correct for mixed log scale warnings. The unique IDs are properly constructed using: - groupId (left/right) for axis-level warnings - accessor (column ID) for dimension-level warnings This ensures each warning has a unique identifier while maintaining proper context. Co-authored-by: markov00 <1421091+markov00@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Check TODO comment in visualization file
Remove TODO comments about unique ID generation in XY visualization warnings
Nov 13, 2025
andrimal
pushed a commit
that referenced
this pull request
Mar 19, 2026
…on (elastic#258531) The `integrations_automatic_import.cy.ts` test was consistently failing at the deploy step with `Bad Request (400): Building the Integration failed` because the test invoked the real `buildPackage()` server endpoint and the real Fleet package install endpoint — neither of which were mocked, unlike the generation graph endpoints (ECS, categorization, related). ## Changes - **`integrations_automatic_import.cy.ts`**: Add `cy.intercept` mocks in `beforeEach` for the two un-mocked endpoints: - `POST /internal/automatic_import/build` → returns `200 {}` - `POST /api/fleet/epm/packages` → returns `200` with `_meta.name: 'test_integration-1.0.0'`, which is what `getIntegrationNameFromResponse()` reads to set `integrationName` and render the success section ```typescript cy.intercept('POST', '/internal/automatic_import/build', { statusCode: 200, body: {}, }); cy.intercept('POST', '/api/fleet/epm/packages', { statusCode: 200, body: { _meta: { install_source: 'upload', name: 'test_integration-1.0.0' }, items: [], }, }); ``` <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Failing test: Fleet Cypress Tests #1 / Add Integration - Automatic Import should create an integration</issue_title> > <issue_description>https://buildkite.com/elastic/kibana-pull-request/builds/412255#019d019d-c8e8-429d-aa99-0585b8e3fc05 > should create an integration > Add Integration - Automatic Import should create an integration > > > Owners: > Unable to determine code owners > Failures in tracked branches: 1 > https://dryrun/ > Buildkite Job > https://buildkite.com/elastic/kibana-pull-request/builds/412255#019d019d-c8e8-429d-aa99-0585b8e3fc05 > > AssertionError: Timed out retrying after 60000ms: Expected to find element: `[data-test-subj="integrationSuccessSection"]`, but never found it. > at Context.eval (webpack://@kbn/fleet-plugin-cypress/./e2e/integrations_automatic_import.cy.ts:107:45) > > <img width="1440" height="900" alt="Image" src="https://github.com/user-attachments/assets/c7e54d6f-85e3-4e2b-87e8-1602c716788d" /> > > Logs: [kibana-pull-request_build_412706_fleet-cypress-tests.log](https://github.com/user-attachments/files/26113748/kibana-pull-request_build_412706_fleet-cypress-tests.log)</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > <comment_new><author>@elasticmachine</author><body> > Pinging @elastic/fleet (Team:Fleet)</body></comment_new> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes elastic#258522 <!-- START COPILOT CODING AGENT TIPS --> --- 📱 Kick off Copilot coding agent tasks wherever you are with [GitHub Mobile](https://gh.io/cca-mobile-docs), available on iOS and Android. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: juliaElastic <90178898+juliaElastic@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
Apr 2, 2026
Closes elastic#258318 Closes elastic#258319 ## Summary Adds logic to the alert episodes table to display `.alert_actions` information. This includes: - New action-specific API paths. - Snooze - **Per group hash.** - Button in the actions column opens a popover where an `until` can be picked. - **When snoozed** - A bell shows up in the status column. - Mouse over the bell icon to see until when the snooze is in effect. - Unsnooze - **Per group hash.** - Clicking the button removes the snooze. - Ack/Unack - **Per episode.** - Button in the actions column - When "acked", an icon shows in the status column. - Tags - This PR only handles displaying tags. They need to be created via API. - Resolve/Unresolve - **Per group hash.** - Button inside the ellipsis always - The status is turned to `inactive` **regardless of the "real" status.** <img width="1704" height="672" alt="Screenshot 2026-03-25 at 16 04 12" src="https://github.com/user-attachments/assets/5ef4111a-6e0c-4114-a60e-ce5f81a86ac6" /> ## Testing <details> <summary>POST mock episodes</summary> ``` POST _bulk { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:00:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:01:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "pending" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:02:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:03:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "inactive" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:04:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:05:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:06:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:07:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:08:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "active" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:09:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "recovering" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:10:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "recovering" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:11:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:12:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "recovering" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:13:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:14:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-003", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:15:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-003", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:16:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-4", "episode": { "id": "ep-004", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:17:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-4", "episode": { "id": "ep-004", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:18:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-4", "episode": { "id": "ep-004", "status": "recovering" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:19:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-4", "episode": { "id": "ep-004", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:20:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-5", "episode": { "id": "ep-005", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:21:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-5", "episode": { "id": "ep-005", "status": "pending" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:22:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-5", "episode": { "id": "ep-005", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:23:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:24:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:25:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "active" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:26:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:14:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-2" }, "group_hash": "elasticgh-7", "episode": { "id": "ep-007", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:15:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-2" }, "group_hash": "elasticgh-7", "episode": { "id": "ep-007", "status": "inactive" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:16:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-3" }, "group_hash": "elasticgh-8", "episode": { "id": "ep-008", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:17:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-3" }, "group_hash": "elasticgh-8", "episode": { "id": "ep-008", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:18:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-3" }, "group_hash": "elasticgh-8", "episode": { "id": "ep-008", "status": "recovering" }, "status": "recovered" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:20:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-4" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-009", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:21:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-4" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-009", "status": "pending" }, "status": "no_data" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:23:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-5" }, "group_hash": "elasticgh-10", "episode": { "id": "ep-010", "status": "pending" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:24:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-5" }, "group_hash": "elasticgh-10", "episode": { "id": "ep-010", "status": "active" }, "status": "breached" } { "create": { "_index": ".rule-events" }} { "@timestamp": "2026-01-27T16:25:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-5" }, "group_hash": "elasticgh-10", "episode": { "id": "ep-010", "status": "active" }, "status": "no_data" } ``` </details> - In the POST above, episodes 1 and 3, and episodes 6 and 9 have the same group hashes. - Go to `https://localhost:5601/app/observability/alerts-v2` and try all buttons. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
May 6, 2026
…stic#260544) ## Summary - Migrates the endpoint case attachment from the legacy `ExternalReferenceAttachmentType` to the new unified `UnifiedReferenceAttachmentType` on both client and server. - New endpoint response-action attachments are written as `{ type: 'security.endpoint', attachmentId, metadata }` (`UnifiedReferenceAttachmentPayload`) instead of the legacy `externalReference` shape. - Adds server-side `io-ts` schema validation for endpoint attachment metadata (`command`, `comment`, `targets[]` with a closed union on `agentType`, unknown keys rejected, non-empty `targets` required). - Adds a generic `externalReference` ↔ unified transformer in the Cases plugin so pre-existing legacy endpoint attachments render as unified on read and unified writes fall back to legacy storage when the new SO type is disabled — no data migration required. ## Details Part of the [Cases Attachments v2 migration](elastic/security-team#15569). The endpoint attachment (historically `externalReferenceAttachmentTypeId: 'endpoint'`) is now registered as the unified type `SECURITY_ENDPOINT_ATTACHMENT_TYPE = 'security.endpoint'`, re-exported from `@kbn/cases-plugin/common`. ### What changed | Layer | Before | After | |-------|--------|-------| | Client registration (`security_solution/public/plugin.tsx`) | `registerExternalReference(getExternalReferenceAttachmentEndpointRegular())` | `registerUnified(getEndpointUnifiedAttachment())` | | Server registration (`security_solution/server/plugin.ts`) | `registerExternalReference({ id: CASE_ATTACHMENT_ENDPOINT_TYPE_ID })` | `registerUnified({ id: SECURITY_ENDPOINT_ATTACHMENT_TYPE, schemaValidator: validateEndpointAttachmentMetadata })` | | Attachment creation (`base_response_actions_client.ts`) | `{ type: 'externalReference', externalReferenceId, externalReferenceStorage, externalReferenceAttachmentTypeId: 'endpoint', externalReferenceMetadata }` | `{ type: 'security.endpoint', attachmentId, metadata, owner }` | | Metadata validation | none | `io-ts` validator run on the unified write path | | Client-side renderers | `external_reference.tsx` + 2 lazy wrappers | `unified_attachment.tsx` + updated `endpoint_event.tsx` / `endpoint_children.tsx` | | Constant `CASE_ATTACHMENT_ENDPOINT_TYPE_ID` | defined in Security Solution | removed; import `SECURITY_ENDPOINT_ATTACHMENT_TYPE` from `@kbn/cases-plugin/common` | ### Backward compatibility (no data migration needed) The legacy `registerExternalReference` calls are removed — BWC is delivered instead by a generic transformer in the Cases plugin: - **Read path** — the Kibana Cases UI reads cases via the internal `resolve` endpoint with `mode: 'unified'`. The new `externalReferenceAttachmentTransformer` in `x-pack/platform/plugins/shared/cases/server/common/attachments/external_reference.ts` converts any pre-existing legacy `externalReference` endpoint attachments stored in `cases-comments` into the unified `security.endpoint` shape on read, driven by `EXTERNAL_REFERENCE_TYPE_MAP`. Existing cases render identically post-deploy without any backfill. - **Write path** — when `xpack.cases.attachments.enabled` is `false` (default), the Cases plugin translates the unified payload back to the legacy `externalReference` shape via `toLegacySchema` and persists it to `cases-comments` — byte-for-byte equivalent to today's storage. When the flag is `true`, the unified payload is stored as-is in the new `cases-attachments` SO. Either way, the on-disk format stays consistent with whatever the deployment is already using. This also gives follow-up subtypes (e.g. `osquery`, other response-action types) a clean seam: add an entry to `EXTERNAL_REFERENCE_TYPE_MAP` / `UNIFIED_TO_EXTERNAL_REFERENCE_TYPE_MAP` and they get the same round-trip behaviour for free. ### Public case APIs `GET /api/cases/:id` (`totalComment`) and `GET /api/cases/:id/comments/_find` continue to be scoped to user-generated comments and do not surface endpoint attachments — this is pre-existing, intended behaviour and is unchanged by this PR. The Kibana UI uses the internal `resolve` endpoint which returns all attachment types and renders endpoint attachments via the new unified registry. ## Incremental fixes after first review A second pass addressed three review items from @szwarckonrad on the upgrade-path walkthrough, plus one CI-driven snapshot follow-up. None of them change the design described above; they harden the same migration against edge cases the first pass missed. 1. **Back-compat for legacy-shape API writes** (`security_solution/server/cases/attachments/register.ts` + `plugin.ts`) — the legacy `endpoint` external-reference id is registered alongside the new unified `security.endpoint` so existing API clients that still POST `{ type: 'externalReference', externalReferenceAttachmentTypeId: 'endpoint', ... }` are not rejected with `400 "Attachment type endpoint is not registered."`. The cases server's external-reference transformer already converts these legacy SOs to the unified shape on read; this restores the same behaviour for legacy-shape *writes*. Covered by a focused unit test (`register.test.ts`) that explicitly asserts the BWC registration so it can't be silently dropped in a future refactor. 2. **400 instead of 500 from the metadata validator** (`endpoint_metadata_schema.ts`) — `validateEndpointAttachmentMetadata` now throws `Boom.badRequest` on invalid metadata. Errors thrown from a registered cases-plugin `schemaValidator` callback are surfaced to the HTTP client as-is — a plain `Error` would have bubbled up as `500 Internal Server Error` with a stack trace in the server log for what is really a caller mistake. Covered by new tests asserting `Boom.isBoom` and `statusCode: 400` for null/non-object/missing-fields/empty-targets/unknown-keys inputs. 3. **Byte-clean legacy storage** (`cases/server/services/attachments/index.ts`) — when a unified payload (`{ type: 'security.endpoint', attachmentId, metadata }`) is POSTed but `xpack.cases.attachments.enabled` is OFF, the request attributes still carry those keys after `io-ts` decoding and could leak into `_source` (the `cases-comments` mapping is `dynamic: false`, so they would be stored but not indexed). The new `stripUnifiedOnlyFields` helper guarantees byte-for-byte equivalence with pre-migration legacy writes for `create`/`bulkCreate`/`update`/`bulkUpdate`. Covered by two regression tests in `services/attachments/index.test.ts`. 4. **Snapshot follow-up for #1** (`cases_api_integration/.../external_references.ts`) — the registry-snapshot assertion that guards the externalReference registry now expects `endpoint: 'e13fe41b5c330dd923da91992ed0cedb7e30960f'` again, with an inline comment explaining the BWC intent. **This file is owned by Response Ops via CODEOWNERS** — the snapshot's purpose is exactly to surface this kind of change for their review. CI on the latest push: green (build #437809, 428/428 jobs). ## Test plan - [x] Unit tests for the generic `externalReference` ↔ unified transformer, storage-type resolver, and type-routing helper (`x-pack/platform/plugins/shared/cases/server/common/attachments/*.test.ts`). - [x] Unit tests for `validateEndpointAttachmentMetadata` covering: valid metadata, each missing required field, empty `targets`, invalid `agentType`, unknown top-level keys, and non-object input. - [x] Updated `endpoint_actions_client` and `base_response_actions_client` unit tests assert the new unified payload shape (`type: 'security.endpoint'`, `attachmentId`, `metadata`). - [x] Updated unit tests for `endpoint_event.tsx` / `endpoint_children.tsx` against the unified props shape. - [x] Integration-test registry expectations updated: `security.endpoint` appears in `registered_unified_{basic,trial}.ts`. `endpoint` is **kept** in `external_references.ts` with a comment explaining the back-compat re-registration (see "Incremental fixes after first review" below). - [x] Type check and lint pass. - [x] Manual end-to-end validation against **Microsoft Defender for Endpoint** — unisolate action from Kibana correctly produces a `cases-attachments` SO of `type: 'security.endpoint'` with `microsoft_defender_endpoint` metadata, rendered by the UI via the unified registry. - [x] Manual end-to-end validation against **CrowdStrike Falcon** — unisolate action from Kibana correctly produces a `cases-attachments` SO of `type: 'security.endpoint'` with `crowdstrike` metadata, rendered by the UI via the unified registry. - [x] Manual verification: isolate a host via response actions and confirm the case attachment renders correctly. <img width="1798" height="1064" alt="CleanShot 2026-04-20 at 14 53 16@2x" src="https://github.com/user-attachments/assets/8c216722-a2a4-42ac-b5ae-dc8962cb2d0d" /> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
May 22, 2026
elastic#270540) ## Release note When using Kibana Spaces, the Synthetics monitor health endpoint could incorrectly report monitors as unhealthy — showing errors such as "missing location", "missing agent policy", or "missing package policy" — even when everything was properly configured. This happened because the health check was only looking for monitors, private locations, and Fleet policies in the current space, missing resources that existed in other spaces. These issues are now fixed: the health check correctly resolves monitors, private locations, package policies, and agent policies across all relevant spaces, giving an accurate health status regardless of how resources are distributed across your Kibana Spaces. ## Summary Closes elastic#270477. `POST /internal/synthetics/monitors/_health` returned wrong results when monitors lived outside the request's space — `missing_package_policy` errors when called from the monitor's space, and 404s when called from `default`. Two independent space-scoping bugs: 1. **Package policy lookup ignored space.** `getExistingPackagePoliciesMap` called Fleet's `packagePolicyService.getByIDs` with `createInternalRepository()`, which is scoped to the default namespace. Package policies created for monitors in another space were therefore invisible. 2. **Monitor saved-object lookup was space-scoped.** `MonitorConfigRepository.get` used the request-scoped saved-objects client, restricted to the request's space. Calling `_health` from `default` for a monitor that lives elsewhere returned a 404. ## What changed - **`PackagePolicyService.getByIds`** — accepts a new optional `additionalSpaceIds`, so the wrapper's per-space scoped-client fan-out can broaden beyond `[spaceId, default]`. Existing callers keep their old behavior. - **`MonitorConfigRepository.getAcrossSpaces(id, namespaces, soClient?)`** — new method that resolves a monitor across an arbitrary list of spaces. Uses the multi-space type's per-object `namespaces` array in one bulkGet entry, plus one entry per namespace for the `namespaceType: 'single'` legacy type. Accepts an injected `soClient` so the health API can pass `createInternalRepository()` and bypass the request's space restriction. - **`MonitorIntegrationHealthApi`**: - Computes `allSpaces = { requestSpace, ...allSpacesWithMonitors }` once, up-front. - `fetchMonitors` calls `monitorConfigRepository.getAcrossSpaces` with the internal repository → fixes bug #2. - `getExistingPackagePoliciesMap` uses the `PackagePolicyService` wrapper with `additionalSpaceIds` → fixes bug #1. ## Test plan - [x] `node scripts/jest` on the three affected suites — **77/77 passing** (includes new cross-space coverage and a new `getAcrossSpaces` test block). - [x] `node scripts/type_check --project x-pack/solutions/observability/plugins/synthetics/tsconfig.json` — clean. - [x] `node scripts/eslint` on the changed files — clean. - [ ] Manual: create a monitor with a private location in a non-default space, then call `POST /internal/synthetics/monitors/_health` both from that space and from `default`. Verify each call reports the monitor accurately instead of `missing_package_policy` / 404. ## Related - elastic#270137 — related health API fix (project monitor policy ID + infinite polling). Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Miguel Martín <miguel.martin@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
May 27, 2026
Fixes elastic#261258 possibly also elastic#264565 (to be verified) ## Summary Add `meta: { id }` to Fleet body/response schemas Files changed: - `server/types/models/agent_policy.ts` — versioned policy schemas (v3–v6), `new_agent_policy`, `agent_policy`, `agent_policy_response`, `full_agent_policy`, outputs responses - `server/types/rest_spec/agent_policy.ts` — bulk get, copy, delete, full policy, K8s manifest, list outputs, cleanup task request/response schemas - `server/types/models/enrollment_api_key.ts `— `enrollment_api_key` - `server/types/rest_spec/enrollment_api_key.ts` — enrollment key CRUD and bulk delete schemas - `common/types/models/package_policy_schema.ts `— all package policy schemas: new, versioned (v22–v24), simplified, update, response, dry-run, status - `server/types/rest_spec/package_policy.ts` — bulk get, create, delete, upgrade, dry-run schemas - `server/types/rest_spec/epm.ts `— ~40 package management schemas (categories, package info/list/stats, install/delete/bulk operations, etc.) - `server/types/rest_spec/agent.ts` — `action_id_response` / `action_message_response` branches of `ActionIdOrMessageSchema` (resolves items #1 and #2 of elastic#264565) ### Testing Verified locally with: ``` node scripts/capture_oas_snapshot --no-serverless --include-path /api/fleet node scripts/validate_oas_docs node scripts/check_api_contracts --distribution stack ``` It looks like there are no Fleet errors left after this change but the final verification step will be ran in the ci for Terraform --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
Jun 19, 2026
…ticsEsClient (elastic#273418) ## Summary Addresses elastic#273086 (fix #1 — the single choke point). Synthetics issued every Elasticsearch query against the broad `synthetics-*` wildcard with only `ignore_unavailable: true` — no data-tier filter. As a result, queries fanned out to **all** backing indices across hot/warm/**frozen** tiers. On clusters with long frozen retention (e.g. `hot: 3d / warm: 4d / frozen: 365d`) this caused high latency (slow screenshots and history) and contributed to cluster-wide **search thread pool rejections**, because frozen searchable-snapshot shards are slow and deliberately throttled. This PR makes `SyntheticsEsClient` honor the existing `observability:searchExcludedDataTiers` advanced setting — the same mechanism already used by **APM** and **Infrastructure**. When configured, a `must_not { terms: { _tier: [...] } }` filter is injected into every `search` / `msearch` / `count`, so excluded tiers (e.g. `data_frozen`) are skipped. ### What changed - `SyntheticsEsClient` reads `observability:searchExcludedDataTiers` (cached per instance) and wraps each query via a new exported `applyExcludedDataTiersToQuery` helper, reusing the shared `excludeTiersQuery` util from `@kbn/observability-utils-common`. - The client now also accepts a plain `IUiSettingsClient` (`uiSettingsClient` option) in addition to the route-handler `uiSettings` context, so alerting rule executors can resolve the setting. - Wired the **status** and **TLS** rule executors (and the inspect-TLS route) to pass their `uiSettingsClient`, so alerting queries are covered too. - Updated the advanced-setting description to list Synthetics among the affected apps. ### Behavior / risk - The setting defaults to `[]`, so **behavior is unchanged unless an operator opts in** — no functional change for existing deployments. - All Synthetics UI queries flow through `SyntheticsEsClient` via the route wrapper (which already passes `uiSettings`), so screenshots, history, latest-ping, and overview queries are all covered with no call-site changes. ### Follow-ups (tracked in elastic#273086, not in this PR) - Add `@timestamp` lower bounds to the time-less queries (screenshots/steps/network events, latest-ping hooks, `get_last_successful_check`). - Drop the residual 30-day default in `fetchMonitorRecentPings`. ## Testing - `node scripts/jest x-pack/solutions/observability/plugins/synthetics/server/lib.test.ts` — added unit tests for `search`/`msearch`/`count` exclusion and the `applyExcludedDataTiersToQuery` helper. - Status + TLS rule executor jest suites pass unchanged. - Manual: set **Stack Management → Advanced Settings → Excluded data tiers from search** to `data_frozen` and confirm Synthetics search requests include the `must_not _tier` filter (visible via the inspector / `observability:enableInspectEsQueries`). ### Checklist - [x] Unit tests updated - [x] Type-check passes for the synthetics project - [x] ESLint + i18n checks pass ### Release note Synthetics now respects the `observability:searchExcludedDataTiers` advanced setting, allowing operators to exclude slow data tiers (such as `data_frozen`) from Synthetics searches to reduce latency and search thread pool pressure. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
Jul 22, 2026
…280124) Fixes elastic#278484 - likely introduced by elastic#278689 (cc @maryam-saeidi) ### Summary - The `should allow creation of lens xy chart` test timed out selecting the `terms`/`geo.src` combobox with *"geo.src doesn't match any options"*. - Lens opened the new visualization on `lens_regular_data` (fields: `@timestamp`, `bytes` only) instead of the rollup view that actually has `geo.src`. - This patch adds `await lens.switchDataPanelIndexPattern('lens_rolled_up_data');` right after `clickVisType('lens')`, so the test no longer depends on which data view Lens auto-picks. ### Context - Follows the [failed-test investigator's proposed fix](elastic#278484 (comment)) (the "explicit in-test switch" option, which it recommended over re-pinning `defaultIndex`). - Root cause: the suite `before` hook calls `cleanStandardList()` — deleting all `index-pattern` objects, including the `logstash-*` default pinned by the group15 hook — then imports only `lens_regular_data` + `lens_rolled_up_data` and never re-pins `defaultIndex`. The stale `defaultIndex` dangles at a deleted object, so Lens auto-picks a data view non-deterministically; test #1 never selects one, so it's at the mercy of that pick. - This is a *different* signature from the earlier save-modal timeout on this same issue: elastic#278635 fixed that at `lens_page.ts:826` and elastic#278689 unskipped the test, which then exposed this latent data-view flake. - Failures were on `kibana-on-merge - main` (the failing lane), most recently [build 103568](https://buildkite.com/elastic/kibana-on-merge/builds/103568#019f897f-646b-4216-a8bf-41601a86dfb3). <details> <summary>Verification</summary> #### Verified locally - ✅ Passed: `node scripts/eslint x-pack/platform/test/functional/apps/lens/group15/rollup.ts` -⚠️ `node scripts/type_check --project x-pack/platform/test/tsconfig.json` — the tsc build was SIGKILLed (OOM) by the sandbox before completing, not a type error. The change is type-trivial: `switchDataPanelIndexPattern(dataViewTitle: string)` is an existing helper already called with a string literal elsewhere in this same file (line 101). #### Not verified locally - This is an FTR test requiring a live Elasticsearch + Kibana, which can't run in this environment — behavior is verified by CI. - `type_check` could not be run to completion in-sandbox (OOM). </details> <details> <summary>Backporting guidance</summary> Applied `backport:all-open`. The same `lens rollup tests` suite with the identical `should allow creation of lens xy chart` flow (and the `switchDataPanelIndexPattern` helper) exists on every open release branch — `9.5` and `9.4` at `x-pack/platform/test/functional/apps/lens/group15/rollup.ts`, `9.3` and `8.19` at `.../lens/group6/rollup.ts` — all carrying the same latent bug, so the one-line switch applies unchanged across all of them. </details> > [!NOTE] > Share feedback in #kibana-qa. Mention `@copilot` to make quick changes. > Generated by [Flaky Test Fixer](https://github.com/elastic/kibana/actions/runs/29919289842) for elastic#278484 · 252.1 AIC · ⌖ 12.4 AIC · ⊞ 6.7K · [◷](https://github.com/search?q=repo%3Aelastic%2Fkibana+%22gh-aw-workflow-id%3A+flaky-test-fixer%22&type=pullrequests) <!-- gh-aw-agentic-workflow: Flaky Test Fixer, engine: claude, version: 2.1.165, model: opus, id: 29919289842, workflow_id: flaky-test-fixer, run: https://github.com/elastic/kibana/actions/runs/29919289842 --> <!-- gh-aw-workflow-id: flaky-test-fixer --> <!-- gh-aw-workflow-call-id: elastic/kibana/flaky-test-fixer --> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nickofthyme
pushed a commit
that referenced
this pull request
Aug 4, 2026
## Summary Adds a Jenkins connector spec (`.jenkins`) so workflows and agents can trigger and follow Jenkins builds to completion, gather evidence (console log, JUnit test report), and mitigate a bad job (stop a build, disable/enable a job, freeze the whole instance with quiet-down) via the [Jenkins Remote Access API](https://www.jenkins.io/doc/book/using/remote-access-api/). - Authenticates with a Jenkins username + API token over HTTP Basic auth (API tokens are exempt from Jenkins' CSRF crumb requirement, unlike password auth). - Implements all 16 actions from the linked issue's acceptance criteria: `request` (generic escape hatch), `triggerBuild`, `triggerBuildWithParameters`, `getQueueItem`, `getBuild`, `getConsoleLog`, `stopBuild`, `getLastBuild`, `listJobs`, `getJob`, `listBuilds`, `getBuildTestReport`, `disableJob`, `enableJob`, `getQueue`, `quietDown`, `cancelQuietDown`. - Blocks the Groovy script console, credentials store, security realm configuration, plugin manager, and instance restart endpoints regardless of which action is used, with guards resilient to percent-encoding and dot-segment path traversal. - Response payloads are slimmed down to agent-friendly shapes (e.g. build/job status, failing test cases capped and truncation-flagged, console log capped to the last 20,000 characters). ### A redirect/auth bug found during verification I verified every action end-to-end against a disposable local Jenkins instance (Docker). That surfaced a real bug: Jenkins responds to its POST "form action" endpoints (`stop`, `disable`, `enable`, `quietDown`, `cancelQuietDown`) with a `302` instead of `200`. Kibana's shared axios client attaches Basic-Auth via the low-level `auth: {username, password}` request option rather than a header, and `follow-redirects` overwrites that option with the (credential-less) URL fields of the redirect target when auto-following — silently dropping the credentials on the follow-up request and turning a successful action into a `403`. Fixed by setting `maxRedirects: 0` and a `validateStatus` that accepts `2xx`/`3xx` on the shared request helper, since the redirect target's body isn't needed anyway. Covered by a regression test asserting the `validateStatus` behavior. Closes elastic/security-team#18424 ## Validated Every action was exercised end-to-end against a disposable local Jenkins instance (Docker, `jenkins/jenkins:lts-jdk17`, seeded with an admin API token and four demo jobs: a plain freestyle job, a parameterized job, a long-running pipeline, and a pipeline publishing JUnit results). | Action | What was tested | Result | | --- | --- | --- | | test (connectivity) | Called `whoAmI` with the configured admin username/API token; confirmed `authenticated: true` and the returned username | ✅ Pass | | request | Called `/computer/api/json` as the generic escape hatch; separately confirmed `/scriptText`, `/credentials/store/system/`, `/pluginManager/installNecessaryPlugins`, a percent-encoded blocked path (`/%73cript`), and a dot-segment traversal (`/job/foo/../../script`) are all rejected | ✅ Pass | | triggerBuild | Triggered the freestyle job; got back a queue item id | ✅ Pass | | triggerBuildWithParameters | Triggered the parameterized job with `TARGET=production`; confirmed via `getConsoleLog` that the value actually reached the build ("Deploying to production") | ✅ Pass | | getQueueItem | Resolved queue items from all of the triggers above to their eventual build numbers; also observed the transient `blocked`/`why` state before a build starts | ✅ Pass | | getBuild | Fetched the freestyle job's build #1; got `result: "SUCCESS"` with correct ISO timestamp/duration | ✅ Pass | | getConsoleLog | Fetched real console output for two different builds; confirmed content matches the job's shell script output | ✅ Pass | | stopBuild | Aborted a running long-pipeline build; confirmed via `getBuild` it transitioned to `ABORTED`. Found and fixed the redirect/Basic-Auth bug described above (initial attempt returned a false `403`) | ✅ Pass (after fixing redirect handling) | | getLastBuild | Fetched the freestyle job's most recent build via the `lastBuild` alias | ✅ Pass | | listJobs | Listed all 4 seeded jobs; confirmed color-to-status mapping (`blue`→`success`, `red`→`failed`, `disabled`→`disabled`) against real Jenkins job colors | ✅ Pass | | getJob | Fetched the parameterized job; confirmed the `TARGET` parameter definition (type, description, default) was extracted correctly | ✅ Pass | | listBuilds | Listed recent builds for the freestyle job after two triggers | ✅ Pass | | getBuildTestReport | Triggered the JUnit-publishing pipeline (which fails on purpose); got back `passCount: 2, failCount: 1` with the real failing test's class/name/error message | ✅ Pass | | disableJob | Disabled the freestyle job. Hit the same redirect/Basic-Auth bug as `stopBuild` before the fix | ✅ Pass (after fixing redirect handling) | | enableJob | Re-enabled the freestyle job after `disableJob` | ✅ Pass (after fixing redirect handling) | | getQueue | Confirmed empty when idle, then confirmed it lists the pending item while a build is held during `quietDown` | ✅ Pass | | quietDown | Triggered a build while in quiet-down mode and confirmed via `getQueueItem`/`getQueue` it was genuinely held with reason "Jenkins is about to shut down" rather than starting | ✅ Pass (after fixing redirect handling) | | cancelQuietDown | Cancelled quiet-down and confirmed the previously-held build immediately proceeded to a build number | ✅ Pass (after fixing redirect handling) | ## Test plan - [x] Unit tests: `node scripts/jest src/platform/packages/shared/kbn-connector-specs/src/specs/jenkins/jenkins.test.ts` (42 passing) - [x] Full package suite: `node scripts/jest --config src/platform/packages/shared/kbn-connector-specs/jest.config.js` (75 suites / 1675 tests passing, no regressions) - [x] Type check: `node scripts/type_check --project src/platform/packages/shared/kbn-connector-specs/tsconfig.json` - [x] Lint: `node scripts/eslint --fix` on changed files (clean) - [x] Manual end-to-end verification against a local Jenkins Docker instance — see `## Validated` above for the per-action breakdown Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
nickofthyme
pushed a commit
that referenced
this pull request
Aug 4, 2026
…lastic#281948) ## Summary While reviewing eval results for a feature-extraction prompt rewrite ([elastic#281431](elastic#281431)), the reported numbers turned out not to support the conclusions being drawn from them. This PR fixes the measurement so prompt variants can be compared, and leaves the prompt itself untouched. Four separate problems, each of which could move a metric for reasons unrelated to the change being measured: **1. `executeAsReasoningAgent` under-reported token usage.** It returned only the token counts from its final LLM call. Every multi-step run silently dropped everything the earlier steps consumed, and the more steps a run took, the more it under-reported. Token counts are now accumulated across all steps, including `thinking` and `cached`. **2. Trace-derived token and latency metrics had no independent check.** `Input Tokens`, `Output Tokens` and latency are summed from exported OTel spans, and the trace-based evaluators only assert `result > 0`. A trace that had not finished exporting produced a low-but-plausible number, indistinguishable from a genuine reduction. Tasks now also surface the counts the inference provider reported (`reported_input_tokens` / `reported_output_tokens`); a gap between the two pairs points at trace collection rather than at model behaviour. **3. Round-trip count was invisible.** Agentic flows re-send the entire context on every step, so token totals and summed latency scale with the number of LLM calls as much as with prompt size. A new `Chat Calls` trace-based evaluator counts `gen_ai.operation.name == "chat"` spans, making a change in step count distinguishable from a change in per-call cost. This is the metric that explains most of the headline "token reduction" we were about to attribute to prompt size. **4. Extraction volume was not comparable across arms.** `ki_feature_count` only checks the returned count against the dataset's min/max bounds, which are wide enough that large swings still score 1. `extracted_feature_count` (extraction) and `exclude_feature_count` (exclusion) now report the raw counts. All three significant-events suites — extraction, exclusion, deduplication — are instrumented, so a single re-run covers everything. The exclusion and deduplication flows call `identifyFeatures` more than once, so their provider token counts are summed across runs to stay comparable with the trace totals. Also included: deduplication contract cases covering the previously-identified-features context, which were written while investigating whether dedup behaviour explained any of the movement. ## Risk The only production code path touched is the token accumulation in `executeAsReasoningAgent`. It changes reported totals — which were wrong — and not control flow. Everything else is eval-only. Note that fixing #1 means token numbers on multi-step runs will now read **higher** than previously recorded baselines. Any comparison against a pre-merge baseline needs re-running. ## Test plan - [ ] `node scripts/jest x-pack/platform/packages/shared/kbn-inference-prompt-utils` — covers token accumulation across reasoning steps - [ ] `node scripts/jest x-pack/platform/packages/shared/kbn-evals-suite-significant-events` — covers the new evaluators - [ ] `node scripts/type_check --project x-pack/platform/packages/shared/kbn-evals-suite-significant-events/tsconfig.json` - [ ] `node scripts/type_check --project x-pack/platform/packages/shared/kbn-inference-prompt-utils/tsconfig.json` - [ ] Re-run the significant events eval suites and confirm the provider-reported and trace-derived token pairs agree Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
nickofthyme
pushed a commit
that referenced
this pull request
Aug 18, 2026
Part of elastic#281244 ## Summary - Migrates the remaining Index Management FTR API suites (stateful + serverless) to Scout, completing the work done in elastic#283397 and elastic#283996. - Adds `x-pack/platform/plugins/shared/index_management/test/scout/api/` with 25 specs across `cluster_nodes`, `component_templates/`, `data_streams/`, `enrich_policies/`, `index_templates/` and `indices/`. - Deletes the stateful FTR suite `x-pack/platform/test/api_integration/apis/management/index_management/**` and the serverless FTR suite + services `x-pack/platform/test/serverless/api_integration/{test_suites,services}/index_management/**`, plus their config/manifest registrations. - `data_streams`: merges the stateful suite with the serverless `ds_common`/`ds_serverless` mirrors into one `deploymentAgnostic` `data_streams.spec.ts`. The old `ds_mki.ts` security-project branch becomes its own `data_streams_mki_security.spec.ts`, tagged `serverless.security.complete` and skipped unless `config.isCloud` (the default retention it asserts only exists on real Cloud projects). This mirrors the old FTR's `ds_serverless.ts`/`ds_mki.ts` split rather than loosening the generic assertion — no environment is left with weaker coverage than before. - `data_streams` logsdb cluster-settings matrix splits by environment because the defaults differ: `data_streams_logsdb_settings.spec.ts` (stateful-only, 4 cases including `logsdb.prior_logs_usage`) and `data_streams_logsdb_settings_serverless.spec.ts` (`serverless.all`, 3 cases, no `prior_logs_usage`). The plain index-mode assertion (no cluster-settings mutation) is its own agnostic `data_streams_index_mode.spec.ts`. - `indices`: merged CRUD/list/details into one agnostic `indices.spec.ts` (env-specific expected key sets via `forDeployment`); close/open/flush/refresh/forcemerge/clear-cache move to `indices_actions.spec.ts`, stateful-only since those actions aren't exposed on serverless. - `index_templates`: split into `index_templates.spec.ts` (agnostic CRUD/simulate/error-parsing), `index_templates_index_mode.spec.ts` (agnostic), `index_templates_legacy.spec.ts` and `index_templates_ilm_policy.spec.ts` (both stateful-only, legacy templates and ILM don't exist on serverless), and a logsdb cluster-settings pair mirroring the `data_streams` split: `index_templates_logsdb_settings.spec.ts` (stateful-only, 4 cases with `prior_logs_usage`) plus `index_templates_logsdb_settings_serverless.spec.ts` (`serverless.all`, 3 cases, no `prior_logs_usage` — same default-flip as its data-streams equivalent). - `component_templates`: single agnostic spec; drops the old serverless file's verbatim-duplicate `Update #2` block. - `enrich_policies`: merged list/execute/delete into one agnostic spec, replacing the old fixed `setTimeout(2000)` + loose `[200, 429]` delete-status assertion with a real retry loop on the ES 409/429 execution-lock response. - `create_enrich_policy`: merged create + field/index/data-stream discovery into one agnostic spec — the data-stream discovery cases were previously stateful-only pending serverless verification; they're confirmed working and now run everywhere. - `cluster_nodes`: single agnostic spec asserting the 200+array (stateful) vs 410 (serverless) contract via `forDeployment`. - Implements the previously-deferred/blocked item (migration plan batch 4, elastic#27): a disabled-data-enrichers indices test now lives in a new test area, `test/scout_disabled_data_enrichers/api/`, backed by a new Scout server config set `disabled_data_enrichers` (`src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/disabled_data_enrichers/stateful/classic.stateful.config.ts`) that boots Kibana with `--xpack.rollup.ui.enabled=false --xpack.ccr.ui.enabled=false --xpack.ilm.ui.enabled=false`, since no existing config set disabled those plugin UIs at boot time. ### Test plan Scout flaky test runner: - `/flaky scoutConfig:x-pack/platform/plugins/shared/index_management/test/scout/api/playwright.config.ts:30` [kibana-flaky-test-suite-runner#13780](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/13780) - `/flaky scoutConfig:x-pack/platform/plugins/shared/index_management/test/scout_disabled_data_enrichers/api/playwright.config.ts:30` [kibana-flaky-test-suite-runner#13781](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/13781) <details> <summary>Migration details</summary> ## Counts | Suite | Before | After | |---|---|---| | FTR stateful API (`test/api_integration/apis/management/index_management/`, 18 test files) | 97 tests | deleted | | FTR serverless API (`test/serverless/api_integration/test_suites/index_management/`, 12 test files) | 59 tests (incl. 2 verbatim-duplicate `Update #2` tests) | deleted | | FTR stateful `disabled_data_enrichers/indices.ts` | 1 test (own CI config entry) | deleted | | Scout API (`test/scout/api/tests/`, 25 specs) | new | 108 tests, tagged `deploymentAgnostic` / `stateful.classic` / `serverless.all` / `serverless.security.complete` | | Scout `scout_disabled_data_enrichers/api/tests/` (1 spec) | new | 1 test, `stateful.classic` | 156 FTR tests (97 stateful + 59 serverless) become 109 Scout tests total (108 + 1): most stateful/serverless mirror pairs collapse into a single `deploymentAgnostic` test that runs on both; the duplicated `Update #2` block (2 tests) and `create_index.ts`'s `can create an index` (a duplicate of `indices.ts`'s own create case) are dropped as duplicates with no coverage lost. ## Stateful API FTR → Scout | FTR test (file) | Scout test | Tag | |---|---|---| | should fetch the nodes plugins (`cluster_nodes.ts`) | `cluster_nodes.spec.ts` › fetches the nodes plugins | agnostic | | should return an array of component templates (`component_templates.ts`) | `component_templates.spec.ts` › returns an array of component templates | agnostic | | should return a single component template | › returns a single component template | agnostic | | should create a component template | › creates a component template | agnostic | | should create a component template with a frozen_after lifecycle | › creates a component template with a frozen_after lifecycle | agnostic | | should create a component template with only required fields | › creates a component template with only required fields | agnostic | | should not allow creation of a component template with the same name of an existing one | › rejects creating a component template that already exists | agnostic | | should allow an existing component template to be updated | › updates an existing component template | agnostic | | should not allow a non-existing component template to be updated | › rejects updating a component template that does not exist | agnostic | | should allow a deprecated component template to be updated | › updates a deprecated component template | agnostic | | should delete a component template | › deletes a component template | agnostic | | should delete multiple component templates | › deletes multiple component templates | agnostic | | should return an error for any component templates not sucessfully deleted | › reports the component templates it could not delete | agnostic | | should return no datastreams | › returns no data streams for an unused component template | agnostic | | should return datastreams | › returns the data streams of a component template | agnostic | | Allows to create an enrich policy (`create_enrich_policy.ts`) | `create_enrich_policy.spec.ts` › allows to create an enrich policy | agnostic | | Can retrieve fields from indices | › can retrieve fields from indices | agnostic | | Can retrieve matching indices | › can retrieve matching indices | agnostic | | Can retrieve matching data streams | › can retrieve matching data streams — previously stateful-only pending verification, now agnostic | agnostic | | can create an index (`create_index.ts`) | dropped as duplicate of `indices.ts`'s own create case | — | | throws 400 when index already created | `indices.spec.ts` › rejects creating an index that already exists | agnostic | | ILM data is fetched by the ILM data enricher (`data_enrichers/ilm.ts`) | `data_enrichers_ilm.spec.ts` › reports the ILM policy of a managed index | stateful | | ILM data is not empty even if the index unmanaged | › reports ILM data for an unmanaged index | stateful | | returns an array of data streams (`data_streams.ts`) | `data_streams.spec.ts` › lists the data streams (skipped on Security MKI, see `data_streams_mki_security.spec.ts`) | agnostic | | includes stats when provided the includeStats query parameter | › includes stats when asked for them (same skip) | agnostic | | returns a single data stream by ID | › returns a single data stream by name (same skip) | agnostic | | updates the data retention of a DS | › updates the data retention of a data stream | agnostic | | updates the data retention of multiple DS | › updates the data retention of multiple data streams | agnostic | | sets data retention to infinite | › sets the data retention to infinite | agnostic | | can disable lifecycle for a given policy | `data_streams_disable_lifecycle.spec.ts` › disables the lifecycle of a data stream | stateful | | updates the failure store configuration | `data_streams.spec.ts` › updates the failure store configuration | agnostic | | deletes multiple data streams | › deletes multiple data streams | agnostic | | Apply mapping from index template | › applies the mappings of the index template | agnostic | | Rollover datastreams | › rolls a data stream over | agnostic | | correctly returns index mode property based on index settings (`data_streams_index_mode.ts`) | `data_streams_index_mode.spec.ts` › same | agnostic | | returns … index mode if logsdb.enabled is … and prior_logs_usage is … (×4 loop) | `data_streams_logsdb_settings.spec.ts` › same (×4) | stateful | | doesn't send ILM, CCR and Rollups requests (`disabled_data_enrichers/indices.ts`) | `scout_disabled_data_enrichers/api/tests/indices.spec.ts` › same | stateful | | should list all policies (`enrich_policies.ts`) | `enrich_policies.spec.ts` › lists the enrich policies | agnostic | | should be able to execute a policy | › executes an enrich policy | agnostic | | should be able to delete a policy | › deletes an enrich policy | agnostic | | falls back to the cluster default failed data retention when no override is set (`failure_store_settings.ts`) | `failure_store_settings.spec.ts` › same | agnostic | | reflects the persistent cluster default failed data retention override | › same | agnostic | | returns index details (`index_details.ts`) | `indices.spec.ts` › returns the details of a single index | agnostic | | throws 404 when index doesn't exist | › returns 404 for an index that does not exist | agnostic | | returns counts per index and fills missing buckets with 0 (`index_doc_count.ts`) | `index_doc_count.spec.ts` › same | stateful | | returns counts for > 10 indices | › returns counts for more than 10 indices | stateful | | supports 255 character index names (20 indices) | › supports 255 character index names | stateful | | validates request body (indexNames must be non-empty) | › rejects an empty list of index names | stateful | | should create an index (`indices.ts`) | `indices.spec.ts` › creates an index with an index mode | agnostic | | should require index name to be provided | › rejects creating an index without a name | agnostic | | should create an index without index mode | › creates an index without an index mode | agnostic | | should clear the cache on a single index | `indices_actions.spec.ts` › clears the cache of an index | stateful | | should close an index | › closes an index | stateful | | should open an index | › opens an index | stateful | | should delete an index | `indices.spec.ts` › deletes an index | agnostic | | should require index or indices to be provided | › rejects a delete without an index name | agnostic | | should flush an index | `indices_actions.spec.ts` › flushes an index | stateful | | should refresh an index | › refreshes an index | stateful | | should force merge an index | › force merges an index | stateful | | should allow to define the number of segments | › force merges an index into a given number of segments | stateful | | should list all the indices with the expected properties and data enrichers | `indices.spec.ts` › lists the indices with the expected properties | agnostic | | should list all the indices … (reload) | › reloads the indices with the expected properties | agnostic | | should allow reloading only certain indices | › reloads only the requested indices | agnostic | | should get the index mappings (`mapping.ts`) | `mappings.spec.ts` › gets the index mappings | agnostic | | show update the index mappings | › updates the index mappings | agnostic | | should fetch an index settings (`settings.ts`) | `settings.spec.ts` › fetches an index settings | agnostic | | should update an index settings | › updates an index settings | agnostic | | reports no default repository when none is configured (`snapshot_repositories.ts`) | `snapshot_repositories.spec.ts` › same | stateful | | reports the configured default repository | › same | stateful | | should fetch the index stats (`stats.ts`) | `stats.spec.ts` › fetches the index stats | stateful | | should list all the index templates … (`templates.ts`, get all) | `index_templates.spec.ts` › lists the index templates with the expected parameters (split by template kind — see below) | agnostic | | should return an index template with the expected parameters (get one) | › returns an index template with the expected parameters | agnostic | | should return a legacy index template with the expected parameters (get one) | `index_templates_legacy.spec.ts` › returns a legacy index template with the expected parameters | stateful | | should round-trip a data stream template with a frozen_after lifecycle | `index_templates.spec.ts` › round-trips a data stream template with a frozen_after lifecycle — previously stateful-only, now agnostic | agnostic | | should return an index template with the expected parameters (with index mode) | `index_templates_index_mode.spec.ts` › returns an index template carrying its index mode | agnostic | | should return a legacy index template with the expected parameters (with index mode) | `index_templates_legacy.spec.ts` › returns a legacy index template carrying an index mode | stateful | | returns … index mode if logsdb.enabled is … and prior_logs_usage is … (×4 loop, `templates.ts`) | `index_templates_logsdb_settings.spec.ts` › same (×4) | stateful | | should create an index template | `index_templates.spec.ts` › creates an index template | agnostic | | should create a legacy index template | `index_templates_legacy.spec.ts` › creates a legacy index template | stateful | | should throw a 409 conflict when trying to create 2 templates with the same name | `index_templates.spec.ts` › rejects creating two templates with the same name | agnostic | | should validate the request payload | › validates the request payload | agnostic | | should parse the ES error and return the cause (create) | › parses the Elasticsearch error of a create and returns its cause | agnostic | | should update an index template | › updates an index template | agnostic | | should update a legacy index template | `index_templates_legacy.spec.ts` › updates a legacy index template | stateful | | should parse the ES error and return the cause (update) | `index_templates.spec.ts` › parses the Elasticsearch error of an update and returns its cause | agnostic | | should update a deprecated index template | › updates a deprecated index template | agnostic | | should delete an index template | › deletes an index template | agnostic | | should delete a legacy index template | `index_templates_legacy.spec.ts` › deletes a legacy index template | stateful | | should simulate an index template | `index_templates.spec.ts` › simulates an index template | agnostic | | should simulate an index template by name | › simulates an index template by name | agnostic | | should simulate an index template by name with a related data stream | › simulates an index template by name with a related data stream | agnostic | `index_templates_ilm_policy.spec.ts`'s two tests (`lists`/`returns an index template carrying its ILM policy`) formalize ILM-policy coverage that was folded into the `templates.ts` `get all`/`get one` fixture setup rather than asserted as its own case in FTR — kept stateful-only since ILM doesn't run on serverless. ## Serverless API FTR → Scout | FTR test (file) | Scout test | Tag | |---|---|---| | should NOT fetch the nodes plugins in serverless (`cluster_nodes.ts`) | merged into `cluster_nodes.spec.ts` (410/no-array branch) | agnostic | | should return an array of component templates (`index_component_templates.ts`) | merged into `component_templates.spec.ts` | agnostic | | should return a single component template | merged | agnostic | | should allow an existing component template to be updated (`Update #1`) | merged | agnostic | | should not allow a non-existing component template to be updated (`Update #1`) | merged | agnostic | | should allow an existing component template to be updated (`Update #2`) | dropped — verbatim duplicate of `Update #1` | — | | should not allow a non-existing component template to be updated (`Update #2`) | dropped — verbatim duplicate of `Update #1` | — | | should delete a component template | merged | agnostic | | should delete multiple component templates | merged | agnostic | | should return an error for any component templates not sucessfully deleted | merged | agnostic | | should return no datastreams | merged | agnostic | | should return datastreams | merged | agnostic | | Allows to create an enrich policy (`create_enrich_policies.ts`) | merged into `create_enrich_policy.spec.ts` | agnostic | | Can retrieve fields from indices | merged | agnostic | | Can retrieve matching indices | merged | agnostic | | returns … index mode if logsdb.enabled is … (×3 loop, `ds_common.ts`) | `data_streams_logsdb_settings_serverless.spec.ts` › same (×3, no `prior_logs_usage`) | serverless | | updates the data retention of a DS | merged into `data_streams.spec.ts` | agnostic | | sets data retention to infinite | merged | agnostic | | deletes multiple data streams | merged | agnostic | | Apply mapping from index template | merged | agnostic | | Rollover datastreams | merged | agnostic | | returns an array of data streams (`ds_serverless.ts`, metering shape) | merged into `data_streams.spec.ts` (env-conditional stats via `forDeployment`) | agnostic | | returns a single data stream by ID | merged | agnostic | | returns an array of data streams (`ds_mki.ts`, security-project branch) | `data_streams_mki_security.spec.ts` › lists the data streams | serverless security complete | | returns a single data stream by ID | `data_streams_mki_security.spec.ts` › returns a single data stream by name | serverless security complete | | should list all policies (`enrich_policies.ts`) | merged into `enrich_policies.spec.ts` | agnostic | | should be able to execute a policy | merged | agnostic | | should be able to delete a policy | merged | agnostic | | returns the cluster default failed data retention with the expected shape (`failure_store_settings.ts`) | merged into `failure_store_settings.spec.ts` | agnostic | | reflects the persistent cluster default failed data retention override | merged | agnostic | | should list all the index templates with the expected parameters (`index_templates.ts`, all) | merged into `index_templates.spec.ts` | agnostic | | should list all the index templates … (with index mode) | `index_templates_index_mode.spec.ts` › lists an index template carrying its index mode | agnostic | | should return an index template with the expected parameters (one) | merged into `index_templates.spec.ts` | agnostic | | returns … index mode if logsdb.enabled is … (×3 loop, with logs-*-* pattern) | `index_templates_logsdb_settings_serverless.spec.ts` › same (×3, no `prior_logs_usage`) | serverless | | should return an index template with the expected parameters (with index mode) | `index_templates_index_mode.spec.ts` › returns an index template carrying its index mode | agnostic | | should create an index template | merged into `index_templates.spec.ts` | agnostic | | should throw a 409 conflict when trying to create 2 templates with the same name | merged | agnostic | | should validate the request payload | merged | agnostic | | should parse the ES error and return the cause (create) | merged | agnostic | | should update an index template | merged | agnostic | | should parse the ES error and return the cause (update) | merged | agnostic | | should delete an index template | merged | agnostic | | should simulate an index template | merged | agnostic | | should list all the indices with the expected properties (`indices.ts`) | merged into `indices.spec.ts` (serverless expected-keys branch) | agnostic | | returns index details for the specified index name | merged | agnostic | | throws 404 for a non-existent index | merged | agnostic | | can create a new index | merged | agnostic | | fails to re-create the same index | merged | agnostic | | should list all the indices … (reload) | merged | agnostic | | should allow reloading only certain indices | merged | agnostic | | should delete an index | merged | agnostic | | should require index or indices to be provided | merged | agnostic | | should get the index mappings (`mappings.ts`) | merged into `mappings.spec.ts` | agnostic | | should fetch an index settings (`settings.ts`) | merged into `settings.spec.ts` | agnostic | | should update an index settings | merged | agnostic | ## Deltas worth calling out **Design decision, not a regression**: the Security-MKI project-level default retention (elastic#241105) is asserted exclusively in `data_streams_mki_security.spec.ts` (Cloud-only, `serverless.security.complete`), while `data_streams.spec.ts` explicitly skips those same 3 assertions (`isMkiSecurity`) and defers to it. This is the same mutually-exclusive split the old FTR had (`ds_serverless.ts` generic assertions vs. `ds_mki.ts` security-project branch) — reimplemented rather than dropped or loosened. **Coverage gained on serverless**: `create_enrich_policy.spec.ts`'s data-stream discovery cases (`get_fields_from_indices`/`get_matching_data_streams`) and `index_templates.spec.ts`'s `frozen_after` lifecycle round-trip were stateful-only in FTR pending serverless verification; both are now `deploymentAgnostic`. `component_templates.spec.ts`'s whole Create block (4 tests) had no serverless FTR equivalent (`index_component_templates.ts` lacked a Create describe) and is now agnostic too. The serverless-only `logsdb.enabled` matrix for index templates (`SL-API/index_templates.ts`'s "with logs-*-* index pattern", 3 cases) also has a Scout home now — `index_templates_logsdb_settings_serverless.spec.ts` — mirroring `data_streams_logsdb_settings_serverless.spec.ts`'s pattern (unset `cluster.logsdb.enabled` defaults to `logsdb` on serverless, the opposite of stateful). **Dropped as duplicates (no coverage lost)**: `SL-API/index_component_templates.ts`'s `Update #2` describe block (verbatim copy of `Update #1`); `SF-API/create_index.ts`'s `can create an index` (duplicate of `indices.ts`'s own create case, folded per the documented migration-plan decision). </details> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
nickofthyme
pushed a commit
that referenced
this pull request
Aug 18, 2026
…pearing when source config is slow/fails (elastic#279610) (elastic#280121) ## Summary Fixes [elastic#279610](elastic#279610): the Metric Threshold rule form's Conditions section silently disappears when `/api/metrics/source/{sourceId}` is slow or fails, and the server-side ES call behind that endpoint had no bound on how long it could hang. Two root causes, both addressed: **1. Client — no loading/error fallback in the Conditions section** `Expressions` (`x-pack/solutions/observability/plugins/infra/public/alerting/metric_threshold/components/expression.tsx`) only rendered the condition rows when `metricsView` was truthy (`{metricsView && ruleParams.criteria.map(...)}`). `metricsView` comes from `useMetricsDataViewContext`, which itself depends on `useSourceContext`'s fetch of the source configuration resolving first. If that fetch was pending or failed, `metricsView` stayed `undefined` and the whole Conditions section just rendered nothing — no spinner, no error, so a user could save a rule believing it was correctly configured when it wasn't. Fix: added explicit loading and error branches around the Conditions section, driven by `isLoading`/`error` from `useSourceContext` and `loading`/`error` from `useMetricsDataViewContext`: - While either is loading, show an `EuiLoadingSpinner`. - If either failed, show an `EuiCallOut` (danger) with the error message and a "Try again" button that re-triggers both `loadSource()` and the metrics view `refetch()`. This mirrors the existing `SourceStatusWrapper` pattern already used by the Log Threshold rule's expression editor (`x-pack/solutions/observability/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx`), so no new UI pattern was introduced. Additionally, the metric-threshold form now requests the source configuration with `includeStatus=false`, so the `/api/metrics/source/{sourceId}` call it makes skips the source-status (`composeSourceStatus`/`getIndexStatus`) work entirely. This is what actually protects the form for elastic#279610 — it no longer waits on the ES status probe at all — and it keeps the response focused on the config the form needs. **2. Server — unbounded Elasticsearch search could hang indefinitely** `InfraElasticsearchSourceStatusAdapter#getIndexStatus` (`x-pack/solutions/observability/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts`, used by the `/api/metrics/source/{sourceId}` route) issued an ES `search` (`size: 0, terminate_after: 1, track_total_hits: 1`) with no timeout. Against an overloaded/unhealthy cluster, or an unreachable remote-cluster pattern in a customized `metricAlias`, this could hang for minutes (one real-world report cites ~6 minutes before Cloud's reverse proxy 502'd while Kibana was still waiting). Fix: added a `requestTimeout: '30s'` to that search call. `requestTimeout` is threaded through `KibanaFramework#callWithRequest` as a transport-level option (extracted from the request body params and passed alongside the existing `AbortSignal` to the underlying ES client call), following the same `requestTimeout` convention already used elsewhere in the codebase (e.g. `observability_ai_assistant_app/server/functions/query/validate_esql_query.ts`, `security_solution/.../fetch_index_patterns.ts`). When the search now times out or errors, it rejects instead of hanging, and the existing `composeSourceStatus`/route error handling in `routes/metrics_sources/index.ts` takes over from there. **Note on scope:** with the metric-threshold form now passing `includeStatus=false` (see root cause #1), `composeSourceStatus`/`getIndexStatus` is no longer called on the form's path — so the form is protected by *skipping* the status call, not by this timeout. The `requestTimeout: '30s'` is therefore **defense-in-depth for the remaining `includeStatus=true` callers** (inventory, hosts, etc.), bounding a previously-unbounded ES status probe so it can't hang indefinitely, rather than the direct fix for the form that motivated this PR. ### Testing - Added `expression.test.tsx` cases for the client: loading spinner while source/metrics-view are resolving, and an error callout (with working "Try again" retry) when either fails. - Added a new `elasticsearch_source_status_adapter.test.ts` covering: `requestTimeout` is forwarded to the underlying `search` call, a timeout/error from the ES call propagates instead of hanging, and the existing 404 → `"missing"` mapping still works. ### Verification - Could not run a full `yarn kbn bootstrap` / Jest in this environment (no network-feasible full install within the available time/disk); the changes were verified by careful manual read-through against the current `main` branch code. - Ran a scoped TypeScript syntax check (`tsc --noEmit` with a minimal standalone config, not the repo's full project references) against the changed and added files — no syntax errors; only expected module-resolution noise from not having `node_modules`/`@kbn/*` path mappings available. - Did **not** execute the Jest suites in this environment — please run `node scripts/jest --config x-pack/solutions/observability/plugins/infra/jest.config.js expression.test elasticsearch_source_status_adapter.test` (or equivalent) as part of review/CI. ### Release notes Fixed the Metric Threshold rule form silently showing no conditions when its backing source-configuration request was slow or failed, instead of showing a loading or error state. The source-status Elasticsearch request also now times out instead of hanging indefinitely. Suggested label: `release_note:fix`. ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes i18n support - [ ] Documentation was added for features that require explanation or tutorials (not applicable — bug fix, no user-facing behavior/config changes beyond the fix itself) - [x] Unit tests were added to match the most common scenarios (loading/error UI states, server-side timeout behavior) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker list (not applicable — no config keys changed) - [x] This was checked for breaking HTTP API changes — none; the route gains an optional `includeStatus` query param (defaults to `true`, preserving existing behavior) and its response shape is otherwise unchanged - [ ] Flaky Test Runner was used on any tests changed (not run in this environment — see Verification above) - [x] The PR description includes the appropriate Release Notes section (see above) — `release_note:*` label needs a maintainer to apply, see note below - [ ] Review the backport guidelines and apply applicable `backport:*` labels — needs a maintainer, see note below ### Identify risks - **Behavior change under slow clusters (`includeStatus=true` callers only)**: `getIndexStatus` will now fail after 30s instead of eventually succeeding on a very slow (but not truly hung) cluster. Severity: low — the existing `composeSourceStatus` caller already treats a thrown error as "assume status unknown" and falls back to a default status rather than failing the whole `/api/metrics/source/{sourceId}` request, so this trades an indefinite hang for a bounded, previously-already-handled error path. The metric-threshold form is unaffected since it no longer requests status. - **New `requestTimeout` param on `callWithRequest`**: scoped to the `'search'` case only and is opt-in (only applied when explicitly passed), so other callers of `callWithRequest` are unaffected. The type is documented to note it's currently only honored for the `search` endpoint. ## Note on this PR I'm not an Elastic employee — two things need a maintainer: 1. A CLA (https://www.elastic.co/contributor-agreement/) needs to be signed on this account before this can be merged; expect a CLA-check failure until then. 2. The `release_note:*` and `backport:*` labels required by prbot can't be self-applied by external contributors (verified — the API rejects it for insufficient permissions). Suggested `release_note:fix`; happy to defer on the backport label to whatever this repo's current policy is for a bug fix of this kind. --------- Co-authored-by: Miguel Martín <miguel.martin@elastic.co>
markov00
added a commit
that referenced
this pull request
Sep 15, 2026
…p value (elastic#288956) (elastic#290653) # Backport This will backport the following commits from `main` to `9.5`: - [[Lens] Assert truncate formatter contract instead of pinning top value (elastic#288956)](elastic#288956) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Kibana Machine","email":"42973632+kibanamachine@users.noreply.github.com"},"sourceCommit":{"committedDate":"2026-09-11T17:25:08Z","message":"[Lens] Assert truncate formatter contract instead of pinning top value (elastic#288956)\n\nFixes elastic#287650\n\n### Summary\n- The `keyword formatters > should display truncate string formatter\ncorrectly` test failed with `expected 'ant...' to sort of equal\n'dal...'`.\n- The datatable is a `Top 9 values` terms aggregation over a runtime\nfield emitting `links.raw`, spanning a 3-shard `logstash-*` data view.\nMulti-shard terms aggregations are approximate, so when candidate values\nare tightly clustered in count the #1 slot flips between runs (`dal...`\nvs `ant...`) — both are valid truncations.\n- This patch asserts the truncation *contract* (3 chars + `...`) via\n`/^.{3}\\.\\.\\.$/` instead of pinning a specific top value, since the\n`TRUNCATE` formatter — not which value ranks first — is what this test\nverifies.\n\n### Context\n- Follows the fix proposed by the [Failed Test\nInvestigator](https://github.com/elastic/kibana/issues/287650#issuecomment-5524651204):\nthe returned cell was a valid truncation, so the formatter rendered\ncorrectly; only the non-deterministic underlying top value changed. This\nis expected Elasticsearch behavior, not a product or timing bug.\n- The new assertion still fails if truncation regresses (wrong length or\nmissing ellipsis), so formatter coverage is preserved. `.match(RegExp)`\nis supported by `@kbn/expect`.\n- Failed twice on `kibana-on-merge` (main) — the local pipeline, not\nCloud-specific — consistent with a data/assertion issue rather than an\nenvironment one.\n\n<details>\n<summary>Verification</summary>\n\n#### Verified locally\n\n✅ Passed: `node scripts/eslint\nx-pack/platform/test/functional/apps/lens/group2/field_formatters.ts`\n\n#### Not verified locally\n\n- This is an FTR test requiring a live Elasticsearch + Kibana, which\ncannot be run in this environment. Its CI covers the change once the PR\nis open.\n\n</details>\n\n> [!NOTE]\n> Share feedback in #kibana-qa. Mention `@copilot` to make quick\nchanges.\n\n> Generated by [Flaky Test\nFixer](https://github.com/elastic/kibana/actions/runs/33747031300) for\n#287650 · opus · 156.2 AIC · ⌖ 38 AIC · ⊞ 12.8K ·\n[◷](https://github.com/search?q=repo%3Aelastic%2Fkibana+%22gh-aw-workflow-id%3A+flaky-test-fixer%22&type=pullrequests)\n\n\n\n\n\n\nCo-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>\nCo-authored-by: Marco Vettorello <marco.vettorello@elastic.co>","sha":"dfab8eefca509d3f9b3060e22108ca6a0350cb8f","branchLabelMapping":{"^v9.6.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:all-open","flaky-test-fixer","flaky-fix-check:passed","v9.6.0"],"title":"[Lens] Assert truncate formatter contract instead of pinning top value","number":288956,"url":"https://github.com/elastic/kibana/pull/288956","mergeCommit":{"message":"[Lens] Assert truncate formatter contract instead of pinning top value (elastic#288956)\n\nFixes elastic#287650\n\n### Summary\n- The `keyword formatters > should display truncate string formatter\ncorrectly` test failed with `expected 'ant...' to sort of equal\n'dal...'`.\n- The datatable is a `Top 9 values` terms aggregation over a runtime\nfield emitting `links.raw`, spanning a 3-shard `logstash-*` data view.\nMulti-shard terms aggregations are approximate, so when candidate values\nare tightly clustered in count the #1 slot flips between runs (`dal...`\nvs `ant...`) — both are valid truncations.\n- This patch asserts the truncation *contract* (3 chars + `...`) via\n`/^.{3}\\.\\.\\.$/` instead of pinning a specific top value, since the\n`TRUNCATE` formatter — not which value ranks first — is what this test\nverifies.\n\n### Context\n- Follows the fix proposed by the [Failed Test\nInvestigator](https://github.com/elastic/kibana/issues/287650#issuecomment-5524651204):\nthe returned cell was a valid truncation, so the formatter rendered\ncorrectly; only the non-deterministic underlying top value changed. This\nis expected Elasticsearch behavior, not a product or timing bug.\n- The new assertion still fails if truncation regresses (wrong length or\nmissing ellipsis), so formatter coverage is preserved. `.match(RegExp)`\nis supported by `@kbn/expect`.\n- Failed twice on `kibana-on-merge` (main) — the local pipeline, not\nCloud-specific — consistent with a data/assertion issue rather than an\nenvironment one.\n\n<details>\n<summary>Verification</summary>\n\n#### Verified locally\n\n✅ Passed: `node scripts/eslint\nx-pack/platform/test/functional/apps/lens/group2/field_formatters.ts`\n\n#### Not verified locally\n\n- This is an FTR test requiring a live Elasticsearch + Kibana, which\ncannot be run in this environment. Its CI covers the change once the PR\nis open.\n\n</details>\n\n> [!NOTE]\n> Share feedback in #kibana-qa. Mention `@copilot` to make quick\nchanges.\n\n> Generated by [Flaky Test\nFixer](https://github.com/elastic/kibana/actions/runs/33747031300) for\n#287650 · opus · 156.2 AIC · ⌖ 38 AIC · ⊞ 12.8K ·\n[◷](https://github.com/search?q=repo%3Aelastic%2Fkibana+%22gh-aw-workflow-id%3A+flaky-test-fixer%22&type=pullrequests)\n\n\n\n\n\n\nCo-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>\nCo-authored-by: Marco Vettorello <marco.vettorello@elastic.co>","sha":"dfab8eefca509d3f9b3060e22108ca6a0350cb8f"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.6.0","branchLabelMappingKey":"^v9.6.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/288956","number":288956,"mergeCommit":{"message":"[Lens] Assert truncate formatter contract instead of pinning top value (elastic#288956)\n\nFixes elastic#287650\n\n### Summary\n- The `keyword formatters > should display truncate string formatter\ncorrectly` test failed with `expected 'ant...' to sort of equal\n'dal...'`.\n- The datatable is a `Top 9 values` terms aggregation over a runtime\nfield emitting `links.raw`, spanning a 3-shard `logstash-*` data view.\nMulti-shard terms aggregations are approximate, so when candidate values\nare tightly clustered in count the #1 slot flips between runs (`dal...`\nvs `ant...`) — both are valid truncations.\n- This patch asserts the truncation *contract* (3 chars + `...`) via\n`/^.{3}\\.\\.\\.$/` instead of pinning a specific top value, since the\n`TRUNCATE` formatter — not which value ranks first — is what this test\nverifies.\n\n### Context\n- Follows the fix proposed by the [Failed Test\nInvestigator](https://github.com/elastic/kibana/issues/287650#issuecomment-5524651204):\nthe returned cell was a valid truncation, so the formatter rendered\ncorrectly; only the non-deterministic underlying top value changed. This\nis expected Elasticsearch behavior, not a product or timing bug.\n- The new assertion still fails if truncation regresses (wrong length or\nmissing ellipsis), so formatter coverage is preserved. `.match(RegExp)`\nis supported by `@kbn/expect`.\n- Failed twice on `kibana-on-merge` (main) — the local pipeline, not\nCloud-specific — consistent with a data/assertion issue rather than an\nenvironment one.\n\n<details>\n<summary>Verification</summary>\n\n#### Verified locally\n\n✅ Passed: `node scripts/eslint\nx-pack/platform/test/functional/apps/lens/group2/field_formatters.ts`\n\n#### Not verified locally\n\n- This is an FTR test requiring a live Elasticsearch + Kibana, which\ncannot be run in this environment. Its CI covers the change once the PR\nis open.\n\n</details>\n\n> [!NOTE]\n> Share feedback in #kibana-qa. Mention `@copilot` to make quick\nchanges.\n\n> Generated by [Flaky Test\nFixer](https://github.com/elastic/kibana/actions/runs/33747031300) for\n#287650 · opus · 156.2 AIC · ⌖ 38 AIC · ⊞ 12.8K ·\n[◷](https://github.com/search?q=repo%3Aelastic%2Fkibana+%22gh-aw-workflow-id%3A+flaky-test-fixer%22&type=pullrequests)\n\n\n\n\n\n\nCo-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>\nCo-authored-by: Marco Vettorello <marco.vettorello@elastic.co>","sha":"dfab8eefca509d3f9b3060e22108ca6a0350cb8f"}}]}] BACKPORT--> Co-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com> Co-authored-by: Marco Vettorello <marco.vettorello@elastic.co>
markov00
pushed a commit
that referenced
this pull request
Oct 2, 2026
## Summary Adds a new `.gitlab` connector spec for the GitLab REST API v4 with enterprise license, bearer (PAT) auth, and agentBuilder support. 23 actions across read/write/destroy scopes: - Read: getCurrentUser, searchProjects, getProject, searchUsers, listIssues, getIssue, listMergeRequests, getMergeRequest, listBranches, getFile, listCommits, listPipelines - Write: createIssue, addIssueNote, addMergeRequestNote, createMergeRequest, requestMergeRequestReview, createBranch, triggerPipeline - Destroy: updateIssue, updateMergeRequest, acceptMergeRequest, createOrUpdateFile All projectId/issueIid/mrIid fields are consistently typed as strings. 59 unit tests, full type check and lint clean. ### Checklist 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) - [x] [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, 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), or updated in external injectors such as kibana-controller; unused in this repo is not unused in production — prefer [`rename()`](https://github.com/elastic/kibana/blob/main/docs/extend/tutorials/configuring-your-plugin.md#handle-plugin-configuration-deprecations) over a hard cut - [ ] 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 - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/docs/extend/kibana/contributing/workflow/how-we-use-github#release-notes) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ### Testing --- # GitLab Connector Test Report (Run 3 — Full Write Coverage) **Connector ID:** `gitlab` **Account:** `erik-elastic` (ID `42560354`) **Test project:** `elastic-group790538/Elastic-project` (ID `86593242`) **Tested by:** `test-connector` skill --- ## Action Coverage | Tier | Action | Sub-Action | Key Params | Result | Notes | |------|--------|------------|------------|--------|-------| | 1 | Get current user | `getCurrentUser` | — | ✅ PASS | `erik-elastic` profile returned | | 1 | Search projects | `searchProjects` | `search="elastic", perPage=5` | ✅ PASS | 5 results | | 1 | Search users | `searchUsers` | `search="a", perPage=5` | ✅ PASS | 5 users returned | | 1 | List issues (closed) | `listIssues` | `state="closed"` | ✅ PASS | Issue #1 confirmed closed | | 1 | List MRs (merged) | `listMergeRequests` | `state="merged"` | ✅ PASS | MR !1 confirmed merged | | 1 | List branches | `listBranches` | — | ✅ PASS | Only `main` (feature branch deleted post-merge as expected) | | 1 | List commits | `listCommits` | — | ✅ PASS | 3 commits: init, feat, merge commit | | 1 | List pipelines | `listPipelines` | — |⚠️ WARN | `[]` — no CI config in project, no pipelines run | | 2 | Get project | `getProject` | `projectId="86593242"` | ✅ PASS | Full metadata returned | | 2 | Get issue | `getIssue` | `issueIid=1` | ✅ PASS | Verified via `listIssues` (state=closed) | | 2 | Get merge request | `getMergeRequest` | `mrIid=1` | ✅ PASS | Verified via `listMergeRequests` (state=merged) | | 2 | Get file | `getFile` | `filePath="README.md", ref="main"` | ✅ PASS | Base64 content + commit SHA returned | | W | Create file | `createOrUpdateFile` | `README.md` on `main` | ✅ PASS | Seeded repo from empty state | | W | Create branch | `createBranch` | `feature/smoke-test` from `main` | ✅ PASS | Branch created at `171e924a` | | W | Create file (branch) | `createOrUpdateFile` | `smoke-test.md` on `feature/smoke-test` | ✅ PASS | Commit `4f64c02f` created | | W | Create issue | `createIssue` | labels=`test,smoke-test` | ✅ PASS | Issue #1 created with description | | W | Add issue note | `addIssueNote` | `issueIid=1` | ✅ PASS | Note ID `3853088289` confirmed | | W | Update issue | `updateIssue` | close + add `verified` label, new title | ✅ PASS | Issue closed, title + 3 labels set | | W | Create MR | `createMergeRequest` | `feature/smoke-test` → `main` | ✅ PASS | MR !1 created, `mergeable` status | | W | Add MR note | `addMergeRequestNote` | `mrIid=1` | ✅ PASS | Note ID `3853092333` confirmed | | W | Update MR | `updateMergeRequest` | new title, description, labels | ✅ PASS | Title, description, labels all updated | | W | Request MR review | `requestMergeRequestReview` | `reviewerIds=[42560354]` | ✅ PASS | `erik-elastic` set as reviewer | | W | Accept MR | `acceptMergeRequest` | custom commit message, delete source branch | ✅ PASS | Merged at `486a7136`, source branch deleted | | W | Trigger pipeline | `triggerPipeline` | `ref="main"` | ❌ FAIL | HTTP 400 — no `.gitlab-ci.yml` in project | | 3 | Paginated search | `searchProjects` | `page=2, perPage=2` | ✅ PASS | Correct page-2 results | | 3 | Limit=1 search | `searchProjects` | `perPage=1` | ✅ PASS | Exactly 1 result | | 3 | Filtered user search | `searchUsers` | `search="erik", perPage=1` | ✅ PASS | Matched `Erik` | --- ## Summary | | Count | |--|--| | ✅ Pass | **25** | |⚠️ Warn | **1** | | ❌ Fail | **1** | | ⏭️ Skip | **0** | | **Total** | **27** | --- ## Findings ### ❌ `triggerPipeline` — HTTP 400 - **Cause:** The test project has no `.gitlab-ci.yml` file. GitLab rejects pipeline triggers on projects with no CI configuration. This is **not a connector bug** — the action correctly forwarded the request and returned the API error. - **Fix for complete coverage:** Add a minimal `.gitlab-ci.yml` to the repo (e.g. a single `echo` job), then retry `triggerPipeline`. ###⚠️ `listPipelines` — empty - Same root cause as above. Returns `[]` because no pipelines have ever run. Expected once a CI config is added. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
Oct 2, 2026
## Summary Adds a **Bitbucket Cloud** connector spec (`.bitbucket`) so workflows and agents can drive the pull-request, branch, commit-status, and pipeline lifecycle on a Bitbucket Cloud workspace without opening the Bitbucket UI or hand-writing REST calls. Closes elastic/security-team#18429 ### Actions | Group | Actions | |---|---| | Discovery | `listRepositories` | | Pull requests | `createPullRequest`, `getPullRequest`, `listPullRequests`, `updatePullRequest`, `mergePullRequest`, `approvePullRequest`, `declinePullRequest`, `addPullRequestComment` | | Branches | `createBranch`, `getBranch`, `deleteBranch` | | Commits | `getCommit`, `listCommits`, `createCommitBuildStatus`, `listCommitBuildStatuses` | | Pipelines | `triggerPipeline`, `getPipeline`, `stopPipeline` | Every action returns a curated camelCase shape (no `links`/avatar noise) and surfaces Bitbucket API errors as `Bitbucket <action> failed (status N): <message> - <detail>`. ### Authentication - **Basic (recommended)**: Atlassian account email + API token created **with scopes** for the Bitbucket app. Bitbucket rejects unscoped API tokens (`API Token provided has no Bitbucket scopes.`), so the in-product help text, the docs page's Authentication line, and the docs' setup steps all list the same six scopes. - **Bearer**: Bitbucket repository/project/workspace access token (or a scoped API token). A repository-scoped access token cannot list the whole workspace, so the connectivity test recognizes a 401/403 from that call under bearer auth and reports the scope limitation instead of reporting valid credentials as broken; every repository-scoped action still works with that token. The connector config holds the **workspace** slug; repositories are addressed by slug in every action. #### Deviation from the issue: no app password option The issue's first acceptance criterion asks for "a workspace and an app password (or an Atlassian email and API token)". This PR deliberately does **not** offer app passwords: - Atlassian [deprecated Bitbucket Cloud app passwords](https://community.atlassian.com/forums/Bitbucket-articles/Deprecation-notice-Bitbucket-Cloud-shifts-to-API-tokens-from-app/ba-p/3040975) in favour of API tokens. Creating new app passwords was disabled on 9 September 2025, and existing app passwords stopped authenticating on 9 June 2026, so at the time of this PR there is no working app password a user could enter. - The replacement, an Atlassian API token **with Bitbucket scopes**, is what the Basic auth option takes (email + token). Technically the same Basic auth fields would have accepted a Bitbucket username + app password while those still worked, but documenting that path would send users to a UI that no longer exists. - For non-user (service) identities, the Bearer option covers Bitbucket repository, project, and workspace access tokens, which is Atlassian's recommended replacement for automation that previously used app passwords. ### Notes for reviewers - `supportedFeatureIds` is `['agentBuilder']` only, per the two-step release rule for new connector types. A follow-up PR will add `'workflows'` once this type is registered in every Production-NonCanary version. The docs page carries a note stating this connector is currently Agent Builder only. - Vendor quirks verified against the live API and handled in code: - Bitbucket ignores the `state` query params **and** its OPEN-only default whenever a `q` filter is present, so `listPullRequests` folds the state filter into `q` when a query is given. `state` also requires `.min(1)` so an empty array cannot produce a malformed `() AND (...)` query or silently fall back to the OPEN default. - Repeated-key form (`state=OPEN&state=MERGED`) is required for multi-state filters; the axios default bracket form is silently ignored. - `PUT /pullrequests/{id}` preserves omitted `title`/`description`, but reviewer handling on omission is undocumented, so `updatePullRequest` reads the current PR and always sends the full field set. - UUIDs (users, pipelines) must be brace-wrapped; the handlers normalise inputs with or without braces. - Bitbucket's commits endpoint paginates with an opaque cursor embedded in the `next` link, not sequential page numbers - its own docs say to follow that link rather than construct one. `listCommits` now returns `nextCursor`/`hasMore` and accepts the cursor back as input, requesting it directly instead of rebuilding it. The cursor is caller-supplied, so it is rejected unless it starts with the Bitbucket API base URL, to keep a workflow author from redirecting the connector's credentials to an arbitrary host. - `createCommitBuildStatus` is `scope: 'destroy'`, not `write`: reposting an existing key overwrites the previous status, which the `ActionScope` contract reserves for updates/overwrites. `listCommitBuildStatuses` (new) is the read counterpart, since `getPullRequest` has no status fields and nothing else could verify a build status before merging. - Icon is the Bitbucket mark from Simple Icons in Atlassian blue (`#0052CC`). - Docs quality skills (`docs-check-style` etc.) from elastic-docs-skills were not installed in the authoring environment; the page follows the Buildkite/Jira Cloud connector docs structure. ## Validated Every action was exercised end-to-end through the Kibana Actions `_execute` API against a live Bitbucket Cloud workspace (`flash1293`, private repo `test`, and separately `semla`/`foo`) using a scoped Atlassian API token. Two Agent Builder chat tests (agent wired to `sml_search`/`sml_attach`/`execute_connector_sub_action`) drove `listRepositories`, `listPullRequests` (multi-state), `getBranch`, `triggerPipeline` (custom pipeline + variables), `stopPipeline`, `getPipeline`, and `createCommitBuildStatus` with all optional fields, choosing correct sub-action names and parameters from the spec descriptions alone. The first chat also exposed that list responses omit participants, so `approvalCount`/`reviewers`/`participants` are now `undefined` rather than `0`/`[]` when Bitbucket does not return them. | Action | What was tested | Result | | --- | --- | --- | | test (connectivity) | `_test` sub-action with email + scoped API token; returned `Connected to Bitbucket workspace "flash1293"` / `"semla"`. Also confirmed a repository-scoped bearer token's 403 on this workspace-wide call is reported as a scope limitation, not a generic failure | ✅ Pass | | `listRepositories` | Listed workspace repos; returned the private `test`/`foo` repo with slug, main branch, project key, URL, `hasMore: false` | ✅ Pass | | `createPullRequest` | Opened PR #1 from `kibana-e2e/remediation-*` → `main` with description and `closeSourceBranch: true`; opened PR #2 as `draft: true` with default destination; also confirmed Bitbucket's "no changes to be pulled" and "author cannot be a reviewer" errors surface verbatim | ✅ Pass | | `getPullRequest` | Read PR #1 before/after approval; `approvalCount` went 0 → 1, `commentCount` 3, `sourceCommit`/`destinationBranch` populated | ✅ Pass | | `listPullRequests` | Default (OPEN only), `state: ["MERGED","DECLINED"]` with `sort`, `state` + `query: title ~ "Remediate"`, and an empty `state: []` rejected at schema validation before any request | ✅ Pass | | `updatePullRequest` | Title-only update preserved description; `reviewers: []` + multi-line description update; rejected with no fields set | ✅ Pass | | `mergePullRequest` | Squash merge of a non-draft PR with custom message and `closeSourceBranch: true`; state `MERGED`, `mergeCommit` returned, source branch gone (`getBranch` → 404); merging a draft PR correctly rejected | ✅ Pass | | `approvePullRequest` | Approved a PR; returned participant with `approved: true`, `state: "approved"` | ✅ Pass | | `declinePullRequest` | Declined a PR → `DECLINED`; second decline surfaced `status 400: This pull request is already closed.` | ✅ Pass | | `addPullRequestComment` | General comment with markdown link, inline comment on a file line (`inline.to`), threaded reply via `parentCommentId` | ✅ Pass | | `createBranch` | From a full commit hash and from the branch name `main`; branch names containing `/` are URL-encoded | ✅ Pass | | `getBranch` | `main` and a `kibana-e2e/...` branch; tip commit, parents, merge strategies returned; nonexistent branch 404s cleanly | ✅ Pass | | `deleteBranch` | Deleted a merged/declined PR's branch (`{deleted: true}`); deleting `main` surfaced `status 400: Cannot delete the main branch` | ✅ Pass | | `getCommit` | Read a commit by full hash; author user, parents, URL returned; non-hex hash rejected by schema | ✅ Pass | | `listCommits` | `revision` + `path` filter, no revision with `pageSize`, and multi-page traversal via the new opaque `cursor`/`nextCursor` following Bitbucket's documented `next`-link pagination; an off-origin cursor rejected before any request | ✅ Pass | | `createCommitBuildStatus` | `INPROGRESS` then `SUCCESSFUL` on the same key (overwrite confirmed, hence `scope: 'destroy'`) with `refname` = PR source branch | ✅ Pass | | `listCommitBuildStatuses` | Listed statuses on a commit that had a status posted by `createCommitBuildStatus`, confirming the value round-trips | ✅ Pass | | `triggerPipeline` | Branch-only (default pipeline, completed `SUCCESSFUL`), branch + commit + `customPipeline` + secured/unsecured `variables`, and bare commit + custom (`pipeline_commit_target`) | ✅ Pass | | `getPipeline` | Polled with and without braces; observed `PARSING` → `PENDING` → `COMPLETED`/`SUCCESSFUL` and `COMPLETED`/`STOPPED` | ✅ Pass | | `stopPipeline` | Stopped a running pipeline; second stop surfaced `status 400: Cannot stop pipeline result that is already complete with status STOPPED` | ✅ Pass | ## Testing this yourself The Elastic Atlassian organization does not have Bitbucket enabled, so you cannot test this connector with your Elastic Atlassian account. To try it: 1. Create a **personal Atlassian account** (any email) at https://id.atlassian.com/ and create a free Bitbucket Cloud workspace with a private repository. 2. Create an API token **with scopes** at https://id.atlassian.com/manage-profile/security/api-tokens, select the Bitbucket app, and grant `read/write:repository`, `read/write:pullrequest`, and `read/write:pipeline`. Tokens without scopes are rejected by the Bitbucket API. 3. In Kibana, create a Bitbucket connector with your workspace slug, the account email, and the token, then use the Test tab or run sub-actions via `POST /api/actions/connector/{id}/_execute`. ## Test plan - [x] `node scripts/check.js --scope=branch` (lint, Jest, tsc) - [x] `node scripts/jest src/platform/packages/shared/kbn-connector-specs/src/specs/bitbucket` (52 tests) - [x] `node scripts/type_check --project src/platform/packages/shared/kbn-connector-specs/tsconfig.json` - [x] `node scripts/i18n_check --path src/platform/packages/shared/kbn-connector-specs/src/specs/bitbucket` - [x] Live end-to-end run against Bitbucket Cloud (see Validated table) ## Follow-ups - Add `'workflows'` to `supportedFeatureIds` once the type is in every Production-NonCanary version. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01MDWdsgup91oM8zLp1m3Bos --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Coen Warmer <coen.warmer@gmail.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
markov00
pushed a commit
that referenced
this pull request
Oct 2, 2026
…patible endpoint (elastic#294736) ## Summary The inference plugin already supports `sessionId` + `cacheControl` for EIS prompt caching (elastic#284774), but the Elastic Console / Elastic Ramen OpenAI-compatible endpoint (`/internal/elastic_ramen/v1/chat/completions`) didn't forward them, so external agents (Elastic Ramen, pi, …) never got prompt caching. This PR adds that wiring: - **Session id** is resolved from (first match wins): 1. `prompt_cache_key` body field (standard OpenAI field; used by Elastic Ramen via `@ai-sdk/openai-compatible`) 2. `x-session-id` header (OpenRouter convention; pi sends this with `sessionAffinityFormat: "openrouter"`) 3. `x-session-affinity` header - When a session id is present, `cacheControl: { type: 'ephemeral', ttl: '5m' }` is set, matching the Agent Builder main loop. `prompt_cache_retention: "24h"` maps to the longest EIS TTL (`1h`). - Usage now includes `prompt_tokens_details.cached_tokens` whenever inference reports cached tokens, so clients like ai-sdk and pi can show cache reads. - Session ids are bounded to 256 chars (validated for the body field; oversized header values are ignored). - **Request body limit raised to 20MB** for `/chat/completions` (from the 1MB `server.maxPayload` default). Long agent conversations with large tool results or base64 images quickly go over 1MB, and every turn resends the full history. - **Message limits raised**: up to 10,000 messages per request (was 1,000) and 2,000 content parts per message (was 100). Each tool call adds an assistant message and a tool message, so long agent sessions hit 1,000 well before the context window fills. The total payload is still capped by the 20MB body limit. - README documents prompt caching and shows a verified pi `models.json` setup. Companion Elastic Ramen PR: elastic/elastic-ramen#122 ### Verification (local stack with EIS) Checked through Phoenix traces (`elastic.cache_control.*` span attributes): - `prompt_cache_key` in the body → `session_id` + `ttl: 5m` ✅ - `x-session-id` header → `session_id` + `ttl: 5m` ✅ - pi (`openai-completions` + compat config from README) → every turn of a pi session carries the pi session id ✅. With `PI_CACHE_RETENTION=long` → `ttl: 1h` ✅ Body limit: checked live. A ~5MB request returns `200`; a ~21MB request returns `413 Payload content length greater than maximum allowed: 20971520`. **Prompt cache hits (Claude Sonnet 5 on EIS):** I sent the same ~422k-token request 3 times with one `prompt_cache_key`: | Request | Time | `cached_tokens` | |---|---|---| | #1 | 8.3s | – (cache written) | | #2 | 2.1s | 422,347 / 422,349 | | elastic#3 | 2.0s | 422,347 / 422,349 | So the session id reaches EIS, EIS caches the prompt, and the new `prompt_tokens_details.cached_tokens` field reports the hit. Note: Claude Sonnet 4.5 on EIS doesn't report cached tokens, so no hits show up for that model. **Large inputs** (one-word answer hidden in the middle of the input, sent through the route): | Model | Prompt tokens | Result | |---|---|---| | Claude Sonnet 5 | 422k / 814k | ✅ / ✅ | | Gemini 2.5 Pro | 388k / 752k | ✅ / ✅ | | GPT-5.4 | 291k | ✅ | | Claude Sonnet 4.5 | 124k | ✅ (EIS context window looks like 200k) | **Long conversations:** a request with 5,001 messages returns `200`. That was over the old 1,000-message limit. ### Checklist - [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] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials ## Release Notes N/A (experimental feature)
markov00
pushed a commit
that referenced
this pull request
Oct 16, 2026
… is accepted (elastic#297598) Follow-up to the review on elastic#294874 (elastic#294874 (review)), part of elastic/nightshift-program#1619. ## Problem A start claimed its subjects right away, but the investigation's conversation was only created later by the workflow's `_ensure` step. If the run waited longer than the claim's 2-minute pending window (busy task queue, retries), the claim looked abandoned (holder conversation missing → `isHolderOpen` false), and the next start for the same subject opened a **second** investigation. Automated starts (alerts, Slack, automations) hit this most. ## Fix As suggested in the review: when a start opens a **new** investigation, it now creates the conversation as soon as the start is accepted and records the run's execution id on it (`workflow_execution_ids`). So: - the claim always points at an open investigation, however long the run waits; - the shared GET returns 200 with `in_progress=true` right after the start (it used to 404 for a few seconds). Details: - Created **after** `runWorkflow` returns: a denied start (quota) or a failed launch creates nothing, so no orphan conversations. The gap between claim and creation is now just the launch call, well inside the pending window. - Created as the start caller, the same identity the workflow runs as (same as the Slack thread path already does). `agentBuilder:write` on the start route is now a real requirement; route comment and description updated. - Subjects are still recorded only by `_ensure`, to avoid concurrent attachment writes on the conversation. `_ensure` uses get-or-create, so it picks up the existing conversation. - Follow-ups are unchanged (they don't create anything). - If creation fails, the start still succeeds and logs a warning; `_ensure` creates the conversation as before. ## Testing - Jest: `nightshift_investigations` `server/client` (208) + start route (12) ✅. New tests cover: created after claim and run, run recorded, no re-record if `_ensure` won the race, failure doesn't fail the start, no conversation on launch failure or follow-up. - type_check `nightshift_investigations` ✅, eslint ✅. - **Live e2e on a local stack (latest `main`, EIS LLM).** Script: start #1 with a named manual subject → read the shared GET → backdate the subject claim by 10 min in `.kibana-investigation-claim` (simulates a run stuck past the pending window) → start #2 with the same subject. | | shared GET right after start #1 | start #2 after the claim expired | |---|---|---| | without the fix (control) | **404** | **new investigation** `9cf076c7…` ≠ `97b3fbd7…` ❌ | | with the fix | **200**, `in_progress=true`, `subjects=[]` (before `_ensure` ran) | **same investigation** `2cabf882…` ✅ | - The investigation the start created ran end to end. Run 1 got a generated title ("Checkout Latency Investigation: …"), severity `low`, summary, verdict and 3 hypotheses, so the agent can still write to a conversation the start created. Run 2 (the follow-up from start #2) queued behind it and completed: 2 rounds, title kept, both execution ids recorded.
markov00
pushed a commit
that referenced
this pull request
Oct 19, 2026
# Relay dev certificate ## Summary Adds a development certificate for the Relay service, signed by the same development root CA as the other certificates in this package. Testing Kibana's Relay integration locally needs mTLS between Kibana and the Relay container. Kibana's side is already covered by `kibana.crt` / `kibana.key`, but there was no certificate for the Relay itself, so each developer generated an ad-hoc one against a throwaway CA. That meant a second trust anchor in the dev setup and a certificate that did not match what `xpack.actions.relay.ssl` expects. | Field | Value | |------------|--------------------------------------------------------------| | Subject | `CN=relay-service, O=Elastic` | | Issuer | `CN=Elastic Certificate Tool Autogenerated CA` | | Validity | 50 years, matching every other certificate here | | Key | RSA 2048-bit, sha256WithRSAEncryption | | SAN | `URI:spiffe://relay-service.elastic.co`, `DNS:localhost` | | EKU | TLS Web Client Authentication, TLS Web Server Authentication | Because it chains to the existing `ca.crt`, which `kbn-es` already mounts into the dev UIAM container, nothing new has to be trusted to use it. ## Changes - `certs/relay.crt`, `certs/relay.key`, `certs/relay.p12` - the certificate, its PKCS #1 private key, and the PKCS elastic#12 keystore. Password is `storepass`, the same as the other `*.p12` files, and the keystore uses the same AES-256 / PBKDF2 parameters as `kibana.p12`. - `src/certs.ts` + `index.ts` - exports `RELAY_CERT_PATH`, `RELAY_KEY_PATH`, `RELAY_P12_PATH` and `RELAY_P12_PASSWORD`, following the existing `KBN_*` / `FLEET_SERVER_*` shape, so dev tooling references them without an absolute path. - `certs/README.md` - documents the generation commands. > [!IMPORTANT] > The Relay certificate is generated with OpenSSL, not `elasticsearch-certutil`, because `certutil` cannot emit a > URI SAN or set both `clientAuth` and `serverAuth`. The README carries the full command sequence and the OpenSSL > config, so it is reproducible. It is still signed by the same `ca.p12`. This PR only adds the certificate and its exports. Wiring it into the dev Relay container is a followup. ## Testing ``` openssl verify -CAfile ca.crt relay.crt # relay.crt: OK ``` Certificate and key moduli match, and the keystore opens with `storepass`. I also ran the real handshake both ways: `relay.crt` as the TLS server with `-Verify 1 -verify_return_error`, `kibana.crt` as the mTLS client, which negotiates TLS 1.3 and verifies against `ca.crt` alone. _Note: these certificates should never be used in production._
markov00
pushed a commit
that referenced
this pull request
Oct 22, 2026
…he execution tree (elastic#299074) ## Summary When a step has both `on-failure.retry` and `on-failure.fallback`, the execution tree shows the `1-attempt`, `2-attempt`, and `fallback` rows as "Not run", even though they ran. The time of each attempt is not visible on these rows. The cause is in the UI. The engine data is correct: - The tree makes one synthetic row for each scope ID in the path (`N-attempt`, `fallback`). These rows have no step execution of their own, so they get the status `SKIPPED`, and the row shows "Not run". - `transformRetryAttempts` replaces the `N-attempt` rows with real attempt rows only when *all* children of the step are attempts. With a fallback, the step also has a `fallback` child, so the replacement does not occur. - The synthetic `fallback` row has the same problem, also without retry. This PR is a UI-only fix: - `build_step_executions_tree.ts`: collapse the attempts when the step has attempt children, and keep the other children (the `fallback` scope) after the attempts. - `workflow_step_execution_tree.tsx`: the synthetic `fallback` row gets its status and time from its child steps, in the same way as iteration rows. The retry parent keeps its wait rows and its total time from the attempts only, also when it has a `fallback` child. - The engine and the API data do not change. | Before | After | |--------|-------| |  |  | ### How to test 1. Create and run this workflow: ```yaml name: Fallback tree not run repro enabled: true triggers: - type: manual steps: - name: hello_world_step type: http with: url: "non-existing-url" on-failure: continue: true retry: max-attempts: 1 fallback: - name: console type: console with: message: "Hello World!" ``` 2. Open the execution. `hello_world_step` shows "2 of 2 attempts", with `Attempt #1` and `Attempt #2` and their times. 3. The `fallback` row shows a time, not "Not run". Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolved TODO comments questioning the correctness of unique ID generation for mixed logarithmic scale warnings in XY visualization. Analysis confirmed the current implementation is correct—unique IDs are properly scoped per axis (
groupId) and per dimension (accessor).Changes
visualization.tsxBefore:
After:
The implementation correctly uses string interpolation with
groupId('left'/'right') for axis-level warnings andaccessor(column ID) for dimension-level warnings. Constants inuser_messages_ids.tshave trailing hyphens ('xy_mixed_log_scale-groupid-') confirming the design intent for concatenation.Checklist
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.Identify risks
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
ci-stats.kibana.dev/home/REDACTED/.nvm/versions/node/v22.17.1/bin/node --no-experimental-require-module scripts/kbn bootstrap --quiet(dns block)download.cypress.io/home/REDACTED/.nvm/versions/node/v22.17.1/bin/node index.js --exec install(dns block)googlechromelabs.github.io/home/REDACTED/.nvm/versions/node/v22.17.1/bin/node install.js(dns block)iojs.orgcurl -q --fail --compressed -L -s REDACTED -o -(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
Created from VS Code via the GitHub Pull Request extension.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.