Skip to content

[otel-advisor] OTel improvement: add trigger item context (item_type, item_number, trigger_label) to all spansΒ #28987

@github-actions

Description

@github-actions

πŸ“‘ OTel Instrumentation Improvement: Add Trigger Item Context to All Spans

Analysis Date: 2026-04-28
Priority: High
Effort: Small (< 2h)

Problem

Every gh-aw workflow is triggered by a specific GitHub item β€” an issue, a pull request, a discussion, or a check run. The buildAwContext() function in aw_context.cjs resolves these details (item_type, item_number, comment_id, trigger_label) from the event payload and writes them to aw_info.json under context.

However, neither sendJobSetupSpan nor sendJobConclusionSpan in send_otlp_span.cjs reads these fields or emits them as span attributes. As a result, every OTel span is completely blind to what triggered the workflow. A DevOps engineer looking at a failing span in Grafana or Honeycomb cannot answer:

This is the most operationally valuable missing attribute: it directly links every trace to a concrete, actionable GitHub item.

Why This Matters (DevOps Perspective)

Without item context, correlating a failed span back to its triggering GitHub item requires:

  1. Find the run ID in the span
  2. Open GitHub Actions
  3. Navigate to the workflow run
  4. Deduce the triggering item from the workflow trigger metadata

With gh-aw.trigger.item_type and gh-aw.trigger.item_number on every span, engineers can:

  • Build Grafana panels like "Agent failures by item type" or "Top failing issues by number"
  • Set alerts on repeated failures for the same item number (e.g., the same PR keeps timing out)
  • Deep-link from Honeycomb/Datadog straight to the triggering issue or PR
  • Reduce MTTR by immediately knowing context β€” e.g., "issue Add source field to track workflow origin in frontmatterΒ #1234 triggered 3 failed runs today"
Current Behavior

In actions/setup/js/aw_context.cjs, buildAwContext() resolves full item context:

// aw_context.cjs β€” buildAwContext() returns:
{
  item_type: "pull_request",     // or "issue", "discussion", "check_run", ...
  item_number: "456",            // PR/issue/discussion number or check ID
  comment_id: "789012345",       // triggering comment ID (if any)
  trigger_label: "bug",          // label that triggered the workflow (if any)
  otel_trace_id: "...",          // ← already propagated to spans
  otel_parent_span_id: "...",    // ← already propagated to spans
  deployment_state: "...",       // ← already propagated to spans
  // ...
}

In send_otlp_span.cjs, sendJobSetupSpan reads awInfo.context for OTel propagation fields only:

// actions/setup/js/send_otlp_span.cjs (lines 464–518)
const rawContextTraceId = typeof awInfo.context?.otel_trace_id === "string" ? ... : "";
const rawContextParentSpanId = typeof awInfo.context?.otel_parent_span_id === "string" ? ... : "";
const deploymentStateSetup = ... || (typeof awInfo.context?.deployment_state === "string" ? awInfo.context.deployment_state : "");
// item_type, item_number, comment_id, trigger_label β€” never read, never emitted

The same gap exists in sendJobConclusionSpan (lines 692, 757).

Proposed Change

In sendJobSetupSpan and sendJobConclusionSpan, read the four trigger-context fields from awInfo.context and emit them as span attributes.

// Proposed addition to sendJobSetupSpan and sendJobConclusionSpan
// in actions/setup/js/send_otlp_span.cjs

// After the existing awInfo.context reads (around line 518 for setup, 757 for conclusion):
const triggerItemType = typeof awInfo.context?.item_type === "string" ? awInfo.context.item_type : "";
const triggerItemNumber = typeof awInfo.context?.item_number === "string" ? awInfo.context.item_number : "";
const triggerLabel = typeof awInfo.context?.trigger_label === "string" ? awInfo.context.trigger_label : "";

// In the attributes array:
if (triggerItemType) {
  attributes.push(buildAttr("gh-aw.trigger.item_type", triggerItemType));
}
if (triggerItemNumber) {
  attributes.push(buildAttr("gh-aw.trigger.item_number", triggerItemNumber));
}
if (triggerLabel) {
  attributes.push(buildAttr("gh-aw.trigger.label", triggerLabel));
}

These three attributes are non-sensitive (item types are enum values, item numbers are public GitHub resource IDs) and do not need to be added to the redaction pattern in SENSITIVE_ATTR_KEY_RE.

Expected Outcome

After this change:

  • In Grafana / Honeycomb / Datadog: spans can be grouped by gh-aw.trigger.item_type ("issue" vs "pull_request" vs "discussion") and filtered by gh-aw.trigger.item_number to find all runs associated with a specific GitHub item.
  • In the JSONL mirror: /tmp/gh-aw/otel.jsonl will include trigger context on every span, making the artifact immediately useful for "which issue triggered this failing run?" without opening the GitHub Actions UI.
  • For on-call engineers: the first thing visible in any span detail view will identify the triggering item, reducing the time-to-context from ~3 navigation steps to zero.
Implementation Steps
  • In actions/setup/js/send_otlp_span.cjs, add reads for awInfo.context.item_type, awInfo.context.item_number, and awInfo.context.trigger_label in both sendJobSetupSpan (after line 518) and sendJobConclusionSpan (after line 757)
  • Emit gh-aw.trigger.item_type, gh-aw.trigger.item_number, and gh-aw.trigger.label as conditional span attributes in both functions
  • Update actions/setup/js/action_otlp.test.cjs to assert that setup and conclusion spans include trigger attributes when awInfo.context provides them
  • Add a test case for the empty-string fallback (no item context for push/workflow_dispatch events) to confirm no spurious attributes are emitted
  • Run cd actions/setup/js && npx vitest run to confirm tests pass
  • Run make fmt to ensure formatting
  • Open a PR referencing this issue
Evidence from Live Sentry Data

The Sentry MCP server returned an empty tool list ([]) during this analysis run, so live span payload inspection was not possible. The gap is confirmed entirely from static code analysis:

  1. buildAwContext() (aw_context.cjs:143–179) populates item_type, item_number, comment_id, and trigger_label on every workflow invocation.
  2. These fields are serialized into aw_info.json under the context key.
  3. Both sendJobSetupSpan and sendJobConclusionSpan read awInfo.context for OTel propagation fields (otel_trace_id, otel_parent_span_id, deployment_state) but make no mention of item_type, item_number, or trigger_label.
  4. A grep -rn "item_type\|item_number\|trigger_label" actions/setup/js/send_otlp_span.cjs returns zero matches, confirming these attributes are never emitted.
Related Files
  • actions/setup/js/send_otlp_span.cjs β€” primary change target (both sendJobSetupSpan and sendJobConclusionSpan)
  • actions/setup/js/aw_context.cjs β€” source of truth for trigger item context (buildAwContext)
  • actions/setup/js/action_setup_otlp.cjs β€” calls sendJobSetupSpan
  • actions/setup/js/action_conclusion_otlp.cjs β€” calls sendJobConclusionSpan
  • actions/setup/js/action_otlp.test.cjs β€” test file to update with new attribute assertions

Generated by the Daily OTel Instrumentation Advisor workflow

Generated by Daily OTel Instrumentation Advisor Β· ● 204.5K Β· β—·

  • expires on May 5, 2026, 9:37 PM UTC

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions