Skip to content

Add frontmatter source/hash/body-modified metadata to OTEL spans#32196

Merged
pelikhan merged 5 commits into
mainfrom
copilot/update-otel-data-fields
May 14, 2026
Merged

Add frontmatter source/hash/body-modified metadata to OTEL spans#32196
pelikhan merged 5 commits into
mainfrom
copilot/update-otel-data-fields

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 14, 2026

✨ Enhancement

OTEL payloads did not include workflow frontmatter provenance/state needed for update analysis. This change adds frontmatter_source (when present), frontmatter_hash, and body_modified so traces can distinguish source drift from frontmatter drift.

  • What does this improve?

    • Adds frontmatter metadata to run telemetry:
      • gh-aw.frontmatter.source
      • gh-aw.frontmatter.hash
      • gh-aw.frontmatter.body_modified
    • Ensures fields are emitted on both setup and conclusion spans.
  • Why is this valuable?

    • Improves trace-level observability for workflow update/debug flows.
    • Makes frontmatter provenance and content-drift signals queryable in OTEL backends.
  • Implementation approach:

    • Propagates compile-time values into generate_aw_info env:
      • GH_AW_INFO_FRONTMATTER_SOURCE (conditional)
      • GH_AW_INFO_FRONTMATTER_HASH (conditional)
      • GH_AW_INFO_BODY_MODIFIED (default false for source-backed workflows)
    • Persists fields in /tmp/gh-aw/aw_info.json as:
      • frontmatter_source, frontmatter_hash, body_modified
    • Reads these fields in OTEL emitters and appends corresponding span attributes.
{
  "frontmatter_source": "owner/repo/.github/workflows/example.md@main",
  "frontmatter_hash": "abc123def456...",
  "body_modified": false
}

Copilot AI and others added 2 commits May 14, 2026 18:44
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title Add frontmatter source/hash/body-modified OTEL metadata Add frontmatter source/hash/body-modified metadata to OTEL spans May 14, 2026
Copilot AI requested a review from pelikhan May 14, 2026 18:51
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot remove gh-aw.frontmatter.source

@pelikhan pelikhan marked this pull request as ready for review May 14, 2026 19:02
Copilot AI review requested due to automatic review settings May 14, 2026 19:02
Copilot stopped work on behalf of pelikhan due to an error May 14, 2026 19:03
@github-actions github-actions Bot mentioned this pull request May 14, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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 /zoom-out — this is a new-feature PR adding OTEL span attributes, so TDD coverage gaps and architectural consistency are the most important lenses.

Key Themes

  1. Code duplication (send_otlp_span.cjs): The 6-line extraction + 3-line attributes.push block for frontmatter fields is copy-pasted identically in both sendJobSetupSpan and sendJobConclusionSpan. A buildFrontmatterAttributes(awInfo) helper would eliminate the duplication and give the logic a testable, named home.

  2. Missing edge-case tests (generate_aw_info.test.cjs): The new test only exercises body_modified: true. The "false" case and the absence cases (empty env var → field not written) are untested. These are exactly the cases that catch regressions.

  3. Coupling asymmetry in compiler (compiler_yaml.go): FRONTMATTER_SOURCE and BODY_MODIFIED share one guard, but FRONTMATTER_HASH has its own — implying they can appear independently. This may be intentional, but it's surprising and worth a comment or collapsing into a single guard.

  4. Unimplemented override path (compiler_yaml.go): BODY_MODIFIED is always "false" at compile time; the comment says an update flow will override it, but that flow isn't visible here. Until it exists, every span emits body_modified: false regardless of actual drift — silently misleading telemetry.

Positive Highlights

  • ✅ Both sendJobSetupSpan and sendJobConclusionSpan are consistently updated — no asymmetry at the span-emission level.
  • ✅ Tests are present for the happy path in all three layers (generate, send setup, send conclusion), which is the right instinct.
  • ✅ Defensive typing (typeof awInfo.body_modified === "boolean") for the undefined case on body_modified is handled carefully.
  • ✅ The aw_info_tmp_test.go integration test validates the compiler-to-env-var pipeline end-to-end.

Verdict

No blocking issues — the feature works for the expected case. The inline comments flag maintainability and test-coverage improvements worth addressing before this area grows further.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 6.8M

Comment thread actions/setup/js/send_otlp_span.cjs Outdated
const itemNumber = typeof awInfo.context?.item_number === "string" ? awInfo.context.item_number : "";
const triggerLabel = typeof awInfo.context?.trigger_label === "string" ? awInfo.context.trigger_label : "";
const commentId = typeof awInfo.context?.comment_id === "string" ? awInfo.context.comment_id : "";
const frontmatterSource = typeof awInfo.frontmatter_source === "string" ? awInfo.frontmatter_source : "";
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 6 lines extracting frontmatterSource, frontmatterHash, bodyModified from awInfo (lines 927–929 and 1335–1337) and the 3 attributes.push(...) calls (lines 997–999 and 1430–1432) are duplicated identically between sendJobSetupSpan and sendJobConclusionSpan.

Consider extracting a helper:

function buildFrontmatterAttributes(awInfo) {
  const attrs = [];
  const source = typeof awInfo.frontmatter_source === "string" ? awInfo.frontmatter_source : "";
  const hash = typeof awInfo.frontmatter_hash === "string" ? awInfo.frontmatter_hash : "";
  const modified = typeof awInfo.body_modified === "boolean" ? awInfo.body_modified : undefined;
  if (source) attrs.push(buildAttr("gh-aw.frontmatter.source", source));
  if (hash) attrs.push(buildAttr("gh-aw.frontmatter.hash", hash));
  if (typeof modified === "boolean") attrs.push(buildAttr("gh-aw.frontmatter.body_modified", modified));
  return attrs;
}

This is the classic deep-module opportunity from /tdd: a small interface (awInfo → attrs[]) with all the defensive-typing complexity hidden inside.

expect(awInfo.created_at).toBeTruthy();
});

