Skip to content

[Streams] Add field descriptions and documentation-only field overrides#255136

Merged
flash1293 merged 22 commits intoelastic:mainfrom
flash1293:ralph/field-descriptions-clean
Mar 11, 2026
Merged

[Streams] Add field descriptions and documentation-only field overrides#255136
flash1293 merged 22 commits intoelastic:mainfrom
flash1293:ralph/field-descriptions-clean

Conversation

@flash1293
Copy link
Copy Markdown
Contributor

@flash1293 flash1293 commented Feb 26, 2026

Summary

This PR adds support for field descriptions in Streams, allowing users to document fields without creating Elasticsearch mappings (documentation-only field overrides).

Closes https://github.com/elastic/streams-program/issues/765

This is a clean version of #250785, which accumulated many iterative changes during review.

Features

Field Descriptions

  • Optional description property on field definitions for documenting fields
  • Description column in schema table with tooltip support
  • "Edit field" flyout allows setting/editing descriptions for all field types
  • "Clear description" context menu action to remove descriptions
  • Descriptions are stored as metadata and not sent to Elasticsearch

Documentation-Only Field Overrides

  • Fields can have descriptions without ES mappings (typeless definitions)
  • Child streams can add description overrides to inherited fields
  • Doc-only overrides don't trigger ES mapping updates or simulation
  • Confirm changes modal filters out doc-only changes

UI Improvements

  • Renamed "Map field" to "Edit field" since users can now add descriptions without mapping
  • "Edit field" action available for all field statuses (mapped, unmapped, inherited)
  • Status filter shows "Dynamic" (not "Unmapped") for Classic streams
  • Field simulation excludes description-only changes from API calls

Validation

  • Server-side validation prevents invalid type overrides on inherited fields
  • UI hides "Unmap field" for fields that would conflict with parent mappings
  • Child streams can map fields when parent has doc-only field (no type)

Classic Streams Support

  • Classic streams can have descriptions on their fields, but it's not possible to have description-only fields. It's necessary to map the field explicitly if you want to provide a description.

Test Plan

Unit Tests

  • yarn test:jest x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts
  • yarn test:jest x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts
  • yarn test:jest x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/validate_ingest_upsert.test.ts
  • yarn test:jest x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/

Integration Tests

  • yarn test:ftr --config x-pack/platform/test/api_integration_deployment_agnostic/configs/stateful/platform.serverless.config.ts (for streams inheritance tests)

Type Checking

  • yarn test:type_check --project x-pack/platform/plugins/shared/streams/tsconfig.json
  • yarn test:type_check --project x-pack/platform/plugins/shared/streams_app/tsconfig.json

Made with Cursor

Summary by CodeRabbit

  • New Features
    • Field descriptions supported across the schema editor and save flows
    • Documentation-only overrides (description without a mapping) and an "Unmapped" field type for wired streams
  • Improvements
    • Description-aware inheritance so child streams can override descriptions without forcing mappings
    • UI updates: description column, editor, and flyout workflows; review flow skips modal for description-only changes
  • Bug Fixes
    • Validation and mapping generation now ignore doc-only fields to prevent incorrect mappings

This PR adds support for field descriptions in Streams, allowing users to
document fields without creating Elasticsearch mappings (doc-only overrides).

## Features

### Field Descriptions
- Optional `description` property on field definitions for documenting fields
- Description column in schema table with tooltip support
- "Edit field" flyout allows setting/editing descriptions for all field types
- "Clear description" context menu action to remove descriptions
- Descriptions are stored as metadata and not sent to Elasticsearch

### Documentation-Only Field Overrides
- Fields can have descriptions without ES mappings (typeless definitions)
- Child streams can add description overrides to inherited fields
- Doc-only overrides don't trigger ES mapping updates or simulation
- Confirm changes modal filters out doc-only changes

### UI Improvements
- Renamed "Map field" to "Edit field" since users can now add descriptions
  without mapping
- "Edit field" action available for all field statuses (mapped, unmapped,
  inherited)
- Status filter shows "Dynamic" (not "Unmapped") for Classic streams
- Field simulation excludes description-only changes from API calls

### Validation
- Server-side validation prevents invalid type overrides on inherited fields
- UI hides "Unmap field" for fields that would conflict with parent mappings
- Child streams can map fields when parent has doc-only field (no type)

### Classic Streams Support
- Description-only overrides work via `ingest.classic.field_overrides`
- Mapping dry-run/update filters out doc-only overrides
- Skips mapping actions when only doc-only overrides present

## Test Coverage
- Unit tests for schema validation and field definition parsing
- Integration tests for doc-only field inheritance
- FTR API tests for description-only overrides
- Scout UI tests for schema editor interactions
- Comprehensive test coverage for all field action scenarios

Made-with: Cursor
@flash1293 flash1293 added backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes v9.1.11 Feature:Streams This is the label for the Streams Project Team:streams-program Team Label for Streams program labels Feb 26, 2026
@flash1293
Copy link
Copy Markdown
Contributor Author

This is a clean PR created from #250785, which had accumulated many iterative changes during review.

The changes have been squashed into a single commit with a comprehensive summary describing all features:

  • Field descriptions support
  • Documentation-only field overrides (typeless definitions)
  • UI improvements (Edit field action, Clear description, status filter)
  • Server-side validation
  • Classic streams support

All tests from the original PR are included.

Written by cursor, wait for @flash1293 to chime in

@flash1293 flash1293 added v9.4.0 Team:obs-onboarding Observability Onboarding Team and removed v9.1.11 Team:streams-program Team Label for Streams program labels Feb 26, 2026
@flash1293
Copy link
Copy Markdown
Contributor Author

/ralph Please simplify this: Classic streams shouldn't be able to have description-only fields. The UI (the field flyout) already enforces this, please check all changes on this PR and remove the support for this (in the API and the backend handling). Make sure that description-only still works for wired streams

…c streams

Classic streams should not support description-only field overrides
(fields with description but no type). The UI already enforces this,
so this removes the redundant defensive handling from the API/backend:

- Add classicFieldDefinitionSchema that requires a type
- Update classic stream schema to use the stricter schema
- Simplify getClassicFieldOverrideMappings to expect type always present
- Remove validate_ingest_upsert.ts validation (now handled by schema)
- Remove API-level validation for typeless descriptions
- Add tests for the new classic field definition schema

Made-with: Cursor
@flash1293
Copy link
Copy Markdown
Contributor Author

Addressed the simplification request. Changes made:

Schema Changes:

  • Added classicFieldDefinitionConfigSchema in @kbn/streams-schema that requires a type for all classic stream field overrides
  • Updated IngestClassic interface and schema to use the stricter ClassicFieldDefinition type

Backend Simplification:

  • Simplified getClassicFieldOverrideMappings() - now expects all field overrides to have a type (since schema enforces this)
  • Removed the check for description-only fields (no longer possible at schema level)

API Cleanup:

  • Deleted validate_ingest_upsert.ts and its tests (validation now handled by schema)
  • Removed the API-level validation code in route.ts that rejected classic streams with typeless description fields

Testing:

  • Added tests for classicFieldDefinitionConfigSchema to verify it rejects description-only overrides
  • Existing tests pass (all used types in field_overrides)

Description-only field overrides still work for wired streams via the general fieldDefinitionSchema.

"xpack.stackConnectors.xmatters.unexpectedNullResponseErrorMessage": "unerwartete Nullreaktion von xmatters",
"xpack.stackConnectors.xmatters.unexpectedStatusErrorMessage": "Fehler beim Auslösen des xMatters-Flows: unerwarteter Status {status}",
"xpack.streams.actions.editFieldLabel": "Feld bearbeiten",
"xpack.streams.actions.mapFieldLabel": "Kartenfeld",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Side note - this translation was very very wrong anyway

@flash1293 flash1293 marked this pull request as ready for review February 27, 2026 11:11
@flash1293 flash1293 requested review from a team as code owners February 27, 2026 11:11
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/obs-onboarding-team (Team:obs-onboarding)

@flash1293
Copy link
Copy Markdown
Contributor Author

/ci-ralph

@flash1293
Copy link
Copy Markdown
Contributor Author

/ralph check the test failures of build 402162 and fix them

Build URL: https://buildkite.com/elastic/kibana-pull-request/builds/402162

Use ci/bk-build-info.sh 402162 to fetch detailed build failure information.

- Remove test case for description-only classic stream fields (no longer supported)
- Prevent description-only fields from being added for classic streams in buildSchemaSavePayload
- Add proper type casting for ClassicFieldDefinition in stream utils
- Export ClassicFieldDefinition and ClassicFieldDefinitionConfig from @kbn/streams-schema

Made-with: Cursor
@flash1293
Copy link
Copy Markdown
Contributor Author

Fixed the type errors from build 402162.

Changes made:

  1. Removed invalid test case in use_schema_fields.test.tsx

    • The test was checking description-only field overrides for classic streams, which are no longer supported after the schema simplification
  2. Fixed buildSchemaSavePayload in schema_editor/utils.ts

    • Added && isWired condition to prevent description-only fields from being added for classic streams
    • Added type cast for classic stream field_overrides to ClassicFieldDefinitionConfig
  3. Fixed buildUpsertStreamRequestPayload in stream_detail_enrichment/utils.ts

    • Added type cast for classic stream field_overrides to ClassicFieldDefinition
  4. Exported missing types from @kbn/streams-schema

    • Added ClassicFieldDefinition and ClassicFieldDefinitionConfig to the package exports

All type checks and Jest tests pass locally.

@flash1293
Copy link
Copy Markdown
Contributor Author

/ralph check the test failures of build 402264 and fix them

Build URL: https://buildkite.com/elastic/kibana-pull-request/builds/402264

Use ci/bk-build-info.sh 402264 to fetch detailed build failure information.

@flash1293
Copy link
Copy Markdown
Contributor Author

/ralph check the test failures of build 405062 and fix them

Build URL: https://buildkite.com/elastic/kibana-pull-request/builds/405062

Use ci/bk-build-info.sh 405062 to fetch detailed build failure information.

@flash1293
Copy link
Copy Markdown
Contributor Author

Investigated the test failure from build 405062.

Finding: The failure is a known flaky test unrelated to this PR.

Failed test:

  • src/platform/plugins/shared/dashboard/test/scout_oas_schema/api/tests/schema.spec.ts
  • "Registered embeddable schemas have not changed"
  • Error: Test timeout of 90000ms exceeded

Evidence:

Recommendation: Retrigger CI — the flaky test should eventually pass on retry, or will be automatically skipped.

@flash1293
Copy link
Copy Markdown
Contributor Author

Thanks @diogolima-elastic , I fixed those issues:

  • Description is properly truncated
  • Doesn't show as unmapped when setting description on an inherited field
  • Added a smoke test for setting the description, but keeping it at a minimum since these tests are expensive

Copy link
Copy Markdown
Contributor

@diogolima-elastic diogolima-elastic left a comment

Choose a reason for hiding this comment

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

Thank you for the quick changes, LGTM! 🚀

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

Adds support for documentation-only (description-only) field overrides across Streams: schema changes, validation, storage, simulation, UI (schema editor flyout/table/actions), state machines, tests, and API/integration tests to treat typeless overrides as non-mapping-only metadata.

Changes

Cohort / File(s) Summary
OpenAPI Schema
oas_docs/output/kibana.yaml, oas_docs/output/kibana.serverless.yaml
Added description: string to multiple object schemas and validation constraints for doc-only shapes.
Streams Schema Exports
x-pack/platform/packages/shared/kbn-streams-schema/index.ts
Exported new types/constants for classic/all field definitions.
Fields implementation & tests
x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts, .../src/fields/index.test.ts
Extended FieldDefinitionConfig to allow description-only overrides; added ClassicFieldDefinition, ALL_FIELD_DEFINITION_TYPES, adjusted schemas and predicate, and added tests.
Helpers: parameters & models
.../helpers/get_advanced_parameters.ts, .../models/ingest/classic.ts
Treat description as a first-class parameter; switch classic ingest types/schemas to ClassicFieldDefinition.
Stream helpers & metadata stripping
x-pack/.../helpers.ts, .../helpers.test.ts
Removed implicit 'from' stripping from base filter; added withoutInheritedFieldMetadata to strip inherited metadata.
Mapping generation & templates
.../component_templates/generate_layer.ts, generate_layer.test.ts, logs_layer.test.ts, logs_otel_layer.ts
Skip doc-only (typeless) and system fields when building ES mappings/aliases; added tests verifying typeless fields are ignored and descriptions are not included in ES mappings.
Simulation & schema routes
.../routes/internal/streams/schema/route.ts, route.test.ts, simulation_handler.ts
Added type guards and getSimulatableFieldDefinitions to filter out doc-only/system fields; strip descriptions when building simulation mappings; exposed test helper.
Classic stream handling
.../state_management/streams/classic_stream.ts
Added getClassicFieldOverrideMappings to convert classic overrides into mappings and used it in upsert/validation/action flows.
Content route export
.../routes/content/route.ts
Apply withoutInheritedFieldMetadata to exported root stream fields.
Schema editor: constants/types/table
.../schema_editor/constants.ts, .../schema_editor/types.ts, schema_editor_table.tsx
Added 'unmapped' field type, description column, new SchemaFieldType/Inherited/Typed variants, and tooltip/ellipsis rendering for description column.
Schema editor: flyout & forms
.../flyout/add_field_flyout.tsx, field_summary.tsx, index.tsx, field_form_type.tsx, field_form_format.tsx
Introduced FieldDescriptionSelector, isDescriptionOnlyMode support, wired vs classic stream handling (allow description-only for wired), gated advanced mapping UI, and adjusted type/format validation.
Flyout/sample preview
.../flyout/sample_preview_table.tsx
Memoized/cached simulation payload and switched to TypedMappedSchemaField usage.
Field actions & UI behavior
.../field_actions.tsx, field_actions.test.tsx, field_type.tsx
Added clear description action, inheritance-aware action visibility, changed action assembly for mapped/unmapped/inherited statuses, and tests.
Status filter & table tests
.../filters/status_filter_group.tsx, related tests
Made status options stream-type-aware (classic vs wired) and preserve selection; updated tests.
Utilities: conversion & payload building
.../utils.ts, utils.test.ts
Added convertToFieldDefinitionConfig; extended buildSchemaSavePayload to persist description-only overrides for wired streams and use ClassicFieldDefinition for classic payloads.
Change detection & review modal
.../schema_changes_review_modal.tsx, schema_changes_review_modal.test.ts, removed old test file
Rewrote getChanges to only surface mapping-affecting changes (ignore description-only), updated simulation filtering and tests, removed replaced test file.
Schema fields hook & tests
.../hooks/use_schema_fields.ts, use_schema_fields.test.tsx
Refactored to explicitly handle typedOverrides/docOnlyOverrides/inherited merging and pending change detection for description-only overrides.
Enrichment state machines & utils
.../simulation_state_machine/*, stream_enrichment_state_machine/*
Added docOnlyOverrides to context, stageDocOnlyOverride action/event/util, updated convertToFieldDefinition typing and utilities, propagated overrides through map/unmap/reset flows and tests.
Detected fields editor & enrichment UI
detected_fields_editor.tsx, page_content.tsx, stream_detail_enrichment/utils.ts
Wire stageDocOnlyOverride into detected fields editing; early-exit saving when no mapping-affecting changes; cast classic payloads to ClassicFieldDefinition.
End-to-end & API tests, page objects
test/scout/.../streams_app.ts, test/scout/ui/tests/..., test/api_integration_deployment_agnostic/apis/streams/*
Added UI helpers (confirmChangesInReviewModalIfPresent, fillFieldDescription, clickEditFieldButton), updated E2E tests to cover adding descriptions, added integration tests for description-only inheritance and simulation behavior.
Translations & config
x-pack/.../translations/*.json, moon.yml, tsconfig.json
Removed obsolete translation keys in several locales; added @kbn/std dependency and tsconfig reference.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant UI as Schema Editor UI
  participant Server as Streams Server
  participant Sim as Simulation Handler
  participant ES as Elasticsearch

  User->>UI: Open/Edit field (add description-only override)
  UI->>Server: stageDocOnlyOverride / save request (doc-only override)
  Server->>Server: merge docOnlyOverrides into detected/upsert payload
  Server->>Sim: request simulation with simulatable fields (typed only)
  Sim->>ES: run mapping simulation (exclude doc-only, strip descriptions)
  ES-->>Sim: simulation result
  Sim-->>Server: simulation outcome
  Server-->>UI: updated preview / confirmation (may skip review modal if no mapping-affecting changes)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

Team:QA

Suggested reviewers

  • crespocarlos
  • steliosmavro
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main feature: adding field descriptions and documentation-only field overrides to the Streams system.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts (1)

88-107: ⚠️ Potential issue | 🟠 Major

Require strict validation in the documentation-only field definition branch.

The current schema intersects recursiveRecord with the union of field variants. This means the documentation-only branch (Lines 96–101) inherits recursiveRecord's permissive key handling, allowing payloads like { description: 'x', index: false } to validate even though documentation-only overrides should carry only a description. The as z.ZodType<FieldDefinitionConfig> cast masks this runtime/type mismatch.

Split the union outside the intersection and apply .strict() to the documentation-only variant:

Suggested schema split
- export const fieldDefinitionConfigSchema = z.intersection(
-   recursiveRecord,
-   z.union([
-     z.object({
-       type: z.enum(FIELD_DEFINITION_TYPES),
-       format: z.optional(NonEmptyString),
-       description: z.optional(z.string()),
-     }),
-     z.object({
-       // Documentation-only override: require description and forbid type entirely
-       description: z.string(),
-       type: z.never().optional(),
-       format: z.never().optional(),
-     }),
-     z.object({
-       type: z.literal('system'),
-       description: z.optional(z.string()),
-     }),
-   ])
- ) as z.ZodType<FieldDefinitionConfig>;
+ const mappingFieldDefinitionConfigSchema = z.intersection(
+   recursiveRecord,
+   z.object({
+     type: z.enum(FIELD_DEFINITION_TYPES),
+     format: z.optional(NonEmptyString),
+     description: z.optional(z.string()),
+   })
+ );
+
+ const documentationOnlyFieldDefinitionConfigSchema = z
+   .object({
+     description: z.string(),
+   })
+   .strict();
+
+ export const fieldDefinitionConfigSchema = z.union([
+   mappingFieldDefinitionConfigSchema,
+   documentationOnlyFieldDefinitionConfigSchema,
+   z.object({
+     type: z.literal('system'),
+     description: z.optional(z.string()),
+   }),
+ ]) as z.ZodType<FieldDefinitionConfig>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts`
around lines 88 - 107, The documentation-only union branch is inheriting
permissive keys from recursiveRecord via the intersection in
fieldDefinitionConfigSchema, allowing extra keys like index to pass validation;
change the schema so the union of variants is created first and then intersected
with recursiveRecord only for the variants that should allow those keys, and
make the documentation-only variant a strict schema (use .strict()) so it
permits only description and forbids all other keys; update the construction
around fieldDefinitionConfigSchema and remove the unsafe cast-as
z.ZodType<FieldDefinitionConfig> if necessary to ensure runtime validation
matches the TypeScript type.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)

122-141: ⚠️ Potential issue | 🟠 Major

Keep descriptions when unmapping wired fields.

Unmap field now removes the mapping by staging only { name, parent, status: 'unmapped' }. On wired streams that also drops any existing description, even though the new doc-only model keeps metadata independent from ES mappings. Please preserve field.description in this path so unmapping converts to a doc-only override instead of silently deleting the docs.

💡 Suggested change
             onClick: () => {
               onFieldUpdate({
                 name: field.name,
                 parent: field.parent,
                 status: 'unmapped',
+                ...(Streams.WiredStream.Definition.is(stream) && field.description
+                  ? { description: field.description }
+                  : {}),
               } as SchemaField);
             },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`
around lines 122 - 141, The unmap action currently calls onFieldUpdate with only
{ name, parent, status: 'unmapped' } which drops field.description for wired
streams; update the unmap action (the object passed to onFieldUpdate in the
Unmap field branch where viewFieldAction, editFieldAction and
clearDescriptionAction are assembled) to include field.description so the staged
SchemaField preserves the existing description and becomes a doc-only override;
keep the rest of the properties (name, parent, status: 'unmapped') intact and
ensure the object is typed as SchemaField when passed to onFieldUpdate.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx (1)

21-41: ⚠️ Potential issue | 🟠 Major

Don't auto-apply metadata recommendations in the wired doc-only flow.

This new filtering makes unmapped a wired-only choice, but FieldFormType still calls onTypeChange(recommendation) whenever field.type is empty. Opening an inherited ECS/OTel field just to add a description will therefore silently stage a real mapping override as soon as metadata resolves. Please gate that autofill path so a wired field can stay typeless.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx`
around lines 21 - 41, The wired "doc-only" flow is still auto-applying metadata
recommendations: in FieldFormType where onTypeChange(recommendation) is invoked
when field.type is empty, add a guard so recommendations are not auto-applied
for wired doc-only/inherited fields; only call onTypeChange(recommendation) when
either streamType !== 'wired' or the field is not a doc-only/inherited field
(e.g., field.inherited or field.isDocOnly is false) and recommendation is
present. This keeps getFieldTypeOptions unchanged but prevents FieldFormType
from silently staging a real mapping override for wired typeless fields.
🧹 Nitpick comments (10)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts (1)

38-43: Use inline type modifier for type-only imports.

The import mixes value imports (Streams, isSchema) with type-only imports (ClassicFieldDefinition, FieldDefinition). Per coding guidelines, type-only imports should use import type.

♻️ Suggested fix using inline type imports
-import {
-  Streams,
-  isSchema,
-  type ClassicFieldDefinition,
-  type FieldDefinition,
-} from '@kbn/streams-schema';
+import {
+  Streams,
+  isSchema,
+} from '@kbn/streams-schema';
+import type { ClassicFieldDefinition, FieldDefinition } from '@kbn/streams-schema';

Alternatively, if the codebase prefers inline type modifiers (TypeScript 4.5+):

import {
  Streams,
  isSchema,
  type ClassicFieldDefinition,
  type FieldDefinition,
} from '@kbn/streams-schema';

As per coding guidelines: "Use import type for type-only imports in TypeScript."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts`
around lines 38 - 43, The import currently mixes runtime values (Streams,
isSchema) with types (ClassicFieldDefinition, FieldDefinition); update the
import to mark the type-only imports with the inline type modifier so the line
imports Streams and isSchema as normal values and imports ClassicFieldDefinition
and FieldDefinition as types (use "type ClassicFieldDefinition" and "type
FieldDefinition" in the same import) to satisfy the coding guideline requiring
type-only imports be declared with import type.
x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts (1)

821-829: Add explicit Promise<void> return types to the new page-object methods.

These helpers are part of the exported StreamsApp API, so the new methods should declare their return types explicitly for consistency with the repo rule. As per coding guidelines "Prefer explicit return types for public APIs and exported functions in TypeScript."

Also applies to: 931-939

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`
around lines 821 - 829, The new page-object method
confirmChangesInReviewModalIfPresent should declare an explicit Promise<void>
return type (i.e., change its signature to
confirmChangesInReviewModalIfPresent(): Promise<void>) to comply with the
exported StreamsApp API typing rule; update this method and the other new helper
method(s) introduced in the same file around the other diff (the methods shown
in the 931-939 range) to similarly declare explicit Promise<void> return types
so the public/page-object API has consistent explicit return types.
x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts (1)

11-60: Add a typeless description-only case here.

These tests cover typed fields, but the new feature is a typeless override like { description, from }. Since withoutInheritedFieldMetadata() now sits on the export path for doc-only overrides, it would be worth locking in that it preserves description while stripping inherited metadata for a field with no type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts`
around lines 11 - 60, Add a new unit test in the
describe('withoutInheritedFieldMetadata') suite that verifies a typeless,
description-only field (e.g. { description: 'desc', from: 'logs.parent' }) is
returned with description preserved and inherited metadata stripped; call
withoutInheritedFieldMetadata with an input like { 'doc.only': { description:
'desc', from: 'logs.parent' } } and assert the result equals { 'doc.only': {
description: 'desc' } } so the function (withoutInheritedFieldMetadata) handles
fields that lack a type.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts (1)

66-72: Narrow docOnlyOverrides to the doc-only shape.

Lines 66-72 document that this bag must only hold typeless documentation overrides, but FieldDefinition also admits mapped field configs. That makes it easy for a future reducer to stash a typed field here and have it flow through the doc-only path accidentally. Prefer a dedicated shape such as Record<string, { description?: string }> or the narrowest exported typeless field-definition type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts`
around lines 66 - 72, The docOnlyOverrides property is currently typed as
FieldDefinition which permits full typed/mapped field configs; narrow it to a
dedicated typeless shape to prevent typed field configs flowing into the
doc-only path—replace the type of docOnlyOverrides (in types.ts) with a specific
shape such as Record<string, { description?: string }> or use the project’s
exported “typeless” field-definition type if one exists; update any related
exports/consumers (references to docOnlyOverrides) to the new type so reducers
and helpers enforce only documentation-only entries.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts (1)

23-24: Consider renaming PersistableSchemaFieldType to avoid confusion.

The JSDoc above notes that 'system' and 'unmapped' are pseudo-types that "must never be persisted as real mapping types." However, PersistableSchemaFieldType is defined as an alias of SchemaFieldType, which includes these pseudo-types. This creates a naming contradiction—the type name suggests these values can be persisted, but they shouldn't be.

Consider either:

  • Renaming to something like UISchemaFieldType to clarify it's for UI purposes only
  • Or narrowing PersistableSchemaFieldType to exclude the pseudo-types:
    export type PersistableSchemaFieldType = NonNullable<FieldDefinitionConfig['type']>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts`
around lines 23 - 24, PersistableSchemaFieldType currently aliases
SchemaFieldType which includes pseudo-types 'system' and 'unmapped' that must
not be persisted; change PersistableSchemaFieldType to only include real mapping
types by narrowing it to NonNullable<FieldDefinitionConfig['type']> (i.e.
exclude 'system' and 'unmapped'), and keep SchemaFieldType as the UI union that
includes the pseudo-types so callers that need persistable-only types use
PersistableSchemaFieldType while the UI continues to use SchemaFieldType.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx (1)

356-359: Consider memoizing the getChanges result to avoid duplicate computation.

getChanges(schemaEditorFields, definitionFields) is computed twice - once in openConfirmationModal (Line 261) and again in the onConfirm prop (Line 357). While the performance impact is likely negligible for typical field counts, memoizing this result would be cleaner.

♻️ Suggested refactor
+  const mappingChanges = React.useMemo(
+    () => getChanges(schemaEditorFields, definitionFields),
+    [schemaEditorFields, definitionFields]
+  );
+
   const openConfirmationModal = () => {
-    const changes = getChanges(schemaEditorFields, definitionFields);
-    if (changes.length === 0) {
+    if (mappingChanges.length === 0) {
       // If there are no mapping-affecting changes, skip the review modal entirely.
       void saveChanges();
       return;
     }
     // ... rest of function
   };

   // Later in ManagementBottomBar:
   onConfirm={
-    schemaEditorFields.length > 0 &&
-    getChanges(schemaEditorFields, definitionFields).length > 0
+    mappingChanges.length > 0
       ? openConfirmationModal
       : saveChanges
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx`
around lines 356 - 359, Compute the diff once and reuse it: create a memoized
value (e.g. pendingChanges = useMemo(() => getChanges(schemaEditorFields,
definitionFields), [schemaEditorFields, definitionFields])) and replace the two
direct calls to getChanges(...) (the one used inside openConfirmationModal and
the one in the onConfirm prop selection) with this pendingChanges; ensure you
import/use React.useMemo and update the conditional that currently chooses
between openConfirmationModal and saveChanges to reference pendingChanges.length
> 0.
x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts (1)

38-93: Add regression coverage for the descendant-side relaxation.

These new cases cover validateAncestorFields(), but the matching validateDescendantFields() change in validate_fields.ts still has no test for doc-only fields. A small pair of cases there would keep both directions aligned.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts`
around lines 38 - 93, Add tests mirroring the ancestor-side cases for the
descendant-side validator: in validate_fields.test.ts add two it() cases that
call validateDescendantFields() (same style as the existing
validateAncestorFields tests) — one where an ancestor defines a doc-only field
(no type, only description) and the descendant supplies a real type, and another
where the descendant supplies a real type plus a description. Use the same
helpers (createWiredStreamDefinition) and streamName ('logs.otel') and assert
that validateDescendantFields({... ancestors: [ancestor], fields, streamName })
does not throw; name the tests to indicate they cover "doc-only" ancestor fields
and the descendant relaxation.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx (1)

27-28: Prefer @emotion/react for the new description-cell styles.

This change pulls in @emotion/css just for one cell renderer. Keeping the styles on the css prop / @emotion/react path avoids mixing Emotion APIs in the same React file and stays aligned with the rest of the EUI styling stack.

As per coding guidelines, "Use @elastic/eui components with Emotion (@emotion/react) for styling in React."

Also applies to: 204-216

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx`
around lines 27 - 28, The file schema_editor_table.tsx imports and uses
`@emotion/css` for the description-cell styles; replace that by using
`@emotion/react`'s css prop to stay consistent with EUI/Emotion usage: remove the
import from '@emotion/css', import the css (and/or jsx pragma if needed) from
'@emotion/react', and update the description cell renderer (the description-cell
code around lines ~204-216 / any function like renderDescriptionCell or the JSX
element that currently uses css(...)) to apply styles via the JSX css prop (e.g.
<div css={css`...`}> or pass the css object to the css prop) instead of calling
the `@emotion/css` helper; ensure any className generated by css(...) is converted
to the css prop usage and that getStreamTypeFromDefinition and other imports
remain unchanged.
x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts (1)

52-63: Good coverage, consider adding a test for fields with both type and description.

The test suite covers the key filtering scenarios well. Consider adding a test case verifying that fields with both a valid type and a description are preserved (not accidentally filtered):

it('preserves fields with both type and description', () => {
  const fields: NamedFieldDefinitionConfig[] = [
    { name: 'attributes.a', type: 'keyword', description: 'has both' },
  ];
  const result = __test__.getSimulatableFieldDefinitions(fields);
  expect(result).toEqual([{ name: 'attributes.a', type: 'keyword', description: 'has both' }]);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts`
around lines 52 - 63, Add a unit test to ensure getSimulatableFieldDefinitions
preserves fields that include both a valid type and a description: in
route.test.ts add a case (similar to existing ones) that passes a
NamedFieldDefinitionConfig like { name: 'attributes.a', type: 'keyword',
description: 'has both' } to __test__.getSimulatableFieldDefinitions and assert
the returned array contains the same object (including description) to verify
the function does not drop the description field.
x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts (1)

191-213: Minor: Tests at Lines 191-196 and 207-213 overlap.

Both tests verify that isMappingProperties returns false when fields contain description-only entries (no type). Consider consolidating these into a single test or differentiating them more clearly (e.g., one for explicitly missing type, one for undefined type).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts`
around lines 191 - 213, The two tests using isMappingProperties that both assert
false for description-only fields are redundant; consolidate them into one
clearer test (e.g., "should return false for doc-only/undefined type fields")
that covers both cases: a field with only description (no type key) and a field
with type explicitly set to undefined, using the same FieldDefinition objects
and a single expect(isMappingProperties(...)).toBe(false) assertion, and remove
the duplicate test block (the ones with descriptions 'should return false for
description-only overrides (no type)' and 'should return false when containing
doc-only fields (description without type)').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx`:
- Around line 95-102: When staging the field in onAddField from
add_field_flyout.tsx, drop any mapping-only keys when rawType === 'unmapped' so
you don't forward stale mapping metadata; specifically, after const { type:
rawType, ...rest } = data and const effectiveType = rawType === 'unmapped' ?
undefined : rawType, create a sanitized payload that, if effectiveType is
undefined, removes mapping-only keys (e.g. format and any advanced mapping
params such as analyzer/search_analyzer/fields or other mapping-specific
properties) from rest before calling onAddField; otherwise include rest as-is
and set type/status as you already do (use the existing onAddField and
SchemaField symbols to locate where to change the object construction).

In `@x-pack/platform/plugins/shared/streams/server/routes/content/route.ts`:
- Around line 86-92: The merge is dropping user overrides because
withoutBaseFields is applied after inheritedFields and local fields are merged;
move the base-field removal to operate on inheritedFields before merging so
explicit local overrides are preserved. Specifically, call withoutBaseFields on
inheritedFields (reference: inheritedFields, withoutBaseFields) first, then
merge that result with stream.ingest.wired.fields, and finally call
withoutInheritedFieldMetadata on the merged result (reference:
stream.ingest.wired.fields, withoutInheritedFieldMetadata) to strip metadata.

---

Outside diff comments:
In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts`:
- Around line 88-107: The documentation-only union branch is inheriting
permissive keys from recursiveRecord via the intersection in
fieldDefinitionConfigSchema, allowing extra keys like index to pass validation;
change the schema so the union of variants is created first and then intersected
with recursiveRecord only for the variants that should allow those keys, and
make the documentation-only variant a strict schema (use .strict()) so it
permits only description and forbids all other keys; update the construction
around fieldDefinitionConfigSchema and remove the unsafe cast-as
z.ZodType<FieldDefinitionConfig> if necessary to ensure runtime validation
matches the TypeScript type.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 122-141: The unmap action currently calls onFieldUpdate with only
{ name, parent, status: 'unmapped' } which drops field.description for wired
streams; update the unmap action (the object passed to onFieldUpdate in the
Unmap field branch where viewFieldAction, editFieldAction and
clearDescriptionAction are assembled) to include field.description so the staged
SchemaField preserves the existing description and becomes a doc-only override;
keep the rest of the properties (name, parent, status: 'unmapped') intact and
ensure the object is typed as SchemaField when passed to onFieldUpdate.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx`:
- Around line 21-41: The wired "doc-only" flow is still auto-applying metadata
recommendations: in FieldFormType where onTypeChange(recommendation) is invoked
when field.type is empty, add a guard so recommendations are not auto-applied
for wired doc-only/inherited fields; only call onTypeChange(recommendation) when
either streamType !== 'wired' or the field is not a doc-only/inherited field
(e.g., field.inherited or field.isDocOnly is false) and recommendation is
present. This keeps getFieldTypeOptions unchanged but prevents FieldFormType
from silently staging a real mapping override for wired typeless fields.

---

Nitpick comments:
In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts`:
- Around line 191-213: The two tests using isMappingProperties that both assert
false for description-only fields are redundant; consolidate them into one
clearer test (e.g., "should return false for doc-only/undefined type fields")
that covers both cases: a field with only description (no type key) and a field
with type explicitly set to undefined, using the same FieldDefinition objects
and a single expect(isMappingProperties(...)).toBe(false) assertion, and remove
the duplicate test block (the ones with descriptions 'should return false for
description-only overrides (no type)' and 'should return false when containing
doc-only fields (description without type)').

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx`:
- Around line 27-28: The file schema_editor_table.tsx imports and uses
`@emotion/css` for the description-cell styles; replace that by using
`@emotion/react`'s css prop to stay consistent with EUI/Emotion usage: remove the
import from '@emotion/css', import the css (and/or jsx pragma if needed) from
'@emotion/react', and update the description cell renderer (the description-cell
code around lines ~204-216 / any function like renderDescriptionCell or the JSX
element that currently uses css(...)) to apply styles via the JSX css prop (e.g.
<div css={css`...`}> or pass the css object to the css prop) instead of calling
the `@emotion/css` helper; ensure any className generated by css(...) is converted
to the css prop usage and that getStreamTypeFromDefinition and other imports
remain unchanged.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts`:
- Around line 23-24: PersistableSchemaFieldType currently aliases
SchemaFieldType which includes pseudo-types 'system' and 'unmapped' that must
not be persisted; change PersistableSchemaFieldType to only include real mapping
types by narrowing it to NonNullable<FieldDefinitionConfig['type']> (i.e.
exclude 'system' and 'unmapped'), and keep SchemaFieldType as the UI union that
includes the pseudo-types so callers that need persistable-only types use
PersistableSchemaFieldType while the UI continues to use SchemaFieldType.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx`:
- Around line 356-359: Compute the diff once and reuse it: create a memoized
value (e.g. pendingChanges = useMemo(() => getChanges(schemaEditorFields,
definitionFields), [schemaEditorFields, definitionFields])) and replace the two
direct calls to getChanges(...) (the one used inside openConfirmationModal and
the one in the onConfirm prop selection) with this pendingChanges; ensure you
import/use React.useMemo and update the conditional that currently chooses
between openConfirmationModal and saveChanges to reference pendingChanges.length
> 0.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts`:
- Around line 66-72: The docOnlyOverrides property is currently typed as
FieldDefinition which permits full typed/mapped field configs; narrow it to a
dedicated typeless shape to prevent typed field configs flowing into the
doc-only path—replace the type of docOnlyOverrides (in types.ts) with a specific
shape such as Record<string, { description?: string }> or use the project’s
exported “typeless” field-definition type if one exists; update any related
exports/consumers (references to docOnlyOverrides) to the new type so reducers
and helpers enforce only documentation-only entries.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts`:
- Around line 38-43: The import currently mixes runtime values (Streams,
isSchema) with types (ClassicFieldDefinition, FieldDefinition); update the
import to mark the type-only imports with the inline type modifier so the line
imports Streams and isSchema as normal values and imports ClassicFieldDefinition
and FieldDefinition as types (use "type ClassicFieldDefinition" and "type
FieldDefinition" in the same import) to satisfy the coding guideline requiring
type-only imports be declared with import type.

In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`:
- Around line 821-829: The new page-object method
confirmChangesInReviewModalIfPresent should declare an explicit Promise<void>
return type (i.e., change its signature to
confirmChangesInReviewModalIfPresent(): Promise<void>) to comply with the
exported StreamsApp API typing rule; update this method and the other new helper
method(s) introduced in the same file around the other diff (the methods shown
in the 931-939 range) to similarly declare explicit Promise<void> return types
so the public/page-object API has consistent explicit return types.

In
`@x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts`:
- Around line 11-60: Add a new unit test in the
describe('withoutInheritedFieldMetadata') suite that verifies a typeless,
description-only field (e.g. { description: 'desc', from: 'logs.parent' }) is
returned with description preserved and inherited metadata stripped; call
withoutInheritedFieldMetadata with an input like { 'doc.only': { description:
'desc', from: 'logs.parent' } } and assert the result equals { 'doc.only': {
description: 'desc' } } so the function (withoutInheritedFieldMetadata) handles
fields that lack a type.

In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts`:
- Around line 38-93: Add tests mirroring the ancestor-side cases for the
descendant-side validator: in validate_fields.test.ts add two it() cases that
call validateDescendantFields() (same style as the existing
validateAncestorFields tests) — one where an ancestor defines a doc-only field
(no type, only description) and the descendant supplies a real type, and another
where the descendant supplies a real type plus a description. Use the same
helpers (createWiredStreamDefinition) and streamName ('logs.otel') and assert
that validateDescendantFields({... ancestors: [ancestor], fields, streamName })
does not throw; name the tests to indicate they cover "doc-only" ancestor fields
and the descendant relaxation.

In
`@x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts`:
- Around line 52-63: Add a unit test to ensure getSimulatableFieldDefinitions
preserves fields that include both a valid type and a description: in
route.test.ts add a case (similar to existing ones) that passes a
NamedFieldDefinitionConfig like { name: 'attributes.a', type: 'keyword',
description: 'has both' } to __test__.getSimulatableFieldDefinitions and assert
the returned array contains the same object (including description) to verify
the function does not drop the description field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 9045abad-8632-472a-adeb-5c7c5b12a569

📥 Commits

Reviewing files that changed from the base of the PR and between 5bcd56a and d462e3e.

📒 Files selected for processing (69)
  • oas_docs/output/kibana.serverless.yaml
  • oas_docs/output/kibana.yaml
  • x-pack/platform/packages/shared/kbn-streams-schema/index.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/helpers/get_advanced_parameters.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/models/ingest/classic.ts
  • x-pack/platform/plugins/private/translations/translations/de-DE.json
  • x-pack/platform/plugins/private/translations/translations/fr-FR.json
  • x-pack/platform/plugins/private/translations/translations/ja-JP.json
  • x-pack/platform/plugins/private/translations/translations/zh-CN.json
  • x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_layer.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_otel_layer.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/state_management/streams/classic_stream.ts
  • x-pack/platform/plugins/shared/streams/server/routes/content/route.ts
  • x-pack/platform/plugins/shared/streams/server/routes/internal/streams/processing/simulation_handler.ts
  • x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts
  • x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.ts
  • x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/route.ts
  • x-pack/platform/plugins/shared/streams_app/moon.yml
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/constants.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/filters/status_filter_group.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_format.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/sample_preview_table.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/schema_editor_flyout.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/simulation_state_machine.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/types.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/use_stream_enrichment.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.tsx
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_mapping/data_mapping.spec.ts
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.ts
  • x-pack/platform/plugins/shared/streams_app/tsconfig.json
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/basic.ts
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/description_only_inheritance.ts
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/index.ts
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/schema.ts
💤 Files with no reviewable changes (5)
  • x-pack/platform/plugins/private/translations/translations/ja-JP.json
  • x-pack/platform/plugins/private/translations/translations/zh-CN.json
  • x-pack/platform/plugins/private/translations/translations/fr-FR.json
  • x-pack/platform/plugins/private/translations/translations/de-DE.json
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx

Comment on lines +95 to +102
const { type: rawType, ...rest } = data;
const effectiveType = rawType === 'unmapped' ? undefined : rawType;

onAddField({
...rest,
...(effectiveType !== undefined ? { type: effectiveType } : {}),
status: effectiveType !== undefined ? 'mapped' : 'unmapped',
} as SchemaField);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Drop mapping-only state when staging a typeless field.

If the user picks a concrete type, edits format or advanced params, and then switches back to unmapped, this branch still forwards those hidden fields via rest. That can stage an invalid typeless definition with stale mapping metadata. Please strip mapping-only keys when effectiveType is undefined.

💡 Suggested change
-    const { type: rawType, ...rest } = data;
+    const { type: rawType, format, additionalParameters, ...rest } = data;
     const effectiveType = rawType === 'unmapped' ? undefined : rawType;

     onAddField({
       ...rest,
-      ...(effectiveType !== undefined ? { type: effectiveType } : {}),
+      ...(effectiveType !== undefined
+        ? {
+            type: effectiveType,
+            ...(format ? { format } : {}),
+            ...(additionalParameters ? { additionalParameters } : {}),
+          }
+        : {}),
       status: effectiveType !== undefined ? 'mapped' : 'unmapped',
     } as SchemaField);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { type: rawType, ...rest } = data;
const effectiveType = rawType === 'unmapped' ? undefined : rawType;
onAddField({
...rest,
...(effectiveType !== undefined ? { type: effectiveType } : {}),
status: effectiveType !== undefined ? 'mapped' : 'unmapped',
} as SchemaField);
const { type: rawType, format, additionalParameters, ...rest } = data;
const effectiveType = rawType === 'unmapped' ? undefined : rawType;
onAddField({
...rest,
...(effectiveType !== undefined
? {
type: effectiveType,
...(format ? { format } : {}),
...(additionalParameters ? { additionalParameters } : {}),
}
: {}),
status: effectiveType !== undefined ? 'mapped' : 'unmapped',
} as SchemaField);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx`
around lines 95 - 102, When staging the field in onAddField from
add_field_flyout.tsx, drop any mapping-only keys when rawType === 'unmapped' so
you don't forward stale mapping metadata; specifically, after const { type:
rawType, ...rest } = data and const effectiveType = rawType === 'unmapped' ?
undefined : rawType, create a sanitized payload that, if effectiveType is
undefined, removes mapping-only keys (e.g. format and any advanced mapping
params such as analyzer/search_analyzer/fields or other mapping-specific
properties) from rest before calling onAddField; otherwise include rest as-is
and set type/status as you already do (use the existing onAddField and
SchemaField symbols to locate where to change the object construction).

Comment on lines +86 to +92
// strip inherited field metadata (from, alias_for) so exports contain clean FieldDefinition
stream.ingest.wired.fields = withoutInheritedFieldMetadata(
withoutBaseFields({
...inheritedFields,
...stream.ingest.wired.fields,
})
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve explicit overrides on base fields during export.

withoutBaseFields() is running after inherited and local fields are merged, so any user-authored override on a built-in base field gets dropped too. That means a description-only override on something like a base field will disappear from the exported pack and won’t round-trip on import.

💡 Proposed fix
-          stream.ingest.wired.fields = withoutInheritedFieldMetadata(
-            withoutBaseFields({
-              ...inheritedFields,
-              ...stream.ingest.wired.fields,
-            })
-          );
+          stream.ingest.wired.fields = withoutInheritedFieldMetadata({
+            ...withoutBaseFields(inheritedFields),
+            ...stream.ingest.wired.fields,
+          });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// strip inherited field metadata (from, alias_for) so exports contain clean FieldDefinition
stream.ingest.wired.fields = withoutInheritedFieldMetadata(
withoutBaseFields({
...inheritedFields,
...stream.ingest.wired.fields,
})
);
// strip inherited field metadata (from, alias_for) so exports contain clean FieldDefinition
stream.ingest.wired.fields = withoutInheritedFieldMetadata({
...withoutBaseFields(inheritedFields),
...stream.ingest.wired.fields,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x-pack/platform/plugins/shared/streams/server/routes/content/route.ts` around
lines 86 - 92, The merge is dropping user overrides because withoutBaseFields is
applied after inheritedFields and local fields are merged; move the base-field
removal to operate on inheritedFields before merging so explicit local overrides
are preserved. Specifically, call withoutBaseFields on inheritedFields
(reference: inheritedFields, withoutBaseFields) first, then merge that result
with stream.ingest.wired.fields, and finally call withoutInheritedFieldMetadata
on the merged result (reference: stream.ingest.wired.fields,
withoutInheritedFieldMetadata) to strip metadata.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

This change introduces support for description-only field overrides in Elasticsearch Streams, allowing fields to store documentation without type mappings. It includes schema updates, new UI components for editing descriptions separately from types, state management enhancements, and test coverage for wired and classic stream handling.

Changes

Cohort / File(s) Summary
OpenAPI Schema Updates
oas_docs/output/kibana.yaml, oas_docs/output/kibana.serverless.yaml
Added description field definitions and constraints within multiple object schemas under OpenAPI paths, expanding metadata for description properties across multiple validation branches.
Streams Schema Core Types
x-pack/platform/packages/shared/kbn-streams-schema/index.ts, x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts
Introduced new exports (ClassicFieldDefinitionConfig, AllFieldDefinitionType, etc.) and expanded FieldDefinitionConfig to support optional descriptions, documentation-only overrides, and system-type fields. Added ClassicFieldDefinition support with corresponding schema validation.
Field Definition Tests
x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts
Expanded test coverage for fieldDefinitionConfigSchema, namedFieldDefinitionConfigSchema, classicFieldDefinitionConfigSchema, and related variants covering description-only overrides and system types.
Field Processing Helpers
x-pack/platform/packages/shared/kbn-streams-schema/src/helpers/get_advanced_parameters.ts, x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.ts, x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts
Updated FIRST_CLASS_PARAMETERS to include description, added withoutInheritedFieldMetadata function to strip inherited metadata, and refined filtering logic for doc-only fields.
Classic Stream Support
x-pack/platform/plugins/shared/streams/server/lib/streams/state_management/streams/classic_stream.ts, x-pack/platform/plugins/shared/streams/server/models/ingest/classic.ts
Updated Classic stream field overrides to use ClassicFieldDefinition, replaced isMappingProperties validation with getClassicFieldOverrideMappings helper, and refined mapping derivation logic.
Mapping Generation
x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.ts, x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.test.ts, x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_layer.test.ts, x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_otel_layer.ts
Added filtering to skip doc-only fields (no type) in mapping generation, updated alias handling to exclude typeless fields, and added test coverage for doc-only field behavior.
Field Validation
x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts, x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts
Refactored ancestor/descendant field validation to handle undefined types, allowing doc-only parent fields to have typed children, with expanded test coverage.
Schema Route Processing
x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.ts, x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts
Introduced isSimulatableFieldDefinition and getSimulatableFieldDefinitions to filter non-simulatable fields, updated simulation and conflict routes to skip doc-only and system fields.
Content Export & Simulation
x-pack/platform/plugins/shared/streams/server/routes/content/route.ts, x-pack/platform/plugins/shared/streams/server/routes/internal/streams/processing/simulation_handler.ts
Updated content export to strip inherited metadata, added MappingDetectedField type guard, and refined computeMappingProperties to return StreamsMappingProperties with type filtering.
Translation Removals
x-pack/platform/plugins/private/translations/translations/de-DE.json, x-pack/platform/plugins/private/translations/translations/fr-FR.json, x-pack/platform/plugins/private/translations/translations/ja-JP.json, x-pack/platform/plugins/private/translations/translations/zh-CN.json
Removed xpack.streams.actions.mapFieldLabel translation entry from German, French, Japanese, and Chinese locale files.
Schema Editor Constants & Types
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/constants.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts
Added unmapped and system pseudo-types to FIELD_TYPE_MAP, added description column definition, introduced InheritedSchemaField interface, and narrowed type constraints with TypedMappedSchemaField.
Schema Editor Core Components
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.test.tsx
Updated FieldType component typing, added clearDescriptionAction and editFieldAction, introduced inheritance checks (inheritedField, hasRealMappingInParent), and refined action visibility based on field status and mapping state.
Schema Editor Status Filter
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/filters/status_filter_group.tsx
Added stream type awareness to FieldStatusFilterGroup, updated getStatusOptions to dynamically map unmapped status to dynamic label for classic streams.
Field Flyout Components
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.test.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.test.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_format.tsx
Added FieldDescriptionSelector, updated submit logic to treat unmapped type as undefined, relaxed field type requirement for wired streams, excluded unmapped from classic stream type options, and refined type parameter constraints.
Field Summary & Details
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/sample_preview_table.tsx
Added Description field with textarea, introduced isDescriptionOnlyMode prop, refined editing control logic (canEditMappingControls), added memoization for field definition caching, and updated type references to TypedMappedSchemaField.
Schema Editor Utilities
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.test.ts
Extended convertToFieldDefinitionConfig to include optional description, updated isFieldUncommitted to treat unmapped fields with descriptions as uncommitted, refactored buildSchemaSavePayload to handle wired vs classic streams differently and persist doc-only overrides.
Schema Changes Review
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx
Updated getChanges to filter for mapping-affecting fields only, replaced removed test file with comprehensive unit tests covering doc-only and description-only scenarios.
Schema Editor Table & Tests
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/schema_editor_flyout.test.tsx
Added description column rendering with tooltip and ellipsis, added comprehensive test suite for SchemaEditorFlyout covering wired and classic stream scenarios with description-only editing.
Schema Fields Hook
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.test.tsx
Refactored getDefinitionFields to use map-based multi-phase reconstruction, added support for doc-only overrides and typeless field handling, removed lodash isEqual dependency, added comprehensive regression tests.
Simulation State Machine
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/simulation_state_machine.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts
Added docOnlyOverrides context property, introduced stageDocOnlyOverride action and state transition, updated utilities to handle doc-only override staging, refined convertToFieldDefinition to use TypedMappedSchemaField, added getOriginalSampleDocument helper.
Stream Enrichment State Machine
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/types.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/use_stream_enrichment.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.ts, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
Added simulation.fields.stageDocOnlyOverride event, added stageDocOnlyOverride handler to useStreamEnrichmentEvents, updated getUpsertFields to include docOnlyOverrides, added comprehensive tests for doc-only override handling.
Stream Enrichment Components
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts
Added stageDocOnlyOverride call for unmapped fields with description, updated openConfirmationModal to bypass review modal when no mapping-affecting changes exist, added ClassicFieldDefinition import and casting for field overrides.
Stream Detail Schema Editor
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx, x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.tsx
Updated test scenarios for description-only changes, refactored openConfirmationModal to filter reviewFields and bypass modal when no changes detected, imported getChanges function.
Test Fixtures & E2E Tests
x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts, x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_mapping/data_mapping.spec.ts, x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.ts
Added confirmChangesInReviewModalIfPresent, fillFieldDescription, and clickEditFieldButton helpers; updated label from "Map field" to "Edit field"; added test for adding description when mapping field; updated modal confirmation calls to use conditional variant.
Configuration & Dependencies
x-pack/platform/plugins/shared/streams_app/moon.yml, x-pack/platform/plugins/shared/streams_app/tsconfig.json, x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/route.ts
Added @kbn/std dependency to moon.yml and tsconfig.json kbn_references; reordered type-only import in route.ts.
API Integration Tests
x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/basic.ts, x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/description_only_inheritance.ts, x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/index.ts, x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/schema.ts
Added comprehensive test for description-only inheritance scenarios, added new test module with parent-child stream fork setup and description override validation, added test cases for simulation ignoring typeless definitions while processing mapping-affecting ones.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

Team:QA, backport:all-open

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: adding field descriptions and documentation-only field overrides to Streams, which are the primary features introduced across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx (1)

395-434: ⚠️ Potential issue | 🟡 Minor

Assert that the confirm path does not submit immediately.

This still passes if the click both opens the modal and calls submitChanges before the user confirms. Add a negative assertion so the test actually guards the modal gate.

Suggested assertion
       const submitButton = screen.getByTestId('streamsAppSchemaEditorReviewStagedChangesButton');
       await user.click(submitButton);

       expect(mockOpenModal).toHaveBeenCalled();
+      expect(mockSubmitChanges).not.toHaveBeenCalled();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`
around lines 395 - 434, The test currently only checks that clicking the submit
button opens the modal but doesn't assert the submission is blocked; after the
user.click(submitButton) and before any simulated modal confirmation, add a
negative assertion that the mockSubmitChanges spy was not called (e.g.,
expect(mockSubmitChanges).not.toHaveBeenCalled()) to ensure
StreamDetailSchemaEditor does not call submitChanges until the modal confirm
path runs; you can also assert mockCloseModal wasn't called yet to further guard
the modal gate.
🧹 Nitpick comments (7)
x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts (1)

140-149: Use direct descendant lookup here too.

Lines 146-149 still rescan descendant.ingest.wired.fields for every field. Matching the ancestor path with a keyed lookup keeps the two validators symmetric and avoids repeated Object.entries() allocations.

♻️ Proposed refactor
       if (!Object.hasOwn(fields, fieldName)) {
         continue;
       }
       const fieldType = fields[fieldName].type;
+      const descendantField = descendant.ingest.wired.fields[fieldName];
       if (
         fieldType !== undefined &&
-        Object.entries(descendant.ingest.wired.fields).some(
-          ([descendantFieldName, attr]) =>
-            descendantFieldName === fieldName && attr.type !== undefined && attr.type !== fieldType
-        )
+        descendantField?.type !== undefined &&
+        descendantField.type !== fieldType
       ) {
         throw new MalformedFieldsError(
           `Field ${fieldName} is already defined with incompatible type in the child stream ${descendant.name}`
         );
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts`
around lines 140 - 149, The loop currently rescans
descendant.ingest.wired.fields with Object.entries(...) for each fieldName;
replace that repeated allocation with a direct keyed lookup: get const
descendantField = descendant.ingest.wired.fields[fieldName] (or check
Object.hasOwn to be safe) and then compare descendantField.type !== undefined &&
descendantField.type !== fieldType, keeping the existing guards (the surrounding
Object.hasOwn(fields, fieldName) and fieldType checks) and using the same
fieldName, fields, descendant, and descendant.ingest.wired.fields symbols so the
validator becomes symmetric and avoids repeated Object.entries allocations.
x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts (1)

38-93: Add the inverse doc-only inheritance regression too.

These cases cover parent doc-only -> child typed, but the primary feature path is also parent typed -> child description-only override. A matching validateDescendantFields no-conflict case would also lock down the new fieldType === undefined branch.

🧪 Suggested test additions
   describe('type validation', () => {
+    it('should allow description-only override when parent already maps the field', () => {
+      const ancestor = createWiredStreamDefinition('logs', {
+        'attributes.field1': { type: 'keyword' },
+      });
+
+      const fields = {
+        'attributes.field1': { description: 'Child documentation' },
+      };
+
+      expect(() =>
+        validateAncestorFields({
+          ancestors: [ancestor],
+          fields,
+          streamName: 'logs.otel',
+        })
+      ).not.toThrow();
+    });
+
     it('should allow same type with description override', () => {
       const ancestor = createWiredStreamDefinition('logs', {
         'attributes.field1': { type: 'keyword' },
@@
 describe('validateDescendantFields', () => {
+  it('should allow description-only updates when a descendant already maps the field', () => {
+    const descendants = [
+      createWiredStreamDefinition('logs.child', {
+        'attributes.level': { type: 'keyword' },
+      }),
+    ];
+    const fields: FieldDefinition = {
+      'attributes.level': { description: 'Updated docs' },
+    };
+
+    expect(() => validateDescendantFields({ descendants, fields })).not.toThrow();
+  });
+
   it('should throw when field type conflicts with descendant', () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts`
around lines 38 - 93, Add the complementary regression test for the inverse path
(parent has a real type, child supplies only a description) by creating a case
that uses validateDescendantFields (mirroring the existing ancestor tests) to
assert no-throw when a parent stream defines 'attributes.field1' with type
'keyword' and the descendant/child defines only description (no type). Use the
same helpers (createWiredStreamDefinition) and test structure as the existing
type validation tests, target the branch that handles fieldType === undefined,
and ensure the test calls validateDescendantFields with
ancestors/fields/streamName analogous to the current validateAncestorFields
tests.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)

144-161: Consider consolidating duplicate "Edit field" action definitions.

Lines 149-154 and the fallback at line 157 both add an "Edit field" action with identical behavior (openFlyout({ isEditingByDefault: true })). Consider reusing the pre-defined editFieldAction for both branches to reduce duplication.

Proposed simplification
       case 'unmapped':
         actions = [viewFieldAction];
-        // Only show "Edit field" if the parent doesn't have a real ES mapping (type !== 'unmapped')
-        // If the parent has a real mapping, the child can't map it differently
-        if (!hasRealMappingInParent) {
-          actions.push({
-            name: i18n.translate('xpack.streams.actions.editFieldLabel', {
-              defaultMessage: 'Edit field',
-            }),
-            onClick: () => openFlyout({ isEditingByDefault: true }),
-          });
-        } else {
-          // If parent has real mapping, only allow editing description
-          actions.push(editFieldAction);
-        }
+        // Edit field is always available - the flyout handles restrictions based on parent mappings
+        actions.push(editFieldAction);
         if (field.description) {
           actions.push(clearDescriptionAction);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`
around lines 144 - 161, The branch handling 'unmapped' currently pushes a
duplicate inline "Edit field" action that calls openFlyout({ isEditingByDefault:
true }) and also pushes editFieldAction in the else branch; replace the inline
action with the existing editFieldAction to eliminate duplication. Locate the
'unmapped' case where viewFieldAction, hasRealMappingInParent, openFlyout and
clearDescriptionAction are used and change the push of the inline Edit field
action to push(editFieldAction) so both branches reuse the same editFieldAction
definition.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx (1)

14-14: Consider adding explicit return type for exported component.

Per coding guidelines, exported functions should have explicit return types for better maintainability and API clarity.

Proposed change
-export const FieldType = ({ type, aliasFor }: { type: FieldTypeOption; aliasFor?: string }) => {
+export const FieldType = ({ type, aliasFor }: { type: FieldTypeOption; aliasFor?: string }): React.ReactElement => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx`
at line 14, The exported component FieldType lacks an explicit return type;
update its signature to include one (e.g., annotate as : JSX.Element or use
React.FC<{ type: FieldTypeOption; aliasFor?: string }>) so the export has a
clear, explicit return type; modify the declaration of FieldType (which accepts
props type: FieldTypeOption and optional aliasFor) to include the chosen return
type.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx (1)

111-131: Consider extracting complex boolean derivations to named functions for readability.

The derived state logic is correct but dense. The inline comments help, but extracting these to small helper functions could improve readability:

const isDescriptionOnlyEditing = computeIsDescriptionOnlyEditing(isEditing, streamType, field.status, isDescriptionOnlyMode);

This is a minor readability suggestion; the current implementation is functionally correct.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx`
around lines 111 - 131, Extract the dense boolean derivations into small named
helper functions to improve readability: create functions such as
computeIsDescriptionOnlyEditing(isEditing, streamType, fieldStatus,
isDescriptionOnlyMode), computeHasDescriptionChanged(field, nextField),
computeIsInheritedDescriptionOnlyEditing(isDescriptionOnlyEditing, fieldStatus)
and computeIsUnmappedDescriptionOnlyChange(isEditing, streamType, fieldStatus,
fieldType, nextFieldType, hasDescriptionChanged) and replace the inline
expressions for isDescriptionOnlyEditing, hasDescriptionChanged,
isInheritedDescriptionOnlyEditing, isDocOnlyField and
isUnmappedDescriptionOnlyChange with calls to these helpers (use the existing
symbols: isDescriptionOnlyEditing, hasDescriptionChanged,
isInheritedDescriptionOnlyEditing, isDocOnlyField,
isUnmappedDescriptionOnlyChange and field/status/type/nextField variables to
wire inputs).
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts (1)

24-24: Consider whether PersistableSchemaFieldType alias adds value.

The alias PersistableSchemaFieldType = SchemaFieldType doesn't narrow the type—it's identical to SchemaFieldType. If the intent is to represent types safe for persistence, it should exclude pseudo-types:

export type PersistableSchemaFieldType = Exclude<SchemaFieldType, 'system' | 'unmapped'>;

If this is intentional (e.g., for future disambiguation), consider adding a JSDoc comment explaining the purpose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts`
at line 24, PersistableSchemaFieldType is currently a direct alias of
SchemaFieldType and doesn't add value; update its definition to explicitly
exclude non-persistable pseudo-types or document intent. Change the type alias
PersistableSchemaFieldType to exclude 'system' and 'unmapped' (using
Exclude<SchemaFieldType, 'system' | 'unmapped'>) so it represents only
persistable types, or if you want to keep the alias as-is for future use, add a
JSDoc above PersistableSchemaFieldType explaining why it mirrors SchemaFieldType
and when it will diverge.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx (1)

355-359: Consider extracting the changes check to avoid duplication.

The getChanges(schemaEditorFields, definitionFields).length > 0 check appears both here (line 357) and in openConfirmationModal (line 262). While currently consistent, this could be memoized to avoid redundant computation and reduce the risk of divergence.

♻️ Optional: Memoize the changes check
+  const mappingChanges = React.useMemo(
+    () => getChanges(schemaEditorFields, definitionFields),
+    [schemaEditorFields, definitionFields]
+  );
+
   const openConfirmationModal = () => {
-    const changes = getChanges(schemaEditorFields, definitionFields);
-    if (changes.length === 0) {
+    if (mappingChanges.length === 0) {
       // If there are no mapping-affecting changes, skip the review modal entirely.
       void saveChanges();
       return;
     }
     // ... rest of function
   };

   // ... later in JSX
   onConfirm={
-    schemaEditorFields.length > 0 &&
-    getChanges(schemaEditorFields, definitionFields).length > 0
+    schemaEditorFields.length > 0 && mappingChanges.length > 0
       ? openConfirmationModal
       : saveChanges
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx`
around lines 355 - 359, Extract the repeated changes check into a memoized
boolean (e.g., hasChanges) computed with useMemo(() =>
getChanges(schemaEditorFields, definitionFields).length > 0,
[schemaEditorFields, definitionFields]) and replace both occurrences (the
onConfirm ternary and the check inside openConfirmationModal) to use hasChanges;
this avoids duplicate getChanges calls and prevents divergence while keeping
existing behavior when invoking openConfirmationModal or saveChanges.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`:
- Around line 245-246: The read-only description branch in the FieldSummary
component currently renders `${field.description ?? EMPTY_CONTENT}` as a plain
string which collapses newlines; update the rendering of the read-only
description (the branch that uses EMPTY_CONTENT and field.description) to output
the value inside an element styled with `white-space: pre-wrap` (e.g., apply a
className or inline style) so multiline input preserves line breaks while
keeping EMPTY_CONTENT fallback behavior intact.
- Around line 234-244: The Description textarea (EuiTextArea in the isEditing
branch) lacks an accessible label; update the render for EuiTextArea used in the
field summary editor to be programmatically labeled—either wrap it in an
EuiFormRow with a visible label "Description" or add aria-labelledby/aria-label
directly to the EuiTextArea (e.g., aria-label="Description" or aria-labelledby
referencing the Description heading) so screen readers can associate the visible
"Description" heading with the textarea; adjust the component around
field.description and the onChange handler accordingly.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx`:
- Around line 91-97: When handling field.status === 'unmapped', only call
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription
}) for wired streams; add a guard that checks the stream type (e.g.,
isWiredStream or stream.type === 'wired') before staging a typeless doc-only
override and fall back to unmapField(field.name) for Classic/non-wired streams
so Classic overrides still require a type. Ensure you reference the existing
symbols: field.status, normalizedDescription, stageDocOnlyOverride, and
unmapField when adding the conditional.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts`:
- Around line 390-430: The helper stageDocOnlyOverride currently treats any call
with an empty or undefined params.description as an overwrite to
status:'unmapped' and type:undefined, which causes cleared doc-only overrides to
leave the UI showing an unmapped field; update stageDocOnlyOverride to check
params.description and if it is empty/undefined, restore the original field
snapshot instead of mutating it: look up the original SchemaField from
context.detectedSchemaFieldsCache or from the existing variable (found via
context.detectedSchemaFields.find by params.fieldName) and put that original
snapshot back into both detectedSchemaFields and detectedSchemaFieldsCache,
otherwise (when description is present) continue to set status:'unmapped', clear
type and set description as before. Ensure you reference the function
stageDocOnlyOverride, the params.fieldName and detectedSchemaFields /
detectedSchemaFieldsCache when making the change.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts`:
- Around line 14-52: Test fixture uses a Classic stream but expects a typeless
doc-only override for "foo", which contradicts the new behavior requiring
Classic field overrides to include a type; update the test for getUpsertFields
to either (a) convert the stream definition to a wired stream definition in the
context (so typeless doc-only overrides are valid) or (b) change the expected
result to assert that the Classic path filters out the doc-only override for
"foo" (i.e., only keep "keep" with its type), and ensure the context still uses
StreamEnrichmentContextType and Streams.ClassicStream.GetResponse symbols to
locate the setup to modify.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`:
- Around line 440-475: The test's mock for mockUseSchemaFields uses
storedFields: [] which makes the hook treat the field as new instead of an
existing mapped field with a description change; update the mockUseSchemaFields
return to include a storedFields entry for 'attributes.test_field' (matching
name and type) so the test represents a description-only change, and ensure the
createMockClassicStreamDefinition fixture still contains the original
description in field_overrides so submitChanges/mocking behavior asserts the
description diff correctly.

In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`:
- Around line 821-828: The check in confirmChangesInReviewModalIfPresent uses
Locator.isVisible with a timeout which is ignored; replace that with awaiting
submitButton.waitFor({ state: 'visible', timeout: 3_000' }) (or equivalent) and
catch the timeout to return false so the modal wait is reliable; if waitFor
succeeds then proceed to await expect(submitButton).toBeEnabled(...) and
submitButton.click() as before, keeping the same timeouts and error handling
around submitButton.

In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.ts`:
- Around line 52-54: The test still calls saveStepsListChanges() in the
update/retry branches without handling the optional review modal, causing
flakes; either update saveStepsListChanges() (the page object helper) to
internally call confirmChangesInReviewModalIfPresent() after the save so all
save flows are guarded, or in the spec replace the later update/retry save calls
to follow each saveStepsListChanges() with await
pageObjects.streams.confirmChangesInReviewModalIfPresent(); ensuring every save
path (create, update, retry) handles the optional review modal.

---

Outside diff comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`:
- Around line 395-434: The test currently only checks that clicking the submit
button opens the modal but doesn't assert the submission is blocked; after the
user.click(submitButton) and before any simulated modal confirmation, add a
negative assertion that the mockSubmitChanges spy was not called (e.g.,
expect(mockSubmitChanges).not.toHaveBeenCalled()) to ensure
StreamDetailSchemaEditor does not call submitChanges until the modal confirm
path runs; you can also assert mockCloseModal wasn't called yet to further guard
the modal gate.

---

Nitpick comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 144-161: The branch handling 'unmapped' currently pushes a
duplicate inline "Edit field" action that calls openFlyout({ isEditingByDefault:
true }) and also pushes editFieldAction in the else branch; replace the inline
action with the existing editFieldAction to eliminate duplication. Locate the
'unmapped' case where viewFieldAction, hasRealMappingInParent, openFlyout and
clearDescriptionAction are used and change the push of the inline Edit field
action to push(editFieldAction) so both branches reuse the same editFieldAction
definition.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx`:
- Line 14: The exported component FieldType lacks an explicit return type;
update its signature to include one (e.g., annotate as : JSX.Element or use
React.FC<{ type: FieldTypeOption; aliasFor?: string }>) so the export has a
clear, explicit return type; modify the declaration of FieldType (which accepts
props type: FieldTypeOption and optional aliasFor) to include the chosen return
type.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx`:
- Around line 111-131: Extract the dense boolean derivations into small named
helper functions to improve readability: create functions such as
computeIsDescriptionOnlyEditing(isEditing, streamType, fieldStatus,
isDescriptionOnlyMode), computeHasDescriptionChanged(field, nextField),
computeIsInheritedDescriptionOnlyEditing(isDescriptionOnlyEditing, fieldStatus)
and computeIsUnmappedDescriptionOnlyChange(isEditing, streamType, fieldStatus,
fieldType, nextFieldType, hasDescriptionChanged) and replace the inline
expressions for isDescriptionOnlyEditing, hasDescriptionChanged,
isInheritedDescriptionOnlyEditing, isDocOnlyField and
isUnmappedDescriptionOnlyChange with calls to these helpers (use the existing
symbols: isDescriptionOnlyEditing, hasDescriptionChanged,
isInheritedDescriptionOnlyEditing, isDocOnlyField,
isUnmappedDescriptionOnlyChange and field/status/type/nextField variables to
wire inputs).

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts`:
- Line 24: PersistableSchemaFieldType is currently a direct alias of
SchemaFieldType and doesn't add value; update its definition to explicitly
exclude non-persistable pseudo-types or document intent. Change the type alias
PersistableSchemaFieldType to exclude 'system' and 'unmapped' (using
Exclude<SchemaFieldType, 'system' | 'unmapped'>) so it represents only
persistable types, or if you want to keep the alias as-is for future use, add a
JSDoc above PersistableSchemaFieldType explaining why it mirrors SchemaFieldType
and when it will diverge.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx`:
- Around line 355-359: Extract the repeated changes check into a memoized
boolean (e.g., hasChanges) computed with useMemo(() =>
getChanges(schemaEditorFields, definitionFields).length > 0,
[schemaEditorFields, definitionFields]) and replace both occurrences (the
onConfirm ternary and the check inside openConfirmationModal) to use hasChanges;
this avoids duplicate getChanges calls and prevents divergence while keeping
existing behavior when invoking openConfirmationModal or saveChanges.

In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts`:
- Around line 38-93: Add the complementary regression test for the inverse path
(parent has a real type, child supplies only a description) by creating a case
that uses validateDescendantFields (mirroring the existing ancestor tests) to
assert no-throw when a parent stream defines 'attributes.field1' with type
'keyword' and the descendant/child defines only description (no type). Use the
same helpers (createWiredStreamDefinition) and test structure as the existing
type validation tests, target the branch that handles fieldType === undefined,
and ensure the test calls validateDescendantFields with
ancestors/fields/streamName analogous to the current validateAncestorFields
tests.

In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts`:
- Around line 140-149: The loop currently rescans descendant.ingest.wired.fields
with Object.entries(...) for each fieldName; replace that repeated allocation
with a direct keyed lookup: get const descendantField =
descendant.ingest.wired.fields[fieldName] (or check Object.hasOwn to be safe)
and then compare descendantField.type !== undefined && descendantField.type !==
fieldType, keeping the existing guards (the surrounding Object.hasOwn(fields,
fieldName) and fieldType checks) and using the same fieldName, fields,
descendant, and descendant.ingest.wired.fields symbols so the validator becomes
symmetric and avoids repeated Object.entries allocations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 460be384-8d02-4ccf-a7f8-73f120e19b32

📥 Commits

Reviewing files that changed from the base of the PR and between 5bcd56a and d462e3e.

📒 Files selected for processing (69)
  • oas_docs/output/kibana.serverless.yaml
  • oas_docs/output/kibana.yaml
  • x-pack/platform/packages/shared/kbn-streams-schema/index.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/helpers/get_advanced_parameters.ts
  • x-pack/platform/packages/shared/kbn-streams-schema/src/models/ingest/classic.ts
  • x-pack/platform/plugins/private/translations/translations/de-DE.json
  • x-pack/platform/plugins/private/translations/translations/fr-FR.json
  • x-pack/platform/plugins/private/translations/translations/ja-JP.json
  • x-pack/platform/plugins/private/translations/translations/zh-CN.json
  • x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_layer.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_otel_layer.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts
  • x-pack/platform/plugins/shared/streams/server/lib/streams/state_management/streams/classic_stream.ts
  • x-pack/platform/plugins/shared/streams/server/routes/content/route.ts
  • x-pack/platform/plugins/shared/streams/server/routes/internal/streams/processing/simulation_handler.ts
  • x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts
  • x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.ts
  • x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/route.ts
  • x-pack/platform/plugins/shared/streams_app/moon.yml
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/constants.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/filters/status_filter_group.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_format.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/sample_preview_table.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/schema_editor_flyout.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/simulation_state_machine.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/types.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/use_stream_enrichment.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.tsx
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_mapping/data_mapping.spec.ts
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.ts
  • x-pack/platform/plugins/shared/streams_app/tsconfig.json
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/basic.ts
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/description_only_inheritance.ts
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/index.ts
  • x-pack/platform/test/api_integration_deployment_agnostic/apis/streams/schema.ts
💤 Files with no reviewable changes (5)
  • x-pack/platform/plugins/private/translations/translations/fr-FR.json
  • x-pack/platform/plugins/private/translations/translations/de-DE.json
  • x-pack/platform/plugins/private/translations/translations/zh-CN.json
  • x-pack/platform/plugins/private/translations/translations/ja-JP.json
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx

Comment on lines +245 to +246
) : (
`${field.description ?? EMPTY_CONTENT}`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Preserve line breaks in read-only descriptions.

The editor accepts multiline input, but this branch renders a plain string, so saved newlines collapse into spaces. Render the read-only value with white-space: pre-wrap so the display matches what was entered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`
around lines 245 - 246, The read-only description branch in the FieldSummary
component currently renders `${field.description ?? EMPTY_CONTENT}` as a plain
string which collapses newlines; update the rendering of the read-only
description (the branch that uses EMPTY_CONTENT and field.description) to output
the value inside an element styled with `white-space: pre-wrap` (e.g., apply a
className or inline style) so multiline input preserves line breaks while
keeping EMPTY_CONTENT fallback behavior intact.

Comment on lines 91 to +97
} else if (field.status === 'unmapped') {
unmapField(field.name);
// Keep "Unmapped" selectable for documentation-only overrides.
if (normalizedDescription) {
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription });
} else {
unmapField(field.name);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard doc-only staging to wired streams only.

This branch now stages a typeless override for any unmapped field with a description, including Classic streams. That conflicts with the PR contract that Classic overrides must include a type. In practice this can leave the UI showing a staged description that the Classic save path later drops or rejects.

Suggested fix
         if (field.status === 'mapped') {
           mapField(field);
         } else if (field.status === 'unmapped') {
           // Keep "Unmapped" selectable for documentation-only overrides.
-          if (normalizedDescription) {
+          if (isWiredStream && normalizedDescription) {
             stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription });
           } else {
             unmapField(field.name);
           }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (field.status === 'unmapped') {
unmapField(field.name);
// Keep "Unmapped" selectable for documentation-only overrides.
if (normalizedDescription) {
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription });
} else {
unmapField(field.name);
}
} else if (field.status === 'unmapped') {
// Keep "Unmapped" selectable for documentation-only overrides.
if (isWiredStream && normalizedDescription) {
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription });
} else {
unmapField(field.name);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx`
around lines 91 - 97, When handling field.status === 'unmapped', only call
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription
}) for wired streams; add a guard that checks the stream type (e.g.,
isWiredStream or stream.type === 'wired') before staging a typeless doc-only
override and fall back to unmapField(field.name) for Classic/non-wired streams
so Classic overrides still require a type. Ensure you reference the existing
symbols: field.status, normalizedDescription, stageDocOnlyOverride, and
unmapField when adding the conditional.

Comment on lines +390 to +430
export function stageDocOnlyOverride(
context: SimulationContext,
params: { fieldName: string; description?: string }
): {
detectedSchemaFields: SchemaField[];
detectedSchemaFieldsCache: Map<string, SchemaField>;
} {
const updatedCache = new Map(context.detectedSchemaFieldsCache);

const updatedFields = (() => {
const existing = context.detectedSchemaFields.find((f) => f.name === params.fieldName);
if (!existing) {
const schemaField: SchemaField = {
name: params.fieldName,
parent: context.streamName,
status: 'unmapped',
description: params.description,
};
updatedCache.set(schemaField.name, schemaField);
return [...context.detectedSchemaFields, schemaField];
}

return context.detectedSchemaFields.map((field) => {
if (field.name !== params.fieldName) return field;

const schemaField: SchemaField = {
...field,
status: 'unmapped',
type: undefined,
description: params.description,
};
updatedCache.set(schemaField.name, schemaField);
return schemaField;
});
})();

return {
detectedSchemaFields: updatedFields,
detectedSchemaFieldsCache: updatedCache,
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clearing a doc-only override leaves the field in the wrong state.

description is optional here, but the helper still forces the row to status: 'unmapped' and drops type when params.description is empty. The state machine deletes docOnlyOverrides in that case, so “clear description” can leave the UI showing an unmapped field even though there is no override left to persist.

Please handle the empty-description path separately and restore the original field snapshot instead of mutating it into an unmapped placeholder.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts`
around lines 390 - 430, The helper stageDocOnlyOverride currently treats any
call with an empty or undefined params.description as an overwrite to
status:'unmapped' and type:undefined, which causes cleared doc-only overrides to
leave the UI showing an unmapped field; update stageDocOnlyOverride to check
params.description and if it is empty/undefined, restore the original field
snapshot instead of mutating it: look up the original SchemaField from
context.detectedSchemaFieldsCache or from the existing variable (found via
context.detectedSchemaFields.find by params.fieldName) and put that original
snapshot back into both detectedSchemaFields and detectedSchemaFieldsCache,
otherwise (when description is present) continue to set status:'unmapped', clear
type and set description as before. Ensure you reference the function
stageDocOnlyOverride, the params.fieldName and detectedSchemaFields /
detectedSchemaFieldsCache when making the change.

Comment on lines +440 to +475
mockUseSchemaFields.mockReturnValue({
fields: [
{
name: 'attributes.test_field',
type: 'keyword',
parent: 'logs.classic-test',
status: 'mapped',
description: 'Updated description',
},
],
storedFields: [],
isLoadingFields: false,
refreshFields: jest.fn(),
addField: jest.fn(),
updateField: jest.fn(),
pendingChangesCount: 1,
discardChanges: jest.fn(),
submitChanges: mockSubmitChanges,
});

const definition = createMockClassicStreamDefinition({
stream: {
...createMockClassicStreamDefinition().stream,
ingest: {
...createMockClassicStreamDefinition().stream.ingest,
classic: {
field_overrides: {
'attributes.test_field': {
type: 'keyword',
description: 'Original description',
},
},
},
},
},
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Seed storedFields for the description-only scenario.

With storedFields: [], this mock represents a new field, not an existing mapped field whose description changed. That makes the test depend on implementation details outside the hook contract and can miss regressions if the submit-flow starts diffing against storedFields.

Suggested fixture shape
+      const baseDefinition = createMockClassicStreamDefinition();
+
       const mockSubmitChanges = jest.fn().mockResolvedValue(undefined);
       mockUseSchemaFields.mockReturnValue({
         fields: [
           {
             name: 'attributes.test_field',
             type: 'keyword',
-            parent: 'logs.classic-test',
+            parent: baseDefinition.stream.name,
             status: 'mapped',
             description: 'Updated description',
           },
         ],
-        storedFields: [],
+        storedFields: [
+          {
+            name: 'attributes.test_field',
+            type: 'keyword',
+            parent: baseDefinition.stream.name,
+            status: 'mapped',
+            description: 'Original description',
+          },
+        ],
         isLoadingFields: false,
         refreshFields: jest.fn(),
         addField: jest.fn(),
         updateField: jest.fn(),
         pendingChangesCount: 1,
         discardChanges: jest.fn(),
         submitChanges: mockSubmitChanges,
       });

       const definition = createMockClassicStreamDefinition({
         stream: {
-          ...createMockClassicStreamDefinition().stream,
+          ...baseDefinition.stream,
           ingest: {
-            ...createMockClassicStreamDefinition().stream.ingest,
+            ...baseDefinition.stream.ingest,
             classic: {
               field_overrides: {
                 'attributes.test_field': {
                   type: 'keyword',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`
around lines 440 - 475, The test's mock for mockUseSchemaFields uses
storedFields: [] which makes the hook treat the field as new instead of an
existing mapped field with a description change; update the mockUseSchemaFields
return to include a storedFields entry for 'attributes.test_field' (matching
name and type) so the test represents a description-only change, and ensure the
createMockClassicStreamDefinition fixture still contains the original
description in field_overrides so submitChanges/mocking behavior asserts the
description diff correctly.

@flash1293 flash1293 requested a review from a team as a code owner March 9, 2026 16:43
@flash1293
Copy link
Copy Markdown
Contributor Author

Good catch by coderabbit on the "unmap field" action, fixed that

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx (1)

246-247: ⚠️ Potential issue | 🟡 Minor

Preserve multiline descriptions in read-only mode.

Line 247 still renders the saved description as a plain string, so any newline entered in the textarea collapses after leaving edit mode. Render the read-only value inside an element with white-space: pre-wrap so the summary matches what the user entered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`
around lines 246 - 247, The read-only description branch currently renders
`${field.description ?? EMPTY_CONTENT}` as a plain string, which collapses
newlines; update the read-only rendering in the FieldSummary component to wrap
the description (or EMPTY_CONTENT fallback) in a container element that applies
CSS white-space: pre-wrap so multiline text preserves line breaks—target the JSX
branch that references field.description and EMPTY_CONTENT and replace the plain
string with that wrapped element.
🧹 Nitpick comments (3)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)

147-164: Remove duplicate inline action definition.

Lines 152-157 define an inline "Edit field" action that's identical to the editFieldAction constant defined at lines 107-112. Both branches (if (!hasRealMappingInParent) and else) push functionally equivalent actions.

♻️ Simplify by using `editFieldAction` in both branches
       case 'unmapped':
         actions = [viewFieldAction];
-        // Only show "Edit field" if the parent doesn't have a real ES mapping (type !== 'unmapped')
-        // If the parent has a real mapping, the child can't map it differently
-        if (!hasRealMappingInParent) {
-          actions.push({
-            name: i18n.translate('xpack.streams.actions.editFieldLabel', {
-              defaultMessage: 'Edit field',
-            }),
-            onClick: () => openFlyout({ isEditingByDefault: true }),
-          });
-        } else {
-          // If parent has real mapping, only allow editing description
-          actions.push(editFieldAction);
-        }
+        // Edit field is always available - if parent has real mapping, 
+        // the flyout restricts editing to description only
+        actions.push(editFieldAction);
         if (field.description) {
           actions.push(clearDescriptionAction);
         }

If the behavior should differ between the two branches, that distinction should be documented or handled via props passed to the flyout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`
around lines 147 - 164, The inline "Edit field" action defined inside the
'unmapped' case duplicates the existing editFieldAction constant; replace the
inline action pushed in the if (!hasRealMappingInParent) branch with the
existing editFieldAction so both branches push editFieldAction (and remove the
duplicate inline object), and if you need different behavior pass a flag into
openFlyout or adjust editFieldAction to call openFlyout({ isEditingByDefault:
true, ... }) based on hasRealMappingInParent; locate edits around
editFieldAction, openFlyout, hasRealMappingInParent, viewFieldAction and
clearDescriptionAction in the 'unmapped' case.
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts (2)

22-37: Keep these fixtures type-checked instead of double-casting through unknown.

as unknown as StreamEnrichmentContextType disables the compiler right where this test should catch shape drift in StreamEnrichmentContextType and simulatorRef.getSnapshot(). A typed helper or satisfies on the minimal surface used by getUpsertFields() keeps the fixture honest without widening everything to unknown.

Suggested tightening
-      const context = {
+      const context = {
         definition: wiredDefinition,
         simulatorRef: {
           getSnapshot: () => ({
             context: {
               detectedSchemaFields: [
                 { name: 'foo', parent: 'wired-stream', status: 'unmapped' },
                 { name: 'keep', parent: 'wired-stream', status: 'mapped', type: 'date' },
               ],
               docOnlyOverrides: {
                 foo: { description: 'Foo description' },
               },
             },
           }),
         },
-      } as unknown as StreamEnrichmentContextType;
+      } satisfies Pick<StreamEnrichmentContextType, 'definition' | 'simulatorRef'>;
 
-      const result = getUpsertFields(context);
+      const result = getUpsertFields(context as StreamEnrichmentContextType);

As per coding guidelines, "Use TypeScript for all new code; avoid any and unknown."

Also applies to: 54-66

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts`
around lines 22 - 37, The fixture is being double-cast through unknown which
disables type-checking; instead make the test fixture conform to
StreamEnrichmentContextType (or the minimal shape used by getUpsertFields) so
the compiler validates it—replace the "as unknown as
StreamEnrichmentContextType" with either a properly typed helper factory or use
TypeScript's "satisfies" on the object, ensuring simulatorRef.getSnapshot()
returns the correctly typed snapshot shape and the context variable is typed as
StreamEnrichmentContextType so getUpsertFields() will be type-checked by the
compiler.

73-73: Drop the JSON serialization assertion here.

expect(result).toEqual({ foo: { description: 'Foo description' } }) already fails if foo.type is present, so the JSON.stringify(...) check is redundant and less precise. If you want the explicit guard, assert the property structurally.

More direct assertion
-      expect(JSON.stringify(result)).not.toContain('"type":"unmapped"');
+      expect(result.foo).not.toHaveProperty('type');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts`
at line 73, Remove the redundant JSON serialization assertion in the test:
delete the line using
expect(JSON.stringify(result)).not.toContain('"type":"unmapped"') and rely on
the existing structural assertion expect(result).toEqual({ foo: { description:
'Foo description' } }); if you want an explicit guard instead, add a structural
check such as expect(result.foo).not.toHaveProperty('type') referencing the
result variable in the utils.test.ts test case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`:
- Around line 246-247: The read-only description branch currently renders
`${field.description ?? EMPTY_CONTENT}` as a plain string, which collapses
newlines; update the read-only rendering in the FieldSummary component to wrap
the description (or EMPTY_CONTENT fallback) in a container element that applies
CSS white-space: pre-wrap so multiline text preserves line breaks—target the JSX
branch that references field.description and EMPTY_CONTENT and replace the plain
string with that wrapped element.

---

Nitpick comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 147-164: The inline "Edit field" action defined inside the
'unmapped' case duplicates the existing editFieldAction constant; replace the
inline action pushed in the if (!hasRealMappingInParent) branch with the
existing editFieldAction so both branches push editFieldAction (and remove the
duplicate inline object), and if you need different behavior pass a flag into
openFlyout or adjust editFieldAction to call openFlyout({ isEditingByDefault:
true, ... }) based on hasRealMappingInParent; locate edits around
editFieldAction, openFlyout, hasRealMappingInParent, viewFieldAction and
clearDescriptionAction in the 'unmapped' case.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts`:
- Around line 22-37: The fixture is being double-cast through unknown which
disables type-checking; instead make the test fixture conform to
StreamEnrichmentContextType (or the minimal shape used by getUpsertFields) so
the compiler validates it—replace the "as unknown as
StreamEnrichmentContextType" with either a properly typed helper factory or use
TypeScript's "satisfies" on the object, ensuring simulatorRef.getSnapshot()
returns the correctly typed snapshot shape and the context variable is typed as
StreamEnrichmentContextType so getUpsertFields() will be type-checked by the
compiler.
- Line 73: Remove the redundant JSON serialization assertion in the test: delete
the line using expect(JSON.stringify(result)).not.toContain('"type":"unmapped"')
and rely on the existing structural assertion expect(result).toEqual({ foo: {
description: 'Foo description' } }); if you want an explicit guard instead, add
a structural check such as expect(result.foo).not.toHaveProperty('type')
referencing the result variable in the utils.test.ts test case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a298d04b-612b-4106-b321-d4f4aab31f29

📥 Commits

Reviewing files that changed from the base of the PR and between d462e3e and 38d4032.

📒 Files selected for processing (4)
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)

148-164: Redundant conditional: both branches add the same action.

The if-else at lines 151-161 performs identical operations in both branches — both add an "Edit field" action with the same i18n key and the same openFlyout({ isEditingByDefault: true }) call. The inline action in the if branch is functionally identical to the predefined editFieldAction.

This can be simplified to unconditionally push editFieldAction.

♻️ Suggested simplification
       case 'unmapped':
         actions = [viewFieldAction];
-        // Only show "Edit field" if the parent doesn't have a real ES mapping (type !== 'unmapped')
-        // If the parent has a real mapping, the child can't map it differently
-        if (!hasRealMappingInParent) {
-          actions.push({
-            name: i18n.translate('xpack.streams.actions.editFieldLabel', {
-              defaultMessage: 'Edit field',
-            }),
-            onClick: () => openFlyout({ isEditingByDefault: true }),
-          });
-        } else {
-          // If parent has real mapping, only allow editing description
-          actions.push(editFieldAction);
-        }
+        // Edit action allows mapping (if no parent ES mapping) or editing description (if parent has mapping)
+        actions.push(editFieldAction);
         if (field.description) {
           actions.push(clearDescriptionAction);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`
around lines 148 - 164, The if-else that checks hasRealMappingInParent is
redundant because both branches push an identical "Edit field" action; replace
the entire conditional with a single unconditional actions.push(editFieldAction)
to simplify the logic—locate where actions is initialized (actions =
[viewFieldAction]) and remove the block that creates an inline action calling
openFlyout({ isEditingByDefault: true }), relying instead on the existing
editFieldAction (and still conditionally push clearDescriptionAction if
field.description is present).
x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts (1)

821-831: Add explicit return types to these new page-object APIs.

These helpers are part of the public StreamsApp surface, so it would be better to declare Promise<void> explicitly instead of relying on inference.

♻️ Proposed change
-  async confirmChangesInReviewModalIfPresent() {
+  async confirmChangesInReviewModalIfPresent(): Promise<void> {

-  async fillFieldDescription(description: string) {
+  async fillFieldDescription(description: string): Promise<void> {

-  async clickEditFieldButton() {
+  async clickEditFieldButton(): Promise<void> {

As per coding guidelines, "Prefer explicit return types for public APIs and exported functions in TypeScript."

Also applies to: 933-941

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`
around lines 821 - 831, The new page-object APIs (e.g., the async method
confirmChangesInReviewModalIfPresent in streams_app.ts and the other similar
helpers mentioned around lines 933-941) should declare explicit return types;
update each public async method signature to include Promise<void> (for example:
async confirmChangesInReviewModalIfPresent(): Promise<void>) so the StreamsApp
public surface uses explicit return types instead of relying on inference.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 148-164: The if-else that checks hasRealMappingInParent is
redundant because both branches push an identical "Edit field" action; replace
the entire conditional with a single unconditional actions.push(editFieldAction)
to simplify the logic—locate where actions is initialized (actions =
[viewFieldAction]) and remove the block that creates an inline action calling
openFlyout({ isEditingByDefault: true }), relying instead on the existing
editFieldAction (and still conditionally push clearDescriptionAction if
field.description is present).

In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`:
- Around line 821-831: The new page-object APIs (e.g., the async method
confirmChangesInReviewModalIfPresent in streams_app.ts and the other similar
helpers mentioned around lines 933-941) should declare explicit return types;
update each public async method signature to include Promise<void> (for example:
async confirmChangesInReviewModalIfPresent(): Promise<void>) so the StreamsApp
public surface uses explicit return types instead of relying on inference.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 99a68ac8-4aec-43d8-b65f-760e61ee4ee9

📥 Commits

Reviewing files that changed from the base of the PR and between d462e3e and 38d4032.

📒 Files selected for processing (4)
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
  • x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx

@flash1293 flash1293 enabled auto-merge (squash) March 10, 2026 16:27
@elasticmachine
Copy link
Copy Markdown
Contributor

elasticmachine commented Mar 10, 2026

💔 Build Failed

Failed CI Steps

Test Failures

  • [job] [logs] FTR Configs #134 / Cloud Security Posture - Group 2 Test adding Cloud Security Posture Integrations CSPM GCP CIS_GCP Organization Add Agent FLyout - Post Installation Google Cloud Shell modal pops up after user clicks on Save button when adding integration, when there are Project ID or Organization ID provided, it should use that value
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for generic and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for generic and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for host and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for host and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for service and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for service and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for user and write to updates then latest index
  • [job] [logs] Scout: [ security / entity_store ] plugin / local-stateful-classic - Entity Store CCS logs extraction (test against local instance) - Should run CCS extraction for user and write to updates then latest index
  • [job] [logs] Jest Tests #12 / saved search embeddable context awareness should resolve data source profile when fetching

Metrics [docs]

Public APIs missing comments

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

id before after diff
@kbn/streams-schema 304 310 +6

Async chunks

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

id before after diff
datasetQuality 580.9KB 581.2KB +294.0B
streamsApp 1.8MB 1.8MB +7.2KB
total +7.5KB
Unknown metric groups

API count

id before after diff
@kbn/streams-schema 345 352 +7

History

@flash1293 flash1293 merged commit 4b660be into elastic:main Mar 11, 2026
18 checks passed
mbondyra added a commit to mbondyra/kibana that referenced this pull request Mar 11, 2026
…e_fix

* commit '565f7545c422192218b803874fbdf93e8d8f08ee': (27 commits)
  [Lens API] ESQL schema for XY separately for Agent and some small token optimizations (elastic#256885)
  Fix "Accessing resource attributes before async attributes settled" telemetry error (elastic#256880)
  [Security Solution][Attacks/Alerts][Attacks page][Table section] Preserver "Sort by" state on Attacks page (elastic#256717) (elastic#256795)
  [APM] Improve redirect with default date range guard (elastic#256887)
  [Security Solution][Attacks/Alerts][Attacks page][Table section] Add assignees avatars to the group component (elastic#250126) (elastic#256901)
  [Docs] add xpack.alerting.rules.maxScheduledPerMinute setting description (elastic#257041)
  [SO] Fix non-deterministic ordering in nested find API integration tests (elastic#256447)
  [Write-restricted dashboards] Update user profile retrieval for getShouldAddAccessControl (elastic#255065)
  [One Workflow] Add Scout API test scaffold and execution tests (elastic#256300)
  [Fleet] add use_apm if dynamic_signal_types are enabled (elastic#256429)
  [Fleet] ignore data streams starting with `.` in Fleet API (elastic#256625)
  [ES|QL] METRICS_INFO support: columns_after & summary (elastic#256758)
  [Agent Builder] Agent plugins: initial installation support (elastic#256478)
  [Streams] Add field descriptions and documentation-only field overrides (elastic#255136)
  [api-docs] 2026-03-11 Daily api_docs build (elastic#257023)
  [Security Solution] fix alerts page infinite loading state due to data view error (elastic#256983)
  [Logging] Add `service.*` global fields (elastic#256878)
  [Canvas] Apply embeddable transforms to embeddable elements (elastic#252191)
  [table_list_view_table] stabilize jest test (elastic#254991)
  [Obs AI] get_index_info: add unit tests (elastic#256802)
  ...
sorenlouv pushed a commit that referenced this pull request Mar 17, 2026
…es (#255136)

## Summary

This PR adds support for field descriptions in Streams, allowing users
to document fields without creating Elasticsearch mappings
(documentation-only field overrides).

Closes https://github.com/elastic/streams-program/issues/765

**This is a clean version of #250785**, which accumulated many iterative
changes during review.

## Features

### Field Descriptions
- Optional `description` property on field definitions for documenting
fields
- Description column in schema table with tooltip support
- "Edit field" flyout allows setting/editing descriptions for all field
types
- "Clear description" context menu action to remove descriptions
- Descriptions are stored as metadata and not sent to Elasticsearch

### Documentation-Only Field Overrides
- Fields can have descriptions without ES mappings (typeless
definitions)
- Child streams can add description overrides to inherited fields
- Doc-only overrides don't trigger ES mapping updates or simulation
- Confirm changes modal filters out doc-only changes

### UI Improvements
- Renamed "Map field" to "Edit field" since users can now add
descriptions without mapping
- "Edit field" action available for all field statuses (mapped,
unmapped, inherited)
- Status filter shows "Dynamic" (not "Unmapped") for Classic streams
- Field simulation excludes description-only changes from API calls

### Validation
- Server-side validation prevents invalid type overrides on inherited
fields
- UI hides "Unmap field" for fields that would conflict with parent
mappings
- Child streams can map fields when parent has doc-only field (no type)

### Classic Streams Support
- Classic streams can have descriptions on their fields, but it's _not_
possible to have description-only fields. It's necessary to map the
field explicitly if you want to provide a description.

## Test Plan

### Unit Tests
- `yarn test:jest
x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts`
- `yarn test:jest
x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts`
- `yarn test:jest
x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/validate_ingest_upsert.test.ts`
- `yarn test:jest
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/`

### Integration Tests
- `yarn test:ftr --config
x-pack/platform/test/api_integration_deployment_agnostic/configs/stateful/platform.serverless.config.ts`
(for streams inheritance tests)

### Type Checking
- `yarn test:type_check --project
x-pack/platform/plugins/shared/streams/tsconfig.json`
- `yarn test:type_check --project
x-pack/platform/plugins/shared/streams_app/tsconfig.json`

Made with [Cursor](https://cursor.com)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Field descriptions supported across the schema editor and save flows
* Documentation-only overrides (description without a mapping) and an
"Unmapped" field type for wired streams
* **Improvements**
* Description-aware inheritance so child streams can override
descriptions without forcing mappings
* UI updates: description column, editor, and flyout workflows; review
flow skips modal for description-only changes
* **Bug Fixes**
* Validation and mapping generation now ignore doc-only fields to
prevent incorrect mappings
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting Feature:Streams This is the label for the Streams Project release_note:skip Skip the PR/issue when compiling release notes Team:obs-onboarding Observability Onboarding Team v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants