Skip to content

fix(compiler): auto-add pre_activation to safe_outputs/conclusion needs when messages reference its outputs#43570

Merged
pelikhan merged 4 commits into
mainfrom
copilot/static-analysis-report-2026-07-05
Jul 5, 2026
Merged

fix(compiler): auto-add pre_activation to safe_outputs/conclusion needs when messages reference its outputs#43570
pelikhan merged 4 commits into
mainfrom
copilot/static-analysis-report-2026-07-05

Conversation

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

skillet.lock.yml produced three actionlint expression errors because safe_outputs and conclusion jobs embedded ${{ needs.pre_activation.outputs.skill_name }} in GH_AW_SAFE_OUTPUT_MESSAGES without declaring pre_activation in their needs.

The compiler serializes safe-outputs.messages templates verbatim into every job that embeds that env var, but the dependency wiring was never propagated for pre_activation (which is explicitly excluded from the normal auto-wiring paths for both jobs).

Changes

  • compiler_safe_outputs_job.go — adds messagesContainPreActivationRef(*SafeOutputMessagesConfig) bool helper that scans all message template fields for needs.pre_activation.outputs. references; used to conditionally append pre_activation to the safe_outputs job's needs list after explicit needs are resolved
  • notify_comment.go — same guard applied in buildConclusionJob after buildConclusionJobNeeds; uses slices.Contains for dedup
  • skillet.lock.yml — recompiled; pre_activation now appears in the needs of both safe_outputs and conclusion

The fix is general: any workflow whose safe-outputs.messages templates reference needs.pre_activation.outputs.* will automatically get the dependency wired, without requiring changes to the source markdown.


Generated by 👨‍🍳 PR Sous Chef · 6.18 AIC · ⌖ 6.22 AIC · ⊞ 3.7K ·
Comment /souschef to run again

Copilot AI linked an issue Jul 5, 2026 that may be closed by this pull request
6 tasks
…n messages reference it

Fixes actionlint errors in skillet.lock.yml at lines 1385, 1411, 1882.
The safe_outputs and conclusion jobs referenced needs.pre_activation.outputs.*
in GH_AW_SAFE_OUTPUT_MESSAGES but did not declare pre_activation in their needs.

- Add messagesContainPreActivationRef() helper in compiler_safe_outputs_job.go
- Auto-add pre_activation to safe_outputs job needs when messages reference it
- Auto-add pre_activation to conclusion job needs when messages reference it
- Recompile all workflows (skillet.lock.yml now correct)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Update static analysis report for 2026-07-05 fix(compiler): auto-add pre_activation to safe_outputs/conclusion needs when messages reference its outputs Jul 5, 2026
Copilot AI requested a review from pelikhan July 5, 2026 15:52
@pelikhan pelikhan marked this pull request as ready for review July 5, 2026 15:58
Copilot AI review requested due to automatic review settings July 5, 2026 15:58
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #43570 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (60 additions across 3 files).

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No test files were added or modified in this PR. Test Quality Sentinel skipped.

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

Fixes a workflow compilation gap where safe_outputs and conclusion jobs could embed ${{ needs.pre_activation.outputs.* }} inside GH_AW_SAFE_OUTPUT_MESSAGES without declaring pre_activation in needs, causing actionlint expression errors in generated lock YAML.

Changes:

  • Added a helper to detect needs.pre_activation.outputs.* references in safe-outputs.messages templates and conditionally wire pre_activation into job needs.
  • Applied this conditional dependency wiring to both the safe_outputs job and the conclusion job (with deduping for conclusion).
  • Recompiled skillet.lock.yml to reflect the corrected needs lists.
Show a summary per file
File Description
pkg/workflow/compiler_safe_outputs_job.go Detects needs.pre_activation.outputs.* in message templates and auto-adds pre_activation to safe_outputs job needs.
pkg/workflow/notify_comment.go Applies the same conditional needs wiring to the conclusion job, with slices.Contains dedup.
.github/workflows/skillet.lock.yml Regenerated lock output showing pre_activation added to safe_outputs and conclusion job needs.

Review details

Tip

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

  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment thread pkg/workflow/compiler_safe_outputs_job.go
Comment thread pkg/workflow/notify_comment.go
@github-actions github-actions Bot mentioned this pull request Jul 5, 2026

@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, /tdd, and /codebase-design — requesting changes on two maintainability risks.

📋 Key Themes & Highlights

Key Themes

  • Missing regression protection: messagesContainPreActivationRef — the heart of the fix — has no unit tests. A future contributor adding a field to SafeOutputMessagesConfig won't know to update the scanned list, reintroducing this class of bug silently.
  • Manually maintained field list: The slice of 16 field values in the helper is a structural copy of the struct. Using reflection in a coverage test (or in the helper itself) would make omissions a compile/test failure rather than a runtime surprise.
  • Duplicated guard pattern with inconsistent dedup primitive: The 9-line block (nil check → jobManager check → dedup → append) appears verbatim in two files, but uses setutil.Contains in one place and slices.Contains in the other. Extracting appendPreActivationNeedIfRequired would remove the duplication and standardise the primitive.

