Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions