Skip to content

Reuse workflow metadata in OTLP attributes - #70860

Merged
pelikhan merged 7 commits into
mainfrom
copilot/feature-reuse-workflow-metadata
Oct 21, 2026
Merged

pelikhan merged 7 commits into
mainfrom
copilot/feature-reuse-workflow-metadata

Conversation

Copilot AI commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

OTLP custom attributes could not reuse workflow metadata, and shared imports dropped resource attributes. This adds compile-time, root-scoped metadata references to both custom attribute maps.

  • Resolution: Accept whole-value ${{ github.aw.metadata.<key> }} references in observability.otlp.attributes and resource-attributes. Missing keys, unsupported selectors, embedded references, and metadata containing GitHub Actions expression delimiters fail compilation.
  • Shared imports: Preserve imported resource attributes while retaining first-import and root-workflow precedence.
  • Documentation: Describe root scope, string-only values, and opt-in export.
# Root workflow
metadata:
  version: "0.5.1"
  category: automation
imports:
  - shared/otlp.md
# shared/otlp.md
observability:
  otlp:
    attributes:
      catalogue.version: "${{ github.aw.metadata.version }}"
      catalogue.category: "${{ github.aw.metadata.category }}"

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for root workflow metadata in custom OTLP attributes Reuse workflow metadata in OTLP attributes Oct 21, 2026
Copilot AI requested a review from pelikhan October 21, 2026 12:36
@pelikhan
pelikhan marked this pull request as ready for review October 21, 2026 14:21
Copilot AI max review requested due to automatic review settings October 21, 2026 14:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Namespace detection rejects valid literal attributes and unrelated expressions, breaking existing workflow compatibility.

1 open issue
What changed in this PR

Adds opt-in reuse of root workflow metadata in gh-aw’s OTLP telemetry, including shared imports.

Changes:

  • Resolves whole-value metadata references at compile time.
  • Preserves imported resource attributes and existing precedence.
  • Adds tests, reference documentation, and a patch release note.
File Description
pkg/​workflow/​observability_metadata.go Resolves and validates metadata references.
pkg/​workflow/​observability_metadata_test.go Tests resolution and rejection cases.
pkg/​workflow/​compiler_orchestrator_workflow.go Runs resolution after import merging.
pkg/​workflow/​compiler_orchestrator_workflow_test.go Tests merged attribute resolution and emission.
pkg/​parser/​import_observability.go Preserves imported resource attributes.
pkg/​parser/​import_observability_test.go Tests first-import precedence.
docs/​src/​content/​docs/​reference/​open-telemetry.mdx Documents metadata references in both attribute maps.
docs/​src/​content/​docs/​reference/​imports.md Explains root-scoped references in shared imports.
docs/​src/​content/​docs/​reference/​frontmatter.md Documents opt-in metadata reuse.
.changeset/​otlp-metadata-attributes.md Records the patch release change.

Review effort: Max

Comment thread pkg/workflow/observability_metadata.go Outdated
}
for name, value := range attributes {
text, ok := value.(string)
if !ok || !strings.Contains(text, otlpMetadataNamespace) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7176f09: detection now scans actual expression references outside quoted strings, ignores literal mentions, and rejects bracket-selected metadata access in both attribute maps.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Test Quality Sentinel · sonnet50 · 15.2 AIC · ⌖ 15.2 AIC · ◇ 6.67 AIC · ⊞ 12.7K · ◷

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

Commit pushed: 36bcf2a

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Generated by Ponytail Reviewer for #70860

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L51-54: shrink: manual extraction repeats the validation regex. Use FindStringSubmatch(text)[1] after the match check.

net: -3 lines possible.

Generated by ✂️ Ponytail Reviewer for #70860 · codex · gpt56 · 7.79 AIC · ⌖ 5.42 AIC · ⊞ 14.8K
Comment /ponytail to run again

Comment thread pkg/workflow/observability_metadata.go Outdated
if !otlpMetadataReferencePattern.MatchString(text) {
return fmt.Errorf("%s must be a whole-value reference in the form ${{ github.aw.metadata.<key> }}", scope)
}
key := strings.TrimSpace(text)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L51-54: shrink: manual extraction repeats the validation regex. Use FindStringSubmatch(text)[1] after the match check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7176f09: the metadata key now comes from the validated FindStringSubmatch capture instead of being manually extracted.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

No additional actionable issues were identified in the changed lines beyond the existing review comment on pkg/workflow/observability_metadata.go:44. The implementation and accompanying tests appear internally consistent; static review only, no tests executed.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · 2.17 AIC · ⌖ 0.548 AIC · ⊞ 1.6K
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR adds 328 new lines of business logic under pkg/, which is over the 100-line threshold, but it does not link an Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/70860-resolve-root-metadata-references-in-otlp-attributes.md. Please review and complete it before merging.

Decision captured in the draft: At compile time, resolve whole-value ${{ github.aw.metadata.<key> }} references in observability.otlp.attributes and resource-attributes against the root workflow's metadata. Validation is strict (missing keys and expression delimiters are errors). Imported resource-attributes are now merged first-wins.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR on your branch. It was generated from the PR diff.
  2. Fill in what the AI could not infer. In particular, confirm the alternatives (import inputs, auto-exporting all metadata, runtime resolution) and why the root-only scope was chosen.
  3. Commit the finished ADR to docs/adr/ on your branch.
  4. Link the ADR in this PR body, for example:

    ADR: ADR-70860: Resolve Root Workflow Metadata References in OTLP Attributes at Compile Time

Once the ADR is linked in the PR body, this gate re-runs and checks that the implementation matches the decision.

❓ Why ADRs Matter

ADRs keep a searchable, permanent record of why the codebase looks the way it does. Future contributors, including your future self, will thank you.

📋 Michael Nygard ADR Format Reference

An ADR is complete only when it has these four sections:

  • Context: What is the problem? What forces are at play?
  • Decision: What did you decide, and why?
  • Alternatives Considered: What else could have been done?
  • Consequences: What are the trade-offs, positive and negative?

ADRs live in docs/adr/ as Markdown files numbered by PR number.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · opus55 · 28.5 AIC · ⌖ 40.5 AIC · ⊞ 1.9K · ◷
Comment /review to run again

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot update compiler threat model and add a actions/aw linter to detect leaks in otlp messages

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /tdd and /diagnosing-bugs (per pr-triage) — commenting with test-coverage and robustness suggestions on the new resolveOTLPMetadataReferences logic.

📋 Key Themes & Highlights

Key Themes

  • Determinism: iterating a map[string]any while short-circuiting on the first validation error means the reported error can vary by compile run when multiple attributes are invalid.
  • Guard precision: the otlpMetadataNamespace substring check (flagged independently by an existing reviewer comment) can misclassify literal strings or unrelated expressions containing the namespace text as broken metadata references.
  • Test gap: the map[string]string branch of normalizeOTLPAttributeMap — the exact shape produced by the import-merge path just before this function runs — isn't directly exercised by a test.

Positive Highlights

  • ✅ Solid security posture: rejects nested selectors, embedded references, non-string metadata, and metadata values containing expression delimiters, with dedicated regression tests for each.
  • ✅ Good integration test (TestMergeImportedObservabilityResolvesRootMetadataReferences) verifying the full compiler pipeline end-to-end, including env propagation.
  • ✅ Clear, well-scoped error messages that name the exact frontmatter path (observability.otlp.<field>.<name>).
  • ✅ Documentation updates across frontmatter, imports, and open-telemetry reference pages are consistent and accurate.

Investigation note: Review was bounded to the pre-fetched diff and the two core files (observability_metadata.go, compiler_orchestrator_workflow.go); no uncertainty beyond the three issues flagged inline.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 125.8 AIC · ⌖ 15.2 AIC · ⊞ 10.2K
Comment /matt to run again

Comment thread pkg/workflow/observability_metadata.go Outdated
if attributes == nil {
continue
}
for name, value := range attributes {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] Iterating attributes (a map[string]any) to find a validation error means that when multiple attributes are invalid, Go's randomized map iteration order makes which error message is actually returned non-deterministic across compiles. That hurts reproducibility when a user reports a compile failure and CI/local runs surface a different offending key.

💡 Suggested fix

Collect names into a sorted slice before iterating, so error reporting (and therefore debugging) is stable:

names := make([]string, 0, len(attributes))
for name := range attributes {
    names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
    value := attributes[name]
    // ... existing validation ...
}

This also makes future regression tests for "multiple invalid attributes" scenarios deterministic.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7176f09: attribute keys are sorted before validation so the first reported error is deterministic.

Comment thread pkg/workflow/observability_metadata.go Outdated
}
for name, value := range attributes {
text, ok := value.(string)
if !ok || !strings.Contains(text, otlpMetadataNamespace) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] Agreeing with the existing reviewer comment here: the guard strings.Contains(text, otlpMetadataNamespace) is a substring check against the literal text github.aw.metadata, not an expression-boundary check. A value like the literal string "github.aw.metadata.version" (no ${{ }}) or an unrelated expression such as ${{ contains(github.aw.metadata.version, x) }} would hit this branch and then fail the whole-value regex with a confusing error, even though the author never intended a metadata reference.

💡 Suggested test + fix

Add a test case with a literal string value containing the substring but not wrapped as ${{ ... }}, asserting it passes through unchanged (or is handled with a clearer, more specific error). Consider checking strings.Contains(text, "${{") combined with the namespace substring before entering this branch, so truly unrelated values are skipped rather than misclassified as broken metadata references.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7176f09: literal namespace text and quoted expression mentions are ignored; actual metadata accesses inside expressions are still validated.

switch attributes := otlp[field].(type) {
case map[string]any:
return attributes
case map[string]string:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The map[string]string branch of normalizeOTLPAttributeMap (triggered when otlp.attributes/otlp.resource-attributes is already typed as map[string]string, e.g. the output of mergeOTLPStringMaps in the import-merge path) has no direct test exercising it — observability_metadata_test.go only constructs frontmatter with map[string]any. Since this is the exact shape produced right before this function runs in mergeImportedWorkflowConfiguration, add a test that sets up RawFrontmatter with a pre-merged map[string]string attributes map to confirm the mutation-and-normalize path works correctly end-to-end.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7176f09: added a direct test with both pre-merged map[string]string attribute maps in RawFrontmatter, confirming they normalize and resolve.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot update compiler threat model and add a actions/aw linter to detect leaks in otlp messages

Added the CTR-017 OTLP leak lint rule and updated the compiler threat model/docs in commit 672980ec. The linter checks AW attribute maps and modeled Actions environment flows with value-free diagnostics.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes.
  2. Review (pkg/workflow/observability_metadata.go:44): This guard treats namespace text as a metadata access. For example, the literal github.aw.metadata.version and the unrelated expression ${{ 'github.aw.metadata.version' }} both fail compilation in either attribute map. Conversely, ${{ github.aw['metadata'].version }} skips this resolver unchanged instead of receiving an unsupported-reference error. Detect actual metadata accesses inside expressions, respecting quoted strings, before enforcing the whole-value selector syntax. Add regression cases for both maps; merely checking for ${{' would still reject quoted mentions. - Reuse workflow metadata in OTLP attributes #70860 (comment)
  3. Review (pkg/workflow/observability_metadata.go:51): L51-54: shrink: manual extraction repeats the validation regex. Use FindStringSubmatch(text)[1] after the match check. - Reuse workflow metadata in OTLP attributes #70860 (comment)
  4. Review (pkg/workflow/observability_metadata.go:42): [/diagnosing-bugs] Iterating attributes (a map[string]any) to find a validation error means that when multiple attributes are invalid, Go's randomized map iteration order makes which error message is actually returned non-deterministic across compiles. That hurts reproducibility when a user reports a compile failure and CI/local runs surface a different offending key. - Reuse workflow metadata in OTLP attributes #70860 (comment)
  5. Review (pkg/workflow/observability_metadata.go:44): [/tdd] Agreeing with the existing reviewer comment here: the guard strings.Contains(text, otlpMetadataNamespace) is a substring check against the literal text github.aw.metadata, not an expression-boundary check. A value like the literal string \"github.aw.metadata.version\" (no ${{ }}) or an unrelated expression such as ${{ contains(github.aw.metadata.version, x) }} would hit this branch and then fail the whole-value regex with a confusing error, even though the author never intended a metadata reference. - Reuse workflow metadata in OTLP attributes #70860 (comment)
  6. Review (pkg/workflow/observability_metadata.go:79): [/tdd] The map[string]string branch of normalizeOTLPAttributeMap (triggered when otlp.attributes/otlp.resource-attributes is already typed as map[string]string, e.g. the output of mergeOTLPStringMaps in the import-merge path) has no direct test exercising it — observability_metadata_test.go only constructs frontmatter with map[string]any. Since this is the exact shape produced right before this function runs in mergeImportedWorkflowConfiguration, add a test that sets up RawFrontmatter with a pre-merged map[string]string attributes map to confirm the mutation-and-normalize path works correctly end-to-end. - Reuse workflow metadata in OTLP attributes #70860 (comment)

Push the necessary fixes, reply to each listed review thread and resolve it when addressed. Ignore feedback already answered or resolved. Use the pr-finisher skill and stop when only human review or CI remains; do not trigger CI.

Sous-chef head: 8ca0d9e
Sous-chef work: 034a22070a7a20a42dcf37db5943b3e05a15946959837e0672708554c6777acb 1c3ca4e88e8994ff9a0a6875ca4121c71b73bc5f5e1a55bb36388dc8640da4bd 3667d34e1fd19da61132d79344046c05057f90d5310d2d6da7e0f2e34e2c6b84 47b76985239857ddb8fe19849dcd42e0bd8221815fd0b1251e8237e437b9a4cd 72887e4962c08344dcdf1ebc5fe012ca5da289e3736c5a0f07e12bf9c698708d
Sous-chef state: 021f8abdf1f825f5ba0edd9019659fc4f2aed908a4bc048f3fd8db9d64d6cc07

Generated by 👨‍🍳 PR Sous Chef · pi · haiku45 · 5.78 AIC · ⌖ 6.39 AIC · ⊞ 1.1K · ◷
Comment /souschef to run again

Copilot AI and others added 2 commits October 21, 2026 15:52
…-workflow-metadata

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot October 21, 2026 16:31
@pelikhan
pelikhan merged commit 95c0350 into main Oct 21, 2026
3 checks passed
@pelikhan
pelikhan deleted the copilot/feature-reuse-workflow-metadata branch October 21, 2026 21:01
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.93.0

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

4 participants