it("should include frontmatter source/hash and body_modified when configured", async () => {
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 new test only verifies body_modified: true. The generate_aw_info.cjs code has a distinct code path for "false" and for invalid/absent values:

if (bodyModified === "true" || bodyModified === "false") {
  awInfo.body_modified = bodyModified === "true";
}

Consider adding tests for:

  1. GH_AW_INFO_BODY_MODIFIED = "false"awInfo.body_modified === false
  2. GH_AW_INFO_BODY_MODIFIED = "" (empty) → awInfo.body_modified is absent from the JSON
  3. GH_AW_INFO_FRONTMATTER_SOURCE = ""awInfo.frontmatter_source is absent

The negative/absence cases are what catch regressions when someone later changes the conditional.

Comment thread pkg/workflow/compiler_yaml.go Outdated
fmt.Fprintf(yaml, " GH_AW_INFO_AWMG_VERSION: \"%s\"\n", mcpGatewayVersion)
fmt.Fprintf(yaml, " GH_AW_INFO_FIREWALL_TYPE: \"%s\"\n", firewallType)
if data.Source != "" {
fmt.Fprintf(yaml, " GH_AW_INFO_FRONTMATTER_SOURCE: \"%s\"\n", data.Source)
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.

[/zoom-out] There's an asymmetry here: GH_AW_INFO_FRONTMATTER_SOURCE and GH_AW_INFO_BODY_MODIFIED are guarded by data.Source != "", but GH_AW_INFO_FRONTMATTER_HASH has its own independent guard (data.FrontmatterHash != ""). This means a workflow with a hash but no source would emit hash without source or body_modified.

Is that intentional? If source and hash always come from the same frontmatter provenance, it may be clearer and less surprising to emit them together (same guard), or at least document the independent-emission intent with a comment.

The test in aw_info_tmp_test.go checks GH_AW_INFO_FRONTMATTER_SOURCE and GH_AW_INFO_FRONTMATTER_HASH but doesn't test the hash-without-source scenario.

fmt.Fprintf(yaml, " GH_AW_INFO_FRONTMATTER_SOURCE: \"%s\"\n", data.Source)
// Body-modified defaults to false at compile time; update flows may override this
// signal when source/body drift is detected before execution.
yaml.WriteString(" GH_AW_INFO_BODY_MODIFIED: \"false\"\n")
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] GH_AW_INFO_BODY_MODIFIED is hardcoded to "false" at compile time with a comment that "update flows may override this signal". But there's no test — and no code visible in this PR — that exercises the override path.

If the override mechanism exists elsewhere, a cross-reference comment or link to that code would help future readers understand when the value actually becomes "true" at runtime. If the override hasn't been implemented yet, this is a [tracer-bullet]((mattpocock.com/redacted) placeholder that could silently produce misleading telemetry: body_modified: false on every span, even when the body was actually modified.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds frontmatter provenance/state metadata to agentic workflow OTEL telemetry, but the implementation is incomplete for setup spans and committed lockfiles.

Changes:

  • Adds frontmatter_source, frontmatter_hash, and body_modified to aw_info.json.
  • Emits the new fields as OTEL span attributes in setup/conclusion span code paths.
  • Updates tests and regenerates many workflow lockfiles, primarily adding wazero MCP cache cleanup steps.
Show a summary per file
File Description
pkg/workflow/compiler_yaml.go Adds frontmatter metadata env vars for generate_aw_info.
pkg/workflow/aw_info_tmp_test.go Extends aw_info generation test expectations.
actions/setup/js/generate_aw_info.cjs Persists frontmatter metadata into aw_info.json.
actions/setup/js/generate_aw_info.test.cjs Tests persisted frontmatter metadata.
actions/setup/js/send_otlp_span.cjs Emits frontmatter metadata as OTEL attributes.
actions/setup/js/send_otlp_span.test.cjs Tests setup/conclusion span attributes.
.github/workflows/*.lock.yml Regenerated workflow lockfiles, mostly adding wazero cache cleanup.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 232/232 changed files
  • Comments generated: 8

Comment on lines +829 to +833
if data.Source != "" {
fmt.Fprintf(yaml, " GH_AW_INFO_FRONTMATTER_SOURCE: \"%s\"\n", data.Source)
// Body-modified defaults to false at compile time; update flows may override this
// signal when source/body drift is detected before execution.
yaml.WriteString(" GH_AW_INFO_BODY_MODIFIED: \"false\"\n")
Comment thread pkg/workflow/compiler_yaml.go Outdated
fmt.Fprintf(yaml, " GH_AW_INFO_AWMG_VERSION: \"%s\"\n", mcpGatewayVersion)
fmt.Fprintf(yaml, " GH_AW_INFO_FIREWALL_TYPE: \"%s\"\n", firewallType)
if data.Source != "" {
fmt.Fprintf(yaml, " GH_AW_INFO_FRONTMATTER_SOURCE: \"%s\"\n", data.Source)
Comment thread .github/workflows/ci-doctor.lock.yml Outdated
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Remove wazero MCP cache
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Remove wazero MCP cache
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Remove wazero MCP cache
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Remove wazero MCP cache
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Remove wazero MCP cache
Comment thread .github/workflows/ace-editor.lock.yml Outdated
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Remove wazero MCP cache
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 85/100

Excellent test quality

Metric Value
New/modified tests analyzed 4
✅ Design tests (behavioral contracts) 4 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 2 (50%)
Duplicate test clusters 0
Test inflation detected No
🚨 Coding-guideline violations None

Test Classification Details

View All Test Classifications (4 tests)
Test File Classification Issues Detected
should include frontmatter source/hash and body_modified when configured actions/setup/js/generate_aw_info.test.cjs ✅ Design Happy-path only; no absent/empty-field case
includes frontmatter source/hash/body-modified metadata from aw_info.json on setup spans actions/setup/js/send_otlp_span.test.cjs ✅ Design Includes ENOENT fallback in spy; asserts observable OTLP output
emits frontmatter source/hash/body-modified attributes when present actions/setup/js/send_otlp_span.test.cjs ✅ Design Tests body_modified: false (boolean edge case)
TestAwInfoTmpPath (new assertions) pkg/workflow/aw_info_tmp_test.go ✅ Design Verifies compiled YAML env vars; happy-path only

Flagged Tests — Minor Suggestions

No tests fail the quality bar. The following are low-priority suggestions:

⚠️ Missing edge-case: absent/empty frontmatter fields (generate_aw_info.test.cjs)

Classification: Design test — passes
Suggestion: The new test verifies the happy path (all three env vars set). Consider adding a companion test for when GH_AW_INFO_FRONTMATTER_SOURCE and GH_AW_INFO_FRONTMATTER_HASH are empty strings (the beforeEach already clears them) — verifying the fields are absent or undefined in the output JSON would lock in the omit-when-empty contract.

⚠️ Missing edge-case: absent frontmatter fields in OTLP output (send_otlp_span.test.cjs)

Classification: Design test — passes
Suggestion: Both new OTLP tests verify the "fields present" path. A test asserting that the gh-aw.frontmatter.* attributes are not emitted when aw_info.json lacks those fields would catch future regressions where default-empty values leak into spans.


Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 1 test function modified — unit (//go:build !integration) ✅
  • 🟨 JavaScript (*.test.cjs): 3 new it() blocks (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All four new/modified tests verify observable behavioral contracts: env-var→JSON-file output, and JSON-file→OTEL-span attribute propagation. No coding-guideline violations detected.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §25879637779

🧪 Test quality analysis by Test Quality Sentinel · ● 7.5M ·

@github-actions
Copy link
Copy Markdown
Contributor

@copilot please refresh this dirty branch, rerun checks, and summarize any remaining blockers.

Generated by PR Sous Chef ·

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot mrerge main recompile review all comments abd reviews

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

@copilot mrerge main recompile review all comments abd reviews

Done in c796d94: merged latest main, recompiled lockfiles, and addressed the review items in telemetry/compiler code. I removed gh-aw.frontmatter.source, added setup-time fallback support for frontmatter hash/body-modified, and regenerated workflows so frontmatter metadata/env vars are present consistently.

@github-actions
Copy link
Copy Markdown
Contributor

pr-sous-chef: please confirm the refreshed branch is green and summarize any remaining blockers.

Generated by PR Sous Chef ·

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot

  • merge main
  • add again gh-aw.frontmatter.source
  • add frontmatter emoji

Copilot AI and others added 2 commits May 14, 2026 20:38
…ata-fields

# Conflicts:
#	.github/workflows/daily-cache-strategy-analyzer.lock.yml
#	.github/workflows/daily-fact.lock.yml
#	.github/workflows/daily-observability-report.lock.yml
#	.github/workflows/duplicate-code-detector.lock.yml
#	.github/workflows/grumpy-reviewer.lock.yml
#	.github/workflows/issue-arborist.lock.yml
#	.github/workflows/necromancer.lock.yml
#	.github/workflows/schema-feature-coverage.lock.yml
#	.github/workflows/smoke-call-workflow.lock.yml
#	.github/workflows/smoke-codex.lock.yml

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

@copilot

  • merge main...

Done in two commits:

  • a23d6fab08: merged latest main.
  • d6e1b7bec7: re-added gh-aw.frontmatter.source and added frontmatter emoji telemetry (gh-aw.frontmatter.emoji) across compiler env -> aw_info.json -> setup/conclusion OTEL spans, with tests updated.

@pelikhan pelikhan merged commit 8a733bf into main May 14, 2026
@pelikhan pelikhan deleted the copilot/update-otel-data-fields branch May 14, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants