Skip to content

claude_code: adopt gen_ai.* fields and fix event.category assignments#19968

Open
efd6 wants to merge 2 commits into
elastic:mainfrom
efd6:19967-claude_code
Open

claude_code: adopt gen_ai.* fields and fix event.category assignments#19968
efd6 wants to merge 2 commits into
elastic:mainfrom
efd6:19967-claude_code

Conversation

@efd6

@efd6 efd6 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Proposed commit message

claude_code: adopt gen_ai.* fields and fix event.category assignments

Map custom fields to their ECS gen_ai.* equivalents (model, tokens,
cache, tool name/id/arguments, operation, provider) and to ECS core
fields (error.code, error.type, organization.id, user.email, user.id).
error.message is intentionally excluded due to elastic-package
overloading the field.

Fix event.category per event type: api_* events use "api" instead of
"web"; tool_decision uses "iam" with allowed/denied type derived from
the decision attribute; user_prompt and skill_activated drop their
category (no closed-vocab value fits); hook_registered moves to
"configuration"; hook_execution_start/complete get proper start/end
types; plugin_loaded moves to "library".

Derive event.outcome from the decision field for tool_decision events
(accept=success, reject=failure) instead of leaving it unknown.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

Screenshots

@efd6 efd6 self-assigned this Jul 6, 2026
@efd6 efd6 added enhancement New feature or request Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations] Integration:claude_code Claude Code labels Jul 6, 2026
@efd6 efd6 force-pushed the 19967-claude_code branch from 0740d22 to c9d67af Compare July 6, 2026 03:29
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

✅ Elastic Docs Style Checker (Vale)

No issues found on modified lines!


The Vale linter checks documentation changes against the Elastic Docs style guide. To use Vale locally or report issues, refer to Elastic style guide for Vale.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

TL;DR

Buildkite failed on stale commit c9d67af09efa59f09de3e0fa6a9d77d8b1577287 because packages/claude_code/data_stream/events/elasticsearch/ingest_pipeline/default.yml had a remove processor without a required tag. The current PR head 48134a56ebbe32cb9bb407bc6c7cc08ac9a79887 appears to have already added the missing tag: remove_ms_duration, so the immediate action is to rerun CI on the current head.

Remediation

  • Keep the current fix that adds tag: remove_ms_duration to the remove processor after set_event_duration.
  • Rerun the Check integrations claude_code Buildkite step against the current PR head.
Investigation details

Root Cause

The failed job ran .buildkite/scripts/test_one_package.sh packages/claude_code origin/main c9d67af09efa59f09de3e0fa6a9d77d8b1577287 and elastic-package rejected the Claude Code ingest pipeline during lint validation.

At the failing commit, the newly added cleanup processor after set_event_duration was:

  - remove:
      field: claude_code.events.duration_ms
      ignore_missing: true

That violates package-spec rule SVR00006, which requires processors that can fail to have tags. The current PR head has the same processor with tag: remove_ms_duration, so this failure should be resolved by a rebuild on 48134a56ebbe32cb9bb407bc6c7cc08ac9a79887 or later.

Evidence

Error: checking package failed: linting package failed: found 1 validation error:

   1. file "/opt/buildkite-agent/builds/.../packages/claude_code/data_stream/events/elasticsearch/ingest_pipeline/default.yml" is invalid: remove processor at line 460 missing required tag (SVR00006)

Verification

  • Compared the failing Buildkite commit c9d67af09efa59f09de3e0fa6a9d77d8b1577287 with current PR head 48134a56ebbe32cb9bb407bc6c7cc08ac9a79887 using the PR file contents.
  • Did not rerun elastic-package; the local workspace is checked out on main, not the PR head.

What is this? | From workflow: PR Buildkite Detective

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

🚀 Benchmarks report

To see the full report comment with /test benchmark fullreport

@efd6 efd6 marked this pull request as ready for review July 6, 2026 04:05
@efd6 efd6 requested review from a team as code owners July 6, 2026 04:05
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/security-service-integrations (Team:Security-Service Integrations)

api_error:
category:
- web
- api

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟡 Medium confidence: high path: packages/claude_code/data_stream/events/elasticsearch/ingest_pipeline/default.yml:104

api_error and api_retries_exhausted map event.type error under category api, but error is not a valid ECS type for api; use a valid type (e.g. info) and rely on event.outcome=failure.

Details

This change moves api_error and api_retries_exhausted from category web to api. The ECS spec lists the expected event.type values for category api as: access, admin, allowed, change, creation, deletion, denied, end, info, start, user. error is not among them (it was valid under the previous web category, whose expected types are access, error, info). The pair event.category: [api] + event.type: [error] is therefore no longer an ECS-sanctioned combination. It does not fail ingest or lint (both values are individually allowed), but it breaks ECS categorization semantics that detection rules and dashboards rely on. The failure signal is already carried by event.outcome, which the pipeline sets to failure for these events via set_event_outcome_failure.

Recommendation:

Keep the api category but use an ECS-valid type and let event.outcome convey the failure:

          api_error:
            category:
              - api
            type:
              - info
          api_retries_exhausted:
            category:
              - api
            type:
              - info

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

plugin_loaded:
category:
- package
- library

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟡 Medium confidence: high path: packages/claude_code/data_stream/events/elasticsearch/ingest_pipeline/default.yml:149

plugin_loaded maps event.type info under category library, but library only allows type start in ECS; change the type to start (or revert the category).

Details

This change moves plugin_loaded from category package to library. Per the ECS spec, the only expected event.type value for category library is start; info is not valid for library (it was valid under the previous package category, whose expected types include info). The resulting pair event.category: [library] + event.type: [info] is not an ECS-sanctioned combination. This is a categorization-semantics regression, not an ingest/lint failure.

Recommendation:

Use the only ECS-valid type for the library category:

          plugin_loaded:
            category:
              - library
            type:
              - start

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

ctx.event.category = ['iam'];
def decision = ctx.attributes?.decision;
if ('accept'.equals(decision)) {
ctx.event.type = ['allowed'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟡 Medium confidence: high path: packages/claude_code/data_stream/events/elasticsearch/ingest_pipeline/default.yml:168

tool_decision sets event.type allowed/denied under category iam, but iam does not sanction those types in ECS; use type info and let event.outcome carry accept/reject.

Details

The new tool_decision branch sets event.category [iam] with event.type [allowed] (accept) or [denied] (reject). The ECS spec lists the expected event.type values for category iam as: admin, change, creation, deletion, group, info, user. Neither allowed nor denied is valid for iam (those types are expected only under network and intrusion_detection). The accept/reject distinction is already captured by event.outcome, which the pipeline sets to success/failure via set_event_outcome_decision_accept and set_event_outcome_decision_reject, so info is sufficient and ECS-valid for the type.

Recommendation:

Use an ECS-valid type for iam and rely on event.outcome for the accept/reject distinction:

        if (name == 'tool_decision') {
          ctx.event.category = ['iam'];
          ctx.event.type = ['info'];
          return;
        }

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

@efd6 efd6 force-pushed the 19967-claude_code branch from 48134a5 to 2baf602 Compare July 6, 2026 05:47
- api
type:
- error
- info

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🔵 Low confidence: low path: packages/claude_code/data_stream/events/elasticsearch/ingest_pipeline/default.yml:106

api_error and api_retries_exhausted now map event.type to info instead of error; consider keeping error so failed API events remain identifiable by event.type.

Details

This change re-categorizes api_error and api_retries_exhausted with event.type: [info] while event.outcome is still set to failure (via set_event_outcome_failure). error is a valid ECS event.type value intended exactly for events that describe an error, and it is the semantically accurate sub-bucket for these two event names. Typing an error event as info reduces its accuracy for the ECS categorization filters that detection rules and dashboards commonly use (e.g. event.category:api AND event.type:error); such consumers would no longer match these events even though they represent failures. The failure signal is preserved in event.outcome, so this is a minor semantic point rather than a functional break.

Recommendation:

Keep event.type: error for the two failure event types so they stay identifiable by categorization, consistent with the retained event.outcome: failure:

          api_error:
            category:
              - api
            type:
              - error
          api_retries_exhausted:
            category:
              - api
            type:
              - error

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

@efd6 efd6 Jul 6, 2026

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.

This contradicts previous advice.

I agree that this is a loss, but the constraint exists. IMO, the api category should admit the error type.

@andrewkroh andrewkroh added the documentation Improvements or additions to documentation. Applied to PRs that modify *.md files. label Jul 6, 2026
@efd6 efd6 force-pushed the 19967-claude_code branch from 2baf602 to 1a0c67d Compare July 6, 2026 22:19
Map custom fields to their ECS gen_ai.* equivalents (model, tokens,
cache, tool name/id/arguments, operation, provider) and to ECS core
fields (error.code, error.type, organization.id, user.email, user.id).
error.message is intentionally excluded due to elastic-package
overloading the field.

Fix event.category per event type: api_* events use "api" instead of
"web"; tool_decision uses "iam" with allowed/denied type derived from
the decision attribute; user_prompt and skill_activated drop their
category (no closed-vocab value fits); hook_registered moves to
"configuration"; hook_execution_start/complete get proper start/end
types; plugin_loaded moves to "library".

Derive event.outcome from the decision field for tool_decision events
(accept=success, reject=failure) instead of leaving it unknown.
@efd6 efd6 force-pushed the 19967-claude_code branch from 1a0c67d to 006ac9b Compare July 6, 2026 22:19

@navnit-elastic navnit-elastic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we also update dashboards to use renamed fields?

@efd6

efd6 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@navnit-elastic, good catch.

"isBucketed": false,
"scale": "ratio",
"sourceField": "duration_ms",
"sourceField": "claude_code.events.duration_ms",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 High confidence: high path: packages/claude_code/kibana/dashboard/claude_code-cost-usage.json:1042

Panels reference claude_code.events.duration_ms, but this PR's pipeline removes that field (remove_ms_duration) after converting it to event.duration; point the panels at event.duration instead.

Details

The migration qualifies duration_ms to claude_code.events.duration_ms (lines 1042 and 1052), but in the same PR the ingest pipeline adds a remove processor (tag: remove_ms_duration) that deletes claude_code.events.duration_ms after copying it into event.duration (nanoseconds), and fields.yml drops the duration_ms field definition. No ingested document will carry claude_code.events.duration_ms, so these median/95th-percentile duration metrics will be empty.

Recommendation:

Reference the ECS field the pipeline populates instead. Note event.duration is in nanoseconds, so adjust the value format accordingly:

"sourceField": "event.duration"

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

"isBucketed": false,
"scale": "ratio",
"sourceField": "duration_ms",
"sourceField": "claude_code.events.duration_ms",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 High confidence: high path: packages/claude_code/kibana/dashboard/claude_code-mcp-server-access.json:1075

duration_ms median panel references claude_code.events.duration_ms, which this PR's pipeline removes; use event.duration instead.

Details

This panel's sourceField was migrated to claude_code.events.duration_ms, but the pipeline in this PR removes claude_code.events.duration_ms (tag: remove_ms_duration) after converting it to event.duration, and fields.yml no longer declares duration_ms. The panel will show no data.

Recommendation:

Point the metric at the populated ECS field (nanoseconds):

"sourceField": "event.duration"

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

"excludeIsRegex": false
},
"sourceField": "duration_ms"
"sourceField": "claude_code.events.duration_ms"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 High confidence: high path: packages/claude_code/kibana/dashboard/claude_code-session-timeline.json:832

Session timeline references claude_code.events.duration_ms, which this PR's pipeline removes; use event.duration instead.

Details

The migrated sourceField claude_code.events.duration_ms is deleted by the pipeline's remove_ms_duration processor added in this PR (the value is preserved in event.duration, nanoseconds) and is no longer declared in fields.yml, so this table column will be empty.

Recommendation:

Reference the ECS field the pipeline populates (nanoseconds):

"sourceField": "event.duration"

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

"isBucketed": false,
"scale": "ratio",
"sourceField": "duration_ms",
"sourceField": "claude_code.events.duration_ms",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 High confidence: high path: packages/claude_code/kibana/dashboard/claude_code-tool-call-analysis.json:461

Tool-call-analysis duration panels reference claude_code.events.duration_ms, which this PR's pipeline removes; use event.duration instead.

Details

The migrated sourceField claude_code.events.duration_ms (also at lines 798, 808, and 1418) is deleted by the pipeline's remove_ms_duration processor added in this PR and dropped from fields.yml; the value now lives in event.duration (nanoseconds). These median/percentile duration metrics will be empty.

Recommendation:

Reference the populated ECS field (nanoseconds) for each occurrence:

"sourceField": "event.duration"

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

"type": "phrase",
"key": "tool_name",
"field": "tool_name",
"field": "gen_ai.tool.name",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 High confidence: medium path: packages/claude_code/kibana/dashboard/claude_code-mcp-server-access.json:437

MCP tool filters now query gen_ai.tool.name for the literal value "mcp_tool", but the pipeline maps mcp_tool_name (e.g. list_projects) into gen_ai.tool.name and removes the "mcp_tool" marker, so these filters match nothing.

Details

These filters (also at lines 794 and 989) were migrated from tool_name to gen_ai.tool.name while keeping the match value "mcp_tool". For MCP tool_result events the pipeline renames claude_code.events.mcp_tool_name into gen_ai.tool.name (yielding the specific tool name such as list_projects) and then removes the literal tool_name value "mcp_tool" (tag: remove_tool_name_after_mcp). gen_ai.tool.name therefore never equals "mcp_tool", so every panel gated on this filter returns zero results.

Recommendation:

Select MCP tool events by the presence of the MCP server name rather than the removed "mcp_tool" marker:

{
  "meta": {
    "type": "exists",
    "key": "claude_code.events.mcp_server_name",
    "field": "claude_code.events.mcp_server_name"
  },
  "query": { "exists": { "field": "claude_code.events.mcp_server_name" } }
}

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

},
"4b22e68f-2839-cd9a-4737-d12438c65d2c": {
"label": "Top 20 values of mcp_tool_name",
"label": "Top 20 values of mcp_gen_ai.tool.name",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🔵 Low confidence: high path: packages/claude_code/kibana/dashboard/claude_code-mcp-server-access.json:1032

A global field rename mangled a column label into "Top 20 values of mcp_gen_ai.tool.name"; restore a clean label such as "MCP tool".

Details

The field-rename pass rewrote the substring inside the display label, producing "Top 20 values of mcp_gen_ai.tool.name". This is a cosmetic artifact of the search/replace, not a valid field path, and is shown to users as the column header.

Recommendation:

Give the column a readable label:

"label": "MCP tool"

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

@efd6 efd6 force-pushed the 19967-claude_code branch from b71d1cb to 826b0f5 Compare July 7, 2026 07:18
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

✅ All changelog entries have the correct PR link.

@vera-review-bot

Copy link
Copy Markdown

Reviewed the latest commits 826b0f5 — nothing new beyond already posted comments.

Review summary

Issues found across earlier commits b71d1cb — 5 high, 1 low
  • 🟠 Panels reference claude_code.events.duration_ms, but this PR's pipeline removes that field (remove_ms_duration) after converting it to event.duration (link) (Unresolved)
  • 🟠 duration_ms median panel references claude_code.events.duration_ms, which this PR's pipeline removes (link) (Unresolved)
  • 🟠 Session timeline references claude_code.events.duration_ms, which this PR's pipeline removes (link) (Unresolved)
  • 🟠 Tool-call-analysis duration panels reference claude_code.events.duration_ms, which this PR's pipeline removes (link) (Unresolved)
  • 🟠 MCP tool filters now query gen_ai.tool.name for the literal value "mcp_tool", but the pipeline maps mcp_tool_name (e.g. list_projects) into gen_ai.tool.name and removes the "mcp_tool" marker, so these filters match nothing. (link) (Unresolved)
  • 🔵 A global field rename mangled a column label into "Top 20 values of mcp_gen_ai.tool.name" (link) (Unresolved)
Issues found across earlier commits 2baf602 — 1 low
  • 🔵 api_error and api_retries_exhausted now map event.type to info instead of error (link) (Unresolved)
Issues found across earlier commits 48134a5 — 3 medium
  • 🟡 api_error and api_retries_exhausted map event.type error under category api, but error is not a valid ECS type for api (link) (Unresolved)
  • 🟡 plugin_loaded maps event.type info under category library, but library only allows type start in ECS (link) (Unresolved)
  • 🟡 tool_decision sets event.type allowed/denied under category iam, but iam does not sanction those types in ECS (link) (Unresolved)

A new commit triggers another review — at most once every 15 minutes. I skip the PR while it's approved or has merge conflicts.

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @efd6

@andrewkroh andrewkroh added the dashboard Relates to a Kibana dashboard bug, enhancement, or modification. label Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dashboard Relates to a Kibana dashboard bug, enhancement, or modification. documentation Improvements or additions to documentation. Applied to PRs that modify *.md files. enhancement New feature or request Integration:claude_code Claude Code Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[claude_code] Improve ECS field mappings: adopt gen_ai.* fields and fix event.category assignments

3 participants