Skip to content

Feature: reuse root workflow metadata in custom OTLP attributes, including shared imports #70377

Closed

Description

@mason-tim

Feature request

Allow whole-value references to the root workflow's custom metadata in observability.otlp.attributes and observability.otlp.resource-attributes, including when the OTLP configuration comes from a shared import.

Motivation

Workflow metadata describes properties such as version, category and ownership. Making those values reusable gives the existing metadata field practical value beyond descriptive annotation, while keeping it the single source of truth.

OpenTelemetry is a natural destination for this information: operators want to group workflow costs, failures and outcomes by version, category or owner. For example, when a cheaper implementation is released, version-tagged telemetry lets us distinguish older installations from the new implementation rather than infer the version from the model or inspect each repository's workflow source.

Today, authors must repeat metadata values in OTLP configuration or pass them again through github.aw.import-inputs. This creates duplication and drift: metadata can be updated while telemetry continues to report an old value. A shared OTLP import should be able to reference each importing workflow's metadata without callers declaring those values twice or requiring custom deploy-time injection.

This is opt-in reuse of selected metadata, not automatic export of the entire metadata map.

Proposed configuration

Syntax below is a proposal for maintainers to confirm. It follows the existing compile-time github.aw.import-inputs.* namespace pattern; it is not a new context for GitHub Actions to evaluate.

Root workflow:

---
on:
  workflow_dispatch:
engine: copilot
metadata:
  version: "0.5.1"
  category: automation
imports:
  - shared/otlp.md
---

Perform the workflow's task.

Shared component:

---
observability:
  otlp:
    endpoint: ${{ secrets.OTLP_ENDPOINT }}
    headers:
      Authorization: ${{ secrets.OTLP_TOKEN }}
    attributes:
      catalogue.version: "${{ github.aw.metadata.version }}"
      catalogue.category: "${{ github.aw.metadata.category }}"
---

The compiled custom attributes would contain ordinary strings:

{"catalogue.version":"0.5.1","catalogue.category":"automation"}

The same reference syntax would work in resource-attributes, subject to its existing restrictions.

Proposed contract and acceptance criteria

  • Resolve at compile time, using the root workflow's validated top-level metadata map. A reference inside an imported component refers to the root workflow, not the component's own metadata.
  • Support whole-value references only. No concatenation, embedded expressions, defaults, functions or recursive substitution. Define the supported selector spelling explicitly; initially simple identifiers such as version, category and owner are sufficient. Do not accidentally interpret flat metadata keys as nested objects.
  • Restrict resolution to values in the two custom OTLP attribute maps. No substitutions in attribute names, endpoints, headers, permissions, arbitrary YAML or prompt content.
  • A missing metadata map or referenced key produces a compile-time error identifying the attribute and missing key. An unsupported use of the new namespace in these maps should fail clearly rather than export an unresolved placeholder.
  • Metadata and declarative OTLP attribute values remain strings. Preserve existing metadata validation: numeric and boolean YAML values are invalid, not silently coerced. An unquoted semantic version such as 0.1.8 is already a YAML string.
  • Copy validated metadata strings without type inference. Reject unsafe substituted expression content where necessary: metadata must not introduce a fresh GitHub Actions expression or undergo a second evaluation.
  • Leave ordinary literal values and existing GitHub Actions expressions unchanged.
  • Preserve existing metadata import semantics, attribute precedence, output encoding, masking, sanitisation and resource-attribute restrictions.
  • Do not automatically export any metadata; only explicitly referenced fields are selected.
  • Generated locks contain resolved values rather than metadata placeholders, and existing OTLP emitters export those values as string attributes.

Source analysis

Reviewed upstream main at 53152dee6f9658a2d48600be8f861c44222ba783.

Proposed implementation plan

These are suggested locations and responsibilities, not a requirement to adopt a particular internal API.

  1. Add a focused compiler helper in pkg/workflow/observability_metadata.go, with tests in observability_metadata_test.go. Go functions are sufficient; no new class, dependency or general templating engine is needed. Suggested helper: resolveOTLPMetadataReferences(workflowData *WorkflowData) error. It should read root metadata from the original validated frontmatter, inspect the effective custom attribute maps, validate whole-value references, and return resolved string maps/errors without mutating the source metadata or hashing inputs.
  2. Wire resolution into the compiler in compiler_orchestrator_workflow.go, after mergeImportedObservability() and before OTLP env emission. Ensure resolution precedes consumers/validation that need the final values, and that raw/typed frontmatter fallbacks cannot reintroduce unresolved placeholders. Propagate errors using the compiler's normal file-aware diagnostics.
  3. Preserve shared resource attributes in pkg/parser/import_observability.go, extending extraction/aggregation of string maps with tests in import_observability_test.go. Follow existing first-occurrence import precedence and retain existing main/import precedence rather than changing it as part of this feature.
  4. Reuse existing OTLP encoding/export in observability_otlp.go. Resolved strings should flow through current JSON/YAML/resource-attribute encoding, masking and validation. Explicitly guard against metadata strings containing executable expression delimiters. No runtime metadata lookup or exporter change should be needed.
  5. Add end-to-end compiler and exporter regression coverage, extending observability_otlp_test.go and existing import/compiler integration fixtures. If useful, add a focused case in actions/setup/js/send_otlp_span.test.cjs confirming the resolved string is emitted with the ordinary custom-attribute path.
  6. Document the feature in docs/src/content/docs/reference/open-telemetry.mdx, frontmatter.md, and imports.md where appropriate. Show the shared-import example and explain root scope, whole-value syntax, string-only values, missing-key errors, and opt-in export. Record the change using the repository's normal changeset process.
  7. Validate using upstream's required make agent-finish gate, fixing failures without weakening existing checks.

Test cases

Area Cases
Inline references Root metadata resolves in both custom span and resource attributes.
Shared/nested imports Shared OTLP references resolve against root metadata; a shared component's own same-named metadata must not replace the root value.
Multiple callers Compile two workflows using the same shared import with different versions; values remain isolated, including reuse of cached imports.
Missing fields Missing metadata map, missing key and malformed/unsupported reference produce actionable compilation errors.
Whole-value scope Whitespace handling is defined; embedded/concatenated uses of the new namespace are rejected. Existing unrelated expressions remain unchanged.
Types Quoted strings and version: 0.1.8 succeed; numeric and boolean metadata values remain schema-validation failures.
Encoding/safety Quotes, backslashes, newlines, Unicode, empty strings and metadata value-length boundaries; expression-looking metadata cannot introduce executable expressions. Existing empty-value export behaviour remains unchanged.
Import aggregation Resource attributes survive shared-import aggregation; existing collision precedence is retained.
Compatibility Workflows without new references retain their existing configuration/output behaviour; metadata/hash input is not rewritten.
Export Generated lock/env has resolved strings, no new metadata placeholders; existing custom-attribute export preserves the expected string type and sanitisation.

Non-goals

General-purpose variables; arbitrary frontmatter/prompt interpolation; embedded formatting; metadata inheritance changes; typed metadata or string coercion; automatic export of all metadata; repurposing service.version; runtime GitHub/API lookups.

Would this scoped approach and compile-time namespace fit the project's direction?

Activity

  1. locked and limited conversation to collaborators on Oct 19, 2026
  2. unlocked this conversation on Oct 19, 2026
  3. linked a pull request that will close this issueReuse workflow metadata in OTLP attributes #70860on Oct 21, 2026
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