📡 OTel Instrumentation Improvement: gh-aw.workflow.name missing from conclusion spans on failed runs
Analysis Date: 2026-04-14
Priority: High
Effort: Small (< 2h)
Problem
sendJobConclusionSpan resolves workflowName exclusively from /tmp/gh-aw/aw_info.json:
// send_otlp_span.cjs line 684
const workflowName = awInfo.workflow_name || "";
aw_info.json is written by the agent job step. When a job fails before the agent runs — or on non-agent jobs (safe-outputs, activation-only, check jobs) — aw_info.json is absent or empty. The conclusion span then emits gh-aw.workflow.name: "".
By contrast, sendJobSetupSpan has a three-layer fallback (line 486):
const workflowName = process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || "";
GH_AW_INFO_WORKFLOW_NAME is injected by the gh-aw compiler at the job level and is always present in the runner environment, including in the post-step where conclusion spans are sent. So conclusion spans can read it — they just don't today.
Why This Matters (DevOps Perspective)
The conclusion span is the primary failure signal: it carries gh-aw.agent.conclusion, error events, token counts, and rate-limit data. Engineers debugging outages filter by gh-aw.workflow.name to scope dashboards to a specific workflow.
Because conclusion spans for failed runs have an empty workflow name, those spans are invisible in any Grafana/Honeycomb/Datadog panel that groups or filters by gh-aw.workflow.name. The failure case — exactly the signal that matters most for MTTR reduction — drops out of the view. The result is that failure-rate panels built on conclusion spans silently undercount failures per workflow.
Current Behavior
// Current: actions/setup/js/send_otlp_span.cjs (line 684)
// Only reads aw_info.json; no env-var fallback.
const workflowName = awInfo.workflow_name || "";
If aw_info.json is missing, gh-aw.workflow.name is set to "" in the OTLP span and in the JSONL mirror.
For comparison, the setup span (line 486) reads:
// actions/setup/js/send_otlp_span.cjs (line 486)
const workflowName = process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || "";
Proposed Change
Add the same env-var fallback chain to sendJobConclusionSpan:
// Proposed: actions/setup/js/send_otlp_span.cjs (line 684)
// Mirror the three-layer fallback used by sendJobSetupSpan so conclusion spans
// always carry workflow name even when aw_info.json is absent (early failures,
// non-agent jobs).
const workflowName = awInfo.workflow_name || process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || "";
No other changes are needed — workflowName is already passed to buildAttr("gh-aw.workflow.name", workflowName) on the next line.
Expected Outcome
After this change:
- In Grafana / Honeycomb / Datadog:
gh-aw.workflow.name will be populated in conclusion spans for all jobs — including failed runs that never reached the agent step. Failure-rate panels grouped by workflow name will no longer silently drop failed jobs.
- In the JSONL mirror: Every conclusion span in
/tmp/gh-aw/otel.jsonl will include the correct gh-aw.workflow.name, making artifact-based post-hoc debugging consistent with what is sent to the OTLP backend.
- For on-call engineers: Querying
gh-aw.workflow.name = "triage-issues" will now return both setup and conclusion spans, enabling end-to-end trace correlation without gaps.
Implementation Steps
Evidence from Static Code Analysis
No Sentry MCP was available for live span queries in this environment. The gap is confirmed by direct code inspection:
| Location |
workflowName resolution |
sendJobSetupSpan — send_otlp_span.cjs:486 |
process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || "" ✅ robust |
sendJobConclusionSpan — send_otlp_span.cjs:684 |
awInfo.workflow_name || "" ⚠️ no env-var fallback |
The asymmetry is deliberate enough to have separate, named variables but the fallback chain was not carried forward when the conclusion span was written. Because GH_AW_INFO_WORKFLOW_NAME is a compiler-injected job-level env var (documented in the JSDoc at line 428), it is available in the post-step runner environment where conclusion spans are emitted.
Related Files
actions/setup/js/send_otlp_span.cjs — primary change location (line 684)
actions/setup/js/action_conclusion_otlp.cjs — orchestrates conclusion span (no change needed)
actions/setup/js/action_conclusion_otlp.test.cjs — test coverage to add
actions/setup/js/send_otlp_span.test.cjs — optional unit test for the fallback logic
Generated by the Daily OTel Instrumentation Advisor workflow
Generated by Daily OTel Instrumentation Advisor · ● 161K · ◷
📡 OTel Instrumentation Improvement:
gh-aw.workflow.namemissing from conclusion spans on failed runsAnalysis Date: 2026-04-14
Priority: High
Effort: Small (< 2h)
Problem
sendJobConclusionSpanresolvesworkflowNameexclusively from/tmp/gh-aw/aw_info.json:aw_info.jsonis written by the agent job step. When a job fails before the agent runs — or on non-agent jobs (safe-outputs, activation-only, check jobs) —aw_info.jsonis absent or empty. The conclusion span then emitsgh-aw.workflow.name: "".By contrast,
sendJobSetupSpanhas a three-layer fallback (line 486):GH_AW_INFO_WORKFLOW_NAMEis injected by the gh-aw compiler at the job level and is always present in the runner environment, including in the post-step where conclusion spans are sent. So conclusion spans can read it — they just don't today.Why This Matters (DevOps Perspective)
The conclusion span is the primary failure signal: it carries
gh-aw.agent.conclusion, error events, token counts, and rate-limit data. Engineers debugging outages filter bygh-aw.workflow.nameto scope dashboards to a specific workflow.Because conclusion spans for failed runs have an empty workflow name, those spans are invisible in any Grafana/Honeycomb/Datadog panel that groups or filters by
gh-aw.workflow.name. The failure case — exactly the signal that matters most for MTTR reduction — drops out of the view. The result is that failure-rate panels built on conclusion spans silently undercount failures per workflow.Current Behavior
If
aw_info.jsonis missing,gh-aw.workflow.nameis set to""in the OTLP span and in the JSONL mirror.For comparison, the setup span (line 486) reads:
Proposed Change
Add the same env-var fallback chain to
sendJobConclusionSpan:No other changes are needed —
workflowNameis already passed tobuildAttr("gh-aw.workflow.name", workflowName)on the next line.Expected Outcome
After this change:
gh-aw.workflow.namewill be populated in conclusion spans for all jobs — including failed runs that never reached the agent step. Failure-rate panels grouped by workflow name will no longer silently drop failed jobs./tmp/gh-aw/otel.jsonlwill include the correctgh-aw.workflow.name, making artifact-based post-hoc debugging consistent with what is sent to the OTLP backend.gh-aw.workflow.name = "triage-issues"will now return both setup and conclusion spans, enabling end-to-end trace correlation without gaps.Implementation Steps
actions/setup/js/send_otlp_span.cjs, change line 684 fromconst workflowName = awInfo.workflow_name || "";toconst workflowName = awInfo.workflow_name || process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || "";actions/setup/js/action_conclusion_otlp.test.cjs(orsend_otlp_span.test.cjs) asserting that whenaw_info.jsonis absent butGH_AW_INFO_WORKFLOW_NAMEis set, the conclusion span carries the correctgh-aw.workflow.nameattributecd actions/setup/js && npx vitest run(ormake test-unit) to confirm all tests passmake fmtto ensure formattingEvidence from Static Code Analysis
No Sentry MCP was available for live span queries in this environment. The gap is confirmed by direct code inspection:
workflowNameresolutionsendJobSetupSpan—send_otlp_span.cjs:486process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || ""✅ robustsendJobConclusionSpan—send_otlp_span.cjs:684awInfo.workflow_name || ""The asymmetry is deliberate enough to have separate, named variables but the fallback chain was not carried forward when the conclusion span was written. Because
GH_AW_INFO_WORKFLOW_NAMEis a compiler-injected job-level env var (documented in the JSDoc at line 428), it is available in the post-step runner environment where conclusion spans are emitted.Related Files
actions/setup/js/send_otlp_span.cjs— primary change location (line 684)actions/setup/js/action_conclusion_otlp.cjs— orchestrates conclusion span (no change needed)actions/setup/js/action_conclusion_otlp.test.cjs— test coverage to addactions/setup/js/send_otlp_span.test.cjs— optional unit test for the fallback logicGenerated by the Daily OTel Instrumentation Advisor workflow