📡 OTel Instrumentation Improvement: add github.workflow_ref resource attribute to all OTel spans
Analysis Date: 2026-04-24
Priority: High
Effort: Small (< 2h)
Problem
Every OTel span emitted by send_otlp_span.cjs is missing the github.workflow_ref resource attribute. This attribute would contain the full workflow file path including the git ref — e.g. github/gh-aw/.github/workflows/otel-advisor.yml@refs/heads/main.
Without it, a DevOps engineer cannot answer the question "which workflow YAML file produced this span?" using an OTel backend query alone. A repository with multiple compiled gh-aw workflows (e.g. pr-labeler, issue-triage, daily-report) produces spans that all share service.name = "gh-aw" and can only be distinguished by the non-standard span attribute gh-aw.workflow.name — which backends don't index as a first-class dimension.
The value IS already read from the environment: aw_context.cjs:142 captures process.env.GITHUB_WORKFLOW_REF as workflow_id for workflow dispatch propagation. It just never makes it into the spans.
Why This Matters (DevOps Perspective)
The standard github.* resource attribute set on every span (github.repository, github.run_id, github.ref, github.sha, github.event_name) is rich enough to correlate a span to a specific commit and branch — but not to a specific workflow file. For any multi-workflow repository, dashboard queries like "alert me when the production-deploy workflow fails" require this field.
GITHUB_WORKFLOW_REF includes the branch ref (@refs/heads/main), which means it also distinguishes "this workflow from main" from "this workflow from a feature branch". That distinction is critical for:
- Scoping production-only alerts (filter
github.workflow_ref ends with @refs/heads/main)
- Attributing cost / token spend to individual workflow files
- Root-causing regressions introduced by workflow YAML changes across branches
Without this attribute, operators must fall back to parsing the non-standard gh-aw.workflow.name span attribute, which only contains the markdown-defined friendly name and is not indexed as a standard GitHub dimension in Grafana/Honeycomb/Datadog.
Current Behavior
Neither sendJobSetupSpan nor sendJobConclusionSpan includes github.workflow_ref in resource attributes.
// Current: actions/setup/js/send_otlp_span.cjs (lines 516–536, setup span)
const resourceAttributes = [buildAttr("github.repository", repository), buildAttr("github.run_id", runId)];
if (repository && runId) {
const [owner, repo] = repository.split("/");
resourceAttributes.push(buildAttr("github.actions.run_url", buildWorkflowRunUrl({ runId }, { owner, repo })));
}
if (eventName) {
resourceAttributes.push(buildAttr("github.event_name", eventName));
}
if (ref) {
resourceAttributes.push(buildAttr("github.ref", ref));
}
if (refName) {
resourceAttributes.push(buildAttr("github.ref_name", refName));
}
if (headRef) {
resourceAttributes.push(buildAttr("github.head_ref", headRef));
}
if (sha) {
resourceAttributes.push(buildAttr("github.sha", sha));
}
resourceAttributes.push(buildAttr("deployment.environment", staged ? "staging" : "production"));
// ❌ github.workflow_ref is never set
The env var is already read elsewhere in the codebase — aw_context.cjs:142 does:
// aw_context.cjs:140–142
// GITHUB_WORKFLOW_REF provides the full workflow file path including the ref,
// e.g. "owner/repo/.github/workflows/dispatcher.yml@refs/heads/main"
workflow_id: process.env.GITHUB_WORKFLOW_REF ?? "",
So the value is available at the same point in the process — it is simply never forwarded into the span.
Proposed Change
In sendJobSetupSpan (around line 493), read the env var:
// actions/setup/js/send_otlp_span.cjs — sendJobSetupSpan
const workflowRef = process.env.GITHUB_WORKFLOW_REF || "";
Then add it to resourceAttributes (after the existing sha block, before deployment.environment):
if (workflowRef) {
resourceAttributes.push(buildAttr("github.workflow_ref", workflowRef));
}
In sendJobConclusionSpan (around line 704), read with aw_info fallback:
// actions/setup/js/send_otlp_span.cjs — sendJobConclusionSpan
const workflowRef = process.env.GITHUB_WORKFLOW_REF || (typeof awInfo.context?.workflow_id === "string" ? awInfo.context.workflow_id : "");
Then add to resourceAttributes (same position, before deployment.environment):
if (workflowRef) {
resourceAttributes.push(buildAttr("github.workflow_ref", workflowRef));
}
The fallback to awInfo.context?.workflow_id ensures conclusion spans for dispatched child workflows still carry the caller's workflow ref when GITHUB_WORKFLOW_REF is not in the environment.
Expected Outcome
After this change:
- In Grafana / Honeycomb / Datadog: every span can be filtered or grouped by
github.workflow_ref. Dashboard panels like "P95 agent latency by workflow" and "error rate by workflow file" become a one-line query rather than a regex over gh-aw.workflow.name.
- In the JSONL mirror (
/tmp/gh-aw/otel.jsonl): the resourceSpans[0].resource.attributes array in every entry will contain {"key": "github.workflow_ref", "value": {"stringValue": "github/gh-aw/.github/workflows/otel-advisor.yml@refs/heads/main"}}, making artifact-based post-hoc debugging faster.
- For on-call engineers: "which workflow YAML is responsible for this alert?" becomes answerable directly from the span, without cross-referencing the run URL or
gh-aw.workflow.name.
Implementation Steps
Evidence from Live Sentry Data
The Sentry MCP server had no tools registered in this workflow run (sentry.json was empty), so live span sampling was not possible. The finding is based entirely on static analysis of the instrumentation code.
Structural evidence from the code:
aw_context.cjs:142 already reads process.env.GITHUB_WORKFLOW_REF and stores it as workflow_id, proving the env var is available at runtime.
send_otlp_span.cjs:516–536 and send_otlp_span.cjs:802–822 enumerate all resource attributes built for each span — github.workflow_ref is absent from both lists.
send_otlp_span.test.cjs has explicit coverage for every other github.* resource attribute (github.repository, github.run_id, github.event_name, github.ref, github.ref_name, github.head_ref, github.sha, github.actions.run_url) but no test case for github.workflow_ref.
Related Files
actions/setup/js/send_otlp_span.cjs — primary change location (both sendJobSetupSpan and sendJobConclusionSpan)
actions/setup/js/send_otlp_span.test.cjs — test assertions to add
actions/setup/js/aw_context.cjs — already reads GITHUB_WORKFLOW_REF as workflow_id; no change needed
Generated by the Daily OTel Instrumentation Advisor workflow
Generated by Daily OTel Instrumentation Advisor · ● 266.3K · ◷
📡 OTel Instrumentation Improvement: add
github.workflow_refresource attribute to all OTel spansAnalysis Date: 2026-04-24
Priority: High
Effort: Small (< 2h)
Problem
Every OTel span emitted by
send_otlp_span.cjsis missing thegithub.workflow_refresource attribute. This attribute would contain the full workflow file path including the git ref — e.g.github/gh-aw/.github/workflows/otel-advisor.yml@refs/heads/main.Without it, a DevOps engineer cannot answer the question "which workflow YAML file produced this span?" using an OTel backend query alone. A repository with multiple compiled gh-aw workflows (e.g.
pr-labeler,issue-triage,daily-report) produces spans that all shareservice.name = "gh-aw"and can only be distinguished by the non-standard span attributegh-aw.workflow.name— which backends don't index as a first-class dimension.The value IS already read from the environment:
aw_context.cjs:142capturesprocess.env.GITHUB_WORKFLOW_REFasworkflow_idfor workflow dispatch propagation. It just never makes it into the spans.Why This Matters (DevOps Perspective)
The standard
github.*resource attribute set on every span (github.repository,github.run_id,github.ref,github.sha,github.event_name) is rich enough to correlate a span to a specific commit and branch — but not to a specific workflow file. For any multi-workflow repository, dashboard queries like "alert me when the production-deploy workflow fails" require this field.GITHUB_WORKFLOW_REFincludes the branch ref (@refs/heads/main), which means it also distinguishes "this workflow from main" from "this workflow from a feature branch". That distinction is critical for:github.workflow_refends with@refs/heads/main)Without this attribute, operators must fall back to parsing the non-standard
gh-aw.workflow.namespan attribute, which only contains the markdown-defined friendly name and is not indexed as a standard GitHub dimension in Grafana/Honeycomb/Datadog.Current Behavior
Neither
sendJobSetupSpannorsendJobConclusionSpanincludesgithub.workflow_refin resource attributes.The env var is already read elsewhere in the codebase —
aw_context.cjs:142does:So the value is available at the same point in the process — it is simply never forwarded into the span.
Proposed Change
In
sendJobSetupSpan(around line 493), read the env var:Then add it to
resourceAttributes(after the existingshablock, beforedeployment.environment):In
sendJobConclusionSpan(around line 704), read with aw_info fallback:Then add to
resourceAttributes(same position, beforedeployment.environment):The fallback to
awInfo.context?.workflow_idensures conclusion spans for dispatched child workflows still carry the caller's workflow ref whenGITHUB_WORKFLOW_REFis not in the environment.Expected Outcome
After this change:
github.workflow_ref. Dashboard panels like "P95 agent latency by workflow" and "error rate by workflow file" become a one-line query rather than a regex overgh-aw.workflow.name./tmp/gh-aw/otel.jsonl): theresourceSpans[0].resource.attributesarray in every entry will contain{"key": "github.workflow_ref", "value": {"stringValue": "github/gh-aw/.github/workflows/otel-advisor.yml@refs/heads/main"}}, making artifact-based post-hoc debugging faster.gh-aw.workflow.name.Implementation Steps
sendJobSetupSpan(~line 493): addconst workflowRef = process.env.GITHUB_WORKFLOW_REF || "";sendJobSetupSpanresource attributes block (~line 530): add conditionalif (workflowRef) { resourceAttributes.push(buildAttr("github.workflow_ref", workflowRef)); }sendJobConclusionSpan(~line 704): addconst workflowRef = process.env.GITHUB_WORKFLOW_REF || (typeof awInfo.context?.workflow_id === "string" ? awInfo.context.workflow_id : "");sendJobConclusionSpanresource attributes block (~line 817): add the same conditional pushsend_otlp_span.test.cjsto assertgithub.workflow_refis present whenGITHUB_WORKFLOW_REFis set, following the same pattern as the existinggithub.ref/github.shatests (e.g. lines 1202–1243)GITHUB_WORKFLOW_REFis not setcd actions/setup/js && npx vitest runto confirm tests passmake fmtto ensure formattingEvidence from Live Sentry Data
The Sentry MCP server had no tools registered in this workflow run (
sentry.jsonwas empty), so live span sampling was not possible. The finding is based entirely on static analysis of the instrumentation code.Structural evidence from the code:
aw_context.cjs:142already readsprocess.env.GITHUB_WORKFLOW_REFand stores it asworkflow_id, proving the env var is available at runtime.send_otlp_span.cjs:516–536andsend_otlp_span.cjs:802–822enumerate all resource attributes built for each span —github.workflow_refis absent from both lists.send_otlp_span.test.cjshas explicit coverage for every othergithub.*resource attribute (github.repository,github.run_id,github.event_name,github.ref,github.ref_name,github.head_ref,github.sha,github.actions.run_url) but no test case forgithub.workflow_ref.Related Files
actions/setup/js/send_otlp_span.cjs— primary change location (bothsendJobSetupSpanandsendJobConclusionSpan)actions/setup/js/send_otlp_span.test.cjs— test assertions to addactions/setup/js/aw_context.cjs— already readsGITHUB_WORKFLOW_REFasworkflow_id; no change neededGenerated by the Daily OTel Instrumentation Advisor workflow