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
2 changes: 1 addition & 1 deletion .github/aw/github-agentic-workflows.md

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

26 changes: 13 additions & 13 deletions .github/workflows/smoke-claude.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 docs/src/content/docs/reference/frontmatter-full.md
Original file line number Diff line number Diff line change
Expand Up @@ -1469,8 +1469,8 @@ pre-steps:
pre-steps: []
# Array items: undefined

# Custom workflow steps to run immediately before AI execution, after all
# initialization and setup steps in the agent job.
# Custom workflow steps to run before MCP gateway startup in the agent job,
# so prerequisite MCP installation/configuration can happen first.
# (optional)
# This field supports multiple formats (oneOf):

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ Custom steps run outside the firewall sandbox. These steps execute with standard

## Pre-Agent Steps (`pre-agent-steps:`)

Add custom steps immediately before the agent execution step, after all initialization/setup logic in the agent job.
Add custom steps before MCP gateway startup in the agent job so prerequisite MCP installation/configuration can happen first.

```yaml wrap
pre-agent-steps:
Expand Down
26 changes: 15 additions & 11 deletions pkg/workflow/compiler_pre_agent_steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ Test pre-agent-steps.
t.Error("Expected pre-agent-step to be in generated workflow")
}

cleanGitCredsIndex := indexInNonCommentLines(lockContent, "- name: Clean git credentials")
startMCPGatewayIndex := indexInNonCommentLines(lockContent, "- name: Start MCP Gateway")
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.

Good test update: changing the assertion from cleanGitCredsIndex to startMCPGatewayIndex accurately reflects the new ordering invariant — pre-agent-steps must precede MCP gateway startup. The naming is clear and aligns well with the PR's intent.

preAgentStepIndex := indexInNonCommentLines(lockContent, "- name: Finalize prompt context")
aiStepIndex := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI")
if cleanGitCredsIndex == -1 || preAgentStepIndex == -1 || aiStepIndex == -1 {
if startMCPGatewayIndex == -1 || preAgentStepIndex == -1 || aiStepIndex == -1 {
t.Fatal("Could not find expected steps in generated workflow")
}
if preAgentStepIndex <= cleanGitCredsIndex {
t.Errorf("Pre-agent-step (%d) should appear after clean git credentials (%d)", preAgentStepIndex, cleanGitCredsIndex)
if preAgentStepIndex >= startMCPGatewayIndex {
t.Errorf("Pre-agent-step (%d) should appear before Start MCP Gateway (%d)", preAgentStepIndex, startMCPGatewayIndex)
}
if preAgentStepIndex >= aiStepIndex {
t.Errorf("Pre-agent-step (%d) should appear before AI execution step (%d)", preAgentStepIndex, aiStepIndex)
Expand Down Expand Up @@ -113,13 +113,17 @@ Main workflow.

importedIdx := indexInNonCommentLines(lockContent, "- name: Imported pre-agent step")
mainIdx := indexInNonCommentLines(lockContent, "- name: Main pre-agent step")
startMCPGatewayIdx := indexInNonCommentLines(lockContent, "- name: Start MCP Gateway")
aiStepIdx := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI")
if importedIdx == -1 || mainIdx == -1 || aiStepIdx == -1 {
t.Fatal("Could not find expected pre-agent and AI steps in generated workflow")
if importedIdx == -1 || mainIdx == -1 || startMCPGatewayIdx == -1 || aiStepIdx == -1 {
t.Fatal("Could not find expected pre-agent, MCP gateway, and AI steps in generated workflow")
}
if importedIdx >= mainIdx {
t.Errorf("Imported pre-agent-step (%d) should appear before main pre-agent-step (%d)", importedIdx, mainIdx)
}
if mainIdx >= startMCPGatewayIdx {
t.Errorf("Main pre-agent-step (%d) should appear before Start MCP Gateway (%d)", mainIdx, startMCPGatewayIdx)
}
if mainIdx >= aiStepIdx {
t.Errorf("Main pre-agent-step (%d) should appear before AI execution step (%d)", mainIdx, aiStepIdx)
}
Expand Down Expand Up @@ -179,14 +183,14 @@ Main workflow.
}
lockContent := string(content)

restoreBaseIdx := indexInNonCommentLines(lockContent, "- name: Restore agent config folders from base branch")
restoreAPMIdx := indexInNonCommentLines(lockContent, "- name: Restore APM packages")
startMCPGatewayIdx := indexInNonCommentLines(lockContent, "- name: Start MCP Gateway")
aiStepIdx := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI")
if restoreBaseIdx == -1 || restoreAPMIdx == -1 || aiStepIdx == -1 {
t.Fatal("Could not find expected PR restore, pre-agent, and AI steps in generated workflow")
if restoreAPMIdx == -1 || startMCPGatewayIdx == -1 || aiStepIdx == -1 {
t.Fatal("Could not find expected pre-agent, MCP gateway, and AI steps in generated workflow")
}
if restoreBaseIdx >= restoreAPMIdx {
t.Errorf("PR base restore step (%d) should appear before imported pre-agent step (%d)", restoreBaseIdx, restoreAPMIdx)
if restoreAPMIdx >= startMCPGatewayIdx {
t.Errorf("Imported pre-agent step (%d) should appear before Start MCP Gateway (%d)", restoreAPMIdx, startMCPGatewayIdx)
}
if restoreAPMIdx >= aiStepIdx {
t.Errorf("Imported pre-agent step (%d) should appear before AI execution step (%d)", restoreAPMIdx, aiStepIdx)
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 test TestImportedPreAgentStepsRunAfterPRBaseRestore now checks ordering relative to Start MCP Gateway rather than the base-restore step. The test name no longer matches its assertions — consider renaming it to TestImportedPreAgentStepsRunBeforeMCPGateway to avoid confusion.

Expand Down
7 changes: 4 additions & 3 deletions pkg/workflow/compiler_yaml_main_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
// to avoid double-filtering: the gateway uses the same guard policy for the agent phase.
c.generateStopDIFCProxyStep(yaml, data)

// Add pre-agent-steps (if any) before MCP setup so they can install/configure MCP dependencies
// that the gateway may reference when it starts.
c.generatePreAgentSteps(yaml, data)
Comment on lines +305 to +307
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

pre-agent-steps are now emitted before generateMCPSetup, but the PR base-branch restoration step ("Restore agent config folders from base branch") is still generated later in this function. In pull_request workflows this means pre-agent steps can run while PR-injected agent config files (and .github/mcp.json) are still present, undermining the purpose of the base restore and potentially letting untrusted PR content influence any artifacts the pre-agent steps produce for the agent run. Consider moving activation artifact download + base restore earlier (before pre-agent-steps), or introducing a separate hook for pre-MCP steps while keeping pre-agent-steps after base restore.

Copilot uses AI. Check for mistakes.
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.

Smoke test agent here — this is a valid concern worth tracking. The base-restore ordering relative to pre-agent-steps is an important security invariant for PR workflows.

📰 BREAKING: Report filed by Smoke Copilot · ● 1.2M

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.

✅ Good change: moving generatePreAgentSteps before generateMCPSetup ensures that MCP dependencies can be installed/configured before the gateway starts. The comment clearly explains the rationale. Consider also verifying that existing pre-agent-step tests cover the case where an MCP server depends on a package installed by a pre-agent step.

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.

Moving pre-agent-steps before generateMCPSetup is the right direction for MCP dependency setup, but it means pre-agent steps run before base-branch restoration in PR workflows. This could allow untrusted PR content to influence pre-agent step artifacts. Consider whether the base-branch restore should also move earlier, or add a note in docs clarifying which use cases are safe for pre-agent-steps.


// Add MCP setup
if err := c.generateMCPSetup(yaml, data.Tools, engine, data); err != nil {
return fmt.Errorf("failed to generate MCP setup: %w", err)
Expand Down Expand Up @@ -384,9 +388,6 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
// connects to via host.docker.internal:18443.
c.generateStartCliProxyStep(yaml, data)

// Add pre-agent-steps (if any) immediately before AI execution.
c.generatePreAgentSteps(yaml, data)

// Add AI execution step using the agentic engine
compilerYamlLog.Printf("Generating engine execution steps for %s", engine.GetID())
c.generateEngineExecutionSteps(yaml, data, engine, logFileFull)
Expand Down
Loading