Skip to content

Migrate deprecated needs.activation.outputs references to steps.sanitized.outputs - #47264

Merged
pelikhan merged 4 commits into
mainfrom
copilot/hippo-migrate-deprecated-references
Jul 22, 2026
Merged

Migrate deprecated needs.activation.outputs references to steps.sanitized.outputs#47264
pelikhan merged 4 commits into
mainfrom
copilot/hippo-migrate-deprecated-references

Conversation

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

The deprecated needs.activation.outputs.{text,title,body} syntax accumulated silently: the compiler auto-rewrote it but detectTextOutputUsage only scanned for the modern form, meaning workflows using the old syntax could compile without the sanitized step when the event trigger didn't provide content context (e.g. workflow_dispatch-only triggers).

Changes

Bug fix

  • compiler_orchestrator_tools.godetectTextOutputUsage now also matches ${{ needs.activation.outputs.{text,title,body} }} so the sanitized step is always included for workflows not yet migrated

Tests

  • compute_text_lazy_test.go — three new TestDetectTextOutputUsage cases covering the deprecated form
  • expression_extraction_test.go — new TestExpressionExtractor_DeprecatedActivationOutputWarning verifies the deprecation warning is emitted to stderr and the correct transformed mapping is produced; updated GenerateEnvVarName and NoCollisions tests to use the modern form (the deprecated form never reaches generateEnvVarName in the live pipeline)
  • runtime_import.test.cjs — two tests updated to use steps.sanitized.outputs.text — compiled templates never contain the deprecated form at runtime, so the previous test inputs were unreachable in practice:
    // Before (unreachable — compiler transforms before runtime)
    "{{#if needs.activation.outputs.text}}body{{/if}}"    "{{#if }}body{{/if}}"
    "{{#if ${{ needs.activation.outputs.text }} }}"       "__GH_AW_NEEDS_ACTIVATION_OUTPUTS_TEXT__"
    
    // After (matches actual compiled output)
    "{{#if steps.sanitized.outputs.text}}body{{/if}}"     "{{#if }}body{{/if}}"
    "{{#if ${{ steps.sanitized.outputs.text }} }}"        "__GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT__"
  • expression_extraction_fuzz_test.go — modern-form seeds added to both fuzz targets; deprecated seeds retained and annotated as backwards-compatibility transformer coverage

…ized.outputs

- Fix detectTextOutputUsage() to also detect deprecated needs.activation.outputs.{text,title,body}
  so workflows not yet migrated still compile correctly when event context alone is insufficient
- Add TestDetectTextOutputUsage cases for deprecated form
- Add TestExpressionExtractor_DeprecatedActivationOutputWarning to verify the
  deprecation warning is emitted (and the correct transformed mapping produced)
- Update TestExpressionExtractor_GenerateEnvVarName to use the modern
  steps.sanitized.outputs.text form (matches what the pipeline actually passes)
- Update TestExpressionExtractor_NoCollisions to use steps.sanitized.outputs.text
- Update runtime_import.test.cjs tests for wrapExpressionsInTemplateConditionals
  and extractAndReplacePlaceholders to use modern form (compiled templates never
  contain the deprecated form at runtime)
- Add modern-form fuzz seeds alongside deprecated ones and clarify the purpose
  of the deprecated seeds in both FuzzRenderExpressions and FuzzExtractExpressions

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Migrate deprecated needs.activation.outputs references Migrate deprecated needs.activation.outputs references to steps.sanitized.outputs Jul 22, 2026
Copilot AI requested a review from pelikhan July 22, 2026 11:17
@pelikhan
pelikhan marked this pull request as ready for review July 22, 2026 11:26
Copilot AI review requested due to automatic review settings July 22, 2026 11:26
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ failed during design decision gate check.

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.

Pull request overview

Adds backward-compatible detection for deprecated activation outputs so sanitized text steps are included when required.

Changes:

  • Detects deprecated text, title, and body output references.
  • Updates Go, fuzz, and JavaScript tests to modern output syntax.
  • Adds deprecation-warning coverage.
Show a summary per file
File Description
pkg/workflow/compiler_orchestrator_tools.go Extends sanitized-output detection.
pkg/workflow/compute_text_lazy_test.go Tests deprecated output detection.
pkg/workflow/expression_extraction_test.go Updates mappings and tests warnings.
pkg/workflow/expression_extraction_fuzz_test.go Clarifies compatibility seeds.
actions/setup/js/runtime_import.test.cjs Uses modern runtime expressions.

Review details

Tip

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

  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Medium

// Also recognise the deprecated needs.activation.outputs.* forms so that workflows
// using the old syntax still get the sanitized step included during compilation.
if !hasTextUsage {
hasTextUsage = strings.Contains(markdownContent, "${{ needs.activation.outputs.text }}")
Comment on lines +548 to +564
stderr := captureStderr(func() {
mappings, err := extractor.ExtractExpressions(tt.markdown)
if err != nil {
t.Fatalf("ExtractExpressions() unexpected error: %v", err)
}
// Verify the mapping was produced with the modern expression.
found := false
for _, m := range mappings {
if m.Content == tt.modern {
found = true
break
}
}
if !found {
t.Errorf("expected mapping for %q but not found", tt.modern)
}
})

@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 /diagnosing-bugs — changes look correct and well-tested.

📋 Key Themes & Highlights

Positive Highlights

  • ✅ Root cause addressed: detectTextOutputUsage now covers both modern and deprecated expression forms
  • ✅ Three focused regression tests added to compute_text_lazy_test.go
  • TestExpressionExtractor_DeprecatedActivationOutputWarning verifies the deprecation warning path end-to-end
  • ✅ Fuzz seeds annotated clearly to distinguish backwards-compat coverage from modern-form coverage
  • ✅ JS runtime tests updated to reflect actual compiled output (deprecated form is never present at runtime)

One Minor Observation (non-blocking)

Both the modern-form and new deprecated-form detections use exact string match (strings.Contains), so expressions with fallbacks like ${{ needs.activation.outputs.text || 'default' }} would not be detected. This is consistent with the existing behaviour for the modern form and is acceptable for now, but worth a follow-up if fallback syntax is common in the wild.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 20.1 AIC · ⌖ 4.56 AIC · ⊞ 6.7K
Comment /matt to run again

@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.

The changes are correct and well-structured.

  • Bug fix in detectTextOutputUsage is sound: the fallback checks for needs.activation.outputs.{text,title,body} are guarded with !hasXUsage so the modern form takes precedence.
  • Deprecation warning path confirmed: expression_extraction.go already emits a warning to stderr for deprecated expressions, so the new TestExpressionExtractor_DeprecatedActivationOutputWarning test exercises real code.
  • captureStderr is defined in the same workflow package so accessible from expression_extraction_test.go.
  • Runtime test updates correctly reflect that compiled templates only contain steps.sanitized.outputs.* at runtime.
  • No security, correctness, or reliability issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 24.2 AIC · ⌖ 5.15 AIC · ⊞ 5K

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 85/100 — Excellent

Analyzed 15 test scenario(s): 13 design, 2 implementation, 0 violation(s).

📊 Metrics (15 tests)
Metric Value
Analyzed 15 (Go: 13, JS: 2)
✅ Design 13 (87%)
⚠️ Implementation 2 (13%)
Edge/error coverage 10 (67%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
TestExpressionExtractor_ExtractExpressions (table: needs.* migration, compound ||, paren groups) expression_extraction_test.go design_test None
TestSubExpressionEnvVars expression_extraction_test.go design_test None
TestMarshalImportInputValue (table-driven) expression_extraction_test.go implementation_test Happy-path serialization only
FuzzExtractTerminalSubExpressions (seed corpus additions) expression_extraction_fuzz_test.go design_test None
TestComputeTextLazyInsertion sub-tests compute_text_lazy_test.go design_test None
JS neutralizeSystemTags tests runtime_import.test.cjs implementation_test Reference-only change

Verdict

Passed. 13% implementation tests (threshold: 30%). No guideline violations. All Go test files have correct //go:build !integration build tags.

Key Observations:

  • New table-driven cases in TestExpressionExtractor_ExtractExpressions directly verify the PR behavioral contract: needs.activation.outputs.textsteps.sanitized.outputs.text, and compound || expressions generate correct sub-expression env vars.
  • TestComputeTextLazyInsertion verifies end-to-end compilation output contains the sanitized step ID, providing meaningful behavioral coverage.
  • Fuzz seed corpus additions exercise parenthesized compound expressions and edge cases (empty string, mismatched parens), strengthening robustness coverage.
  • JS changes are pure reference updates (5 lines in/out) — no net new test logic added.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 38 AIC · ⌖ 7.76 AIC · ⊞ 7K ·
Comment /review to run again

@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.

✅ Test Quality Sentinel: 85/100. 13% implementation tests (threshold: 30%). No guideline violations.

@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.

Non-blocking observations on the deprecated-syntax backwards-compatibility fix.

### Findings

1. TestExpressionExtractor_DeprecatedActivationOutputWarning misses EnvVar assertion (medium)

The new test verifies m.Content == tt.modern but never checks m.EnvVar. A regression where the transformer rewrites Content correctly but keeps the old env-var name (GH_AW_NEEDS_ACTIVATION_OUTPUTS_TEXT) would pass undetected — and that bug would cause the compiled workflow to reference an unset env var at runtime.

2. JS test removal leaves deprecated-form behaviour at the JS layer undocumented (low)

The deleted tests were the only documentation that the JS runtime does not understand the deprecated form. The extractAndReplacePlaceholders function produces __GH_AW_NEEDS_ACTIVATION_OUTPUTS_TEXT__ for any deprecated expression it receives — an env var that is never set. The compiler is the intended gatekeeper, but there is no test or comment anchoring that assumption at the JS layer.

What's good

  • The detectTextOutputUsage fix is correct and the three new compute_text_lazy_test.go cases directly cover the bug.
  • transformActivationOutputs and the deprecation-warning logic in expression_extraction.go pre-exist and work correctly — the new test confirms their behaviour.
  • Fuzz seeds for the deprecated form are retained, which is the right call.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 74.6 AIC · ⌖ 4.85 AIC · ⊞ 5.7K
Comment /review to run again

// Verify the mapping was produced with the modern expression.
found := false
for _, m := range mappings {
if m.Content == tt.modern {

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.

Missing EnvVar assertion leaves deprecated-path env-var name generation untested. If the transformation maps needs.activation.outputs.text to the correct Content but produces the wrong EnvVar (e.g., still emitting GH_AW_NEEDS_ACTIVATION_OUTPUTS_TEXT instead of GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT), this test would still pass.

💡 Suggested fix

Add an EnvVar assertion inside the found-mapping check to close this gap:

for _, m := range mappings {
    if m.Content == tt.modern {
        found = true
        // Also verify the env-var name reflects the modern (transformed) form,
        // not the deprecated form. A buggy transformer could rewrite Content
        // correctly but leave the old env-var name, causing a runtime miss.
        wantEnvVar := "GH_AW_" + strings.ToUpper(
            strings.NewReplacer(".", "_").Replace(tt.modern))
        if m.EnvVar != wantEnvVar {
            t.Errorf("expected EnvVar %q, got %q", wantEnvVar, m.EnvVar)
        }
        break
    }
}

it("should produce {{#if }} (falsy) for needs.activation.outputs.text when env var is absent", () => {
expect(wrapExpressionsInTemplateConditionals("{{#if needs.activation.outputs.text}}body{{/if}}")).toBe("{{#if }}body{{/if}}");
it("should produce {{#if }} (falsy) for steps.sanitized.outputs.text when env var is absent", () => {
expect(wrapExpressionsInTemplateConditionals("{{#if steps.sanitized.outputs.text}}body{{/if}}")).toBe("{{#if }}body{{/if}}");

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.

Removing the deprecated-form JS test creates an undocumented silent failure path. The PR's claim that "compiled templates never contain the deprecated form at runtime" is correct for newly compiled workflows, but extractAndReplacePlaceholders does mechanical dot→underscore conversion with no knowledge of the Go-side transformation — so the old placeholder __GH_AW_NEEDS_ACTIVATION_OUTPUTS_TEXT__ can still be generated in edge cases, and that env var is never set.

💡 Details

If a workflow YAML reaches the JS runtime with the deprecated expression still present (cached artifact, test fixture, direct compiler bypass), extractAndReplacePlaceholders produces __GH_AW_NEEDS_ACTIVATION_OUTPUTS_TEXT__ — an env var that is never populated — causing silently empty output.

Consider either:

  1. Retaining the old test cases as comments that document the intentional behavior gap, or
  2. Adding a dedicated test asserting what happens when the deprecated form hits the JS layer, with a comment that the compiler is the single source of truth for preventing this.

@pelikhan
pelikhan merged commit e01a194 into main Jul 22, 2026
31 checks passed
@pelikhan
pelikhan deleted the copilot/hippo-migrate-deprecated-references branch July 22, 2026 12:37
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.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.

[hippo] Migrate deprecated needs.activation.outputs text/title/body references

3 participants