Positive Highlights

  • ✅ Root cause properly diagnosed: the serializer embeds message templates verbatim, so the dependency must be wired at compile time.
  • ✅ The fix is general and automatic — no per-workflow markdown changes needed.
  • jobManager.GetJob guard is correctly placed to avoid wiring the dep when the job doesn't exist.
  • ✅ Both affected jobs (safe_outputs and conclusion) are covered.
  • ✅ The lock file recompilation confirms end-to-end correctness.

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

Comment thread pkg/workflow/compiler_safe_outputs_job.go
Comment thread pkg/workflow/compiler_safe_outputs_job.go
needs := buildConclusionJobNeeds(data, mainJobName, safeOutputJobNames)
// If any message template references needs.pre_activation.outputs.*, add pre_activation
// as a dependency so that GitHub Actions can resolve the expression at runtime.
if data.SafeOutputs != nil && messagesContainPreActivationRef(data.SafeOutputs.Messages) {

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.

[/codebase-design] The guard block here is structurally identical to the one in compiler_safe_outputs_job.go (lines 639–647). The same pattern — check nil, check jobManager, dedup-append — is now duplicated across two files with slightly different dedup primitives (setutil.Contains vs slices.Contains). Consider extracting a shared helper or at minimum aligning the two call sites on the same dedup primitive to reduce cognitive load.

💡 Suggested refactor direction

A helper like:

// appendPreActivationNeedIfRequired appends pre_activation to needs if:
//   1. any message template references needs.pre_activation.outputs.*
//   2. the pre_activation job actually exists in the job manager
// It is a no-op if pre_activation is already present.
func (c *Compiler) appendPreActivationNeedIfRequired(
    data *WorkflowData, needs []string,
) []string {
    if data.SafeOutputs == nil || !messagesContainPreActivationRef(data.SafeOutputs.Messages) {
        return needs
    }
    name := string(constants.PreActivationJobName)
    if _, exists := c.jobManager.GetJob(name); !exists {
        return needs
    }
    if slices.Contains(needs, name) {
        return needs
    }
    return append(needs, name)
}

Both call sites collapse to a single line and the dedup primitive is consistent.

@copilot please address this.

@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

The fix is correct and addresses the root cause: safe_outputs and conclusion jobs embedding needs.pre_activation.outputs.* expressions without declaring the dependency.

One non-blocking concern: messagesContainPreActivationRef enumerates all string fields of SafeOutputMessagesConfig by hand. Currently the only omission is AppendOnlyComments (a bool field, which cannot contain such a reference — fine). However, if a new string field is added to the struct in the future, this function will silently miss it — the compiler will not warn and no existing test will fail. Using reflect to iterate string-typed fields, or adding a canary test that asserts the hardcoded field count matches the struct's actual string-field count, would keep the scan complete automatically.

Everything else looks good: the seenNeeds dedup in compiler_safe_outputs_job.go is consistent with existing patterns, slices.Contains in notify_comment.go is the right choice for a plain slice, the jobManager.GetJob guard prevents wiring when pre_activation was never created, and the recompiled skillet.lock.yml diff confirms the intended effect.

Checked
  • SafeOutputMessagesConfig fields vs scanned fields: only AppendOnlyComments (bool) absent — expected
  • Dedup logic in both jobs: correct
  • buildConclusionJobNeeds does not already insert pre_activation — new code is the only insertion point
  • No existing test covers the new code path (inline comment filed)

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 88.1 AIC · ⌖ 6.47 AIC · ⊞ 4.9K

Comment thread pkg/workflow/compiler_safe_outputs_job.go

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

Verdict: request changes — two correctness bugs and missing test coverage

The logic is sound for the happy path (skillet.lock.yml confirms it compiles correctly when pre_activation exists), but there are two silent-failure bugs and a structural fragility that will cause problems as the codebase evolves.

Blocking issues (2)

1. Silent no-op when pre_activation job is absent (both paths)
Both buildSafeOutputsJobFromParts and buildConclusionJob silently skip adding the needs edge when GetJob(PreActivationJobName) returns false, with no error. A workflow that has needs.pre_activation.outputs.skill_name in a footer template but no pre_activation job will compile without error, emit a valid-looking YAML, and then produce empty/broken message bodies at runtime. The fix is to return a fmt.Errorf in both paths when the job is absent — the compiler already propagates errors from both call sites.

2. No unit tests for the new correctness-critical function
messagesContainPreActivationRef has zero test coverage despite gating the needs-edge generation. The pre_activation injection in buildConclusionJob also has no tests. Correctness regressions here produce silently broken compiled workflows.

Non-blocking issues (2)

3. Field enumeration will silently miss future struct fields
The hard-coded string-field slice in messagesContainPreActivationRef will silently skip any new fields added to SafeOutputMessagesConfig. A reflection-based exhaustiveness test (or using reflect.Value iteration in the function itself) would catch this at test time.

4. Inconsistent deduplication between the two paths
safe_outputs uses seenNeeds map[string]struct{}, conclusion uses slices.Contains. Both work, but the inconsistency is a maintenance hazard.

🔎 Code quality review by PR Code Quality Reviewer · 178.9 AIC · ⌖ 8.38 AIC · ⊞ 5.4K
Comment /review to run again

Comment thread pkg/workflow/compiler_safe_outputs_job.go
// If any message template references needs.pre_activation.outputs.*, add pre_activation
// as a dependency so that GitHub Actions can resolve the expression at runtime.
if data.SafeOutputs != nil && messagesContainPreActivationRef(data.SafeOutputs.Messages) {
if _, exists := c.jobManager.GetJob(string(constants.PreActivationJobName)); exists {

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.

Same silent failure in conclusion job path: when GetJob(PreActivationJobName) returns false, the conclusion job is emitted with unresolvable needs.pre_activation.outputs.* template expressions and no error. This is the same bug as the safe_outputs path above.

💡 Suggested fix

Mirror the fix from the safe_outputs path — return an error instead of a no-op:

if data.SafeOutputs != nil && messagesContainPreActivationRef(data.SafeOutputs.Messages) {
	if _, exists := c.jobManager.GetJob(string(constants.PreActivationJobName)); !exists {
		return nil, fmt.Errorf(
			"safe-outputs messages reference %s outputs but no %s job exists",
			constants.PreActivationJobName, constants.PreActivationJobName,
		)
	}
	preActName := string(constants.PreActivationJobName)
	if !slices.Contains(needs, preActName) {
		needs = append(needs, preActName)
	}
}

Note: the conclusion job already returns (*Job, error), so the error return is already wired up.

Comment thread pkg/workflow/compiler_safe_outputs_job.go
Comment thread pkg/workflow/compiler_safe_outputs_job.go
if data.SafeOutputs != nil && messagesContainPreActivationRef(data.SafeOutputs.Messages) {
if _, exists := c.jobManager.GetJob(string(constants.PreActivationJobName)); exists {
preActName := string(constants.PreActivationJobName)
if !slices.Contains(needs, preActName) {

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.

Inconsistent deduplication strategy: the safe_outputs path uses an O(1) seenNeeds map[string]struct{}, while this conclusion-job path uses slices.Contains (O(n) linear scan). Both are correct for current list sizes, but the inconsistency is a maintenance hazard — contributors will copy one pattern without knowing there are two, and future code audits will waste time questioning whether the difference is intentional.

💡 Suggested fix

Either:

  1. Extract a shared addUniqueNeed(needs *[]string, seen map[string]struct{}, name string) helper that both call-sites use, or
  2. Build a local seenNeeds map in buildConclusionJob using the same approach as buildSafeOutputsJobFromParts.

The map-based approach is also naturally extensible if the needs list ever grows large (e.g. multi-job workflows with many safe-output dependencies).

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, review the latest feedback, and rerun checks once the branch is ready.

Generated by 👨‍🍳 PR Sous Chef · 5.29 AIC · ⌖ 5.03 AIC · ⊞ 7.1K ·
Comment /souschef to run again

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

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, review the latest feedback, and rerun checks once the branch is ready.

...

Done. Added TestBuildConsolidatedSafeOutputsJobNeedsPreActivationFromMessages in compiler_safe_outputs_job_test.go and TestConclusionJobNeedsPreActivationFromMessages in notify_comment_test.go — each covering the positive cases (Footer/RunSuccess/RunFailure referencing needs.pre_activation.outputs.*), the no-reference case, the unregistered-job guard, and the nil-messages guard. All pass locally. The one pre-existing failure (TestPRSousChefWorkflowAddCommentTargetContract) pre-dates these changes and is unrelated. CI needs a human re-trigger to verify on the new HEAD.

Copilot AI requested a review from gh-aw-bot July 5, 2026 16:50
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the skill, address unresolved review comments, and rerun checks once the branch is up to date.

Unresolved review feedback to address:

  • github-actions: requested changes

Generated by 👨‍🍳 PR Sous Chef · 6.18 AIC · ⌖ 6.22 AIC · ⊞ 3.7K ·
Comment /souschef to run again

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the skill, address unresolved review comments, and rerun checks once the branch is up to date.

Unresolved review feedback to address:...

The two review threads are already addressed — TestBuildConsolidatedSafeOutputsJobNeedsPreActivationFromMessages and TestConclusionJobNeedsPreActivationFromMessages were added in commit 18144ee. The branch has since been merged with main (commit 6742f3c); all tests still pass locally after the merge. No further code changes are needed — CI re-trigger is the remaining step.

@pelikhan pelikhan merged commit 5641e3a into main Jul 5, 2026
30 checks passed
@pelikhan pelikhan deleted the copilot/static-analysis-report-2026-07-05 branch July 5, 2026 18:19
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.

[static-analysis] Report - 2026-07-05

4 participants