Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/agent-performance-analyzer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/audit-workflows.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/code-scanning-fixer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-agent-analysis.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-cli-deep-research.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-pr-nlp-analysis.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-pr-prompt-analysis.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-session-insights.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-token-audit.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/copilot-token-optimizer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/daily-cli-performance.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/daily-code-metrics.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/daily-community-attribution.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/daily-news.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/daily-testify-uber-super-expert.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/deep-report.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/delight.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/developer-docs-consolidator.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/discussion-task-miner.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/firewall-escape.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/glossary-maintainer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/metrics-collector.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/pr-triage-agent.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/security-compliance.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/technical-doc-writer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/weekly-blog-post-writer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/workflow-health-manager.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 15 additions & 7 deletions pkg/workflow/repo_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -736,21 +736,29 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna
steps = append(steps, c.generateRestoreActionsSetupStep())
}

// Job condition: only run if the agent job succeeded (do not push repo memory when agent
// failed or was skipped). Using always() so the job still runs even when upstream jobs
// are skipped (e.g. detection is skipped when agent produces no outputs).
agentSucceeded := BuildEquals(
// Job condition: only run when the agent actually executed (not skipped) and
// the workflow was not cancelled. Using always() so the job still runs even
// when upstream jobs are skipped (e.g. detection is skipped when agent produces
// no outputs). We check != 'skipped' rather than == 'success' so that
// repo-memory is pushed even when the agent fails — partial memory data is
// still valuable. Adding !cancelled() prevents the job from running after
// workflow cancellation (similar to compiler_safe_outputs_job.go).
// Crucially, the != 'skipped' check prevents the job from running on no-op
// workflow invocations (e.g. bot comments) where pre_activation is skipped
// and the skip cascades through activation → agent → detection.
agentNotSkipped := BuildNotEquals(
BuildPropertyAccess(fmt.Sprintf("needs.%s.result", constants.AgentJobName)),
BuildStringLiteral("success"),
BuildStringLiteral("skipped"),
)
notCancelled := &NotNode{Child: BuildFunctionCall("cancelled")}
jobNeeds := []string{string(constants.AgentJobName), string(constants.ActivationJobName)}
var jobCondition string
if threatDetectionEnabled {
// When threat detection is enabled, also require detection passed (succeeded or skipped).
jobCondition = RenderCondition(BuildAnd(BuildAnd(BuildFunctionCall("always"), buildDetectionPassedCondition()), agentSucceeded))
jobCondition = RenderCondition(BuildAnd(BuildAnd(BuildAnd(BuildFunctionCall("always"), notCancelled), buildDetectionPassedCondition()), agentNotSkipped))
jobNeeds = append(jobNeeds, string(constants.DetectionJobName))
} else {
jobCondition = RenderCondition(BuildAnd(BuildFunctionCall("always"), agentSucceeded))
jobCondition = RenderCondition(BuildAnd(BuildAnd(BuildFunctionCall("always"), notCancelled), agentNotSkipped))
}

// Build outputs map for validation failures from all memory steps
Expand Down
45 changes: 45 additions & 0 deletions pkg/workflow/repo_memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1281,3 +1281,48 @@ func TestPushRepoMemoryJobConcurrencyKey(t *testing.T) {
assert.NotContains(t, pushJob.Concurrency, "push-repo-memory-${{ github.repository }}\"",
"Concurrency key should not be the old repo-wide-only key format")
}

// TestPushRepoMemoryJobConditionGatesOnAgentNotSkipped verifies that the push_repo_memory
// job condition uses needs.agent.result != 'skipped' and !cancelled() so the job does not
// run on no-op workflow invocations (e.g. bot comments where pre_activation is skipped)
// or after workflow cancellation.
func TestPushRepoMemoryJobConditionGatesOnAgentNotSkipped(t *testing.T) {
data := &WorkflowData{
RepoMemoryConfig: &RepoMemoryConfig{
Memories: []RepoMemoryEntry{
{ID: "default", BranchName: "memory/my-workflow"},
},
},
}

compiler := NewCompiler()

t.Run("without threat detection", func(t *testing.T) {
pushJob, err := compiler.buildPushRepoMemoryJob(data, false)
require.NoError(t, err, "Should build push job without error")
require.NotNil(t, pushJob, "Should produce a push job")

assert.Equal(t,
"always() && (!cancelled()) && needs.agent.result != 'skipped'",
pushJob.If,
"Condition should use always() && (!cancelled()) && agent != skipped",
)
})

t.Run("with threat detection", func(t *testing.T) {
pushJob, err := compiler.buildPushRepoMemoryJob(data, true)
require.NoError(t, err, "Should build push job without error")
require.NotNil(t, pushJob, "Should produce a push job")

assert.Contains(t, pushJob.If, "always()",
"Condition should contain always()")
assert.Contains(t, pushJob.If, "!cancelled()",
"Condition should contain !cancelled() to prevent running after cancellation")
assert.Contains(t, pushJob.If, "needs.agent.result != 'skipped'",
"Condition should check agent result != 'skipped'")
assert.Contains(t, pushJob.If, "needs.detection.result",
"Condition should still check detection result when threat detection is enabled")
assert.NotContains(t, pushJob.If, "needs.agent.result == 'success'",
"Condition should NOT use == 'success' for agent check")
})
}