How the Detection Job Determines When to Activate Workflow Runs in gh-aw

The detection job activates when either the main agent job produces safe-output types (output_types != '') or the workflow contains a code patch (has_patch == 'true'), as defined by a conditional if expression built in pkg/workflow/threat_detection.go.

The github/gh-aw repository generates dynamic GitHub Actions workflows that include an optional threat detection step. This step does not run unconditionally; instead, the compiler injects a logical condition that evaluates the outputs of the preceding agent job to determine if threat analysis is necessary.

How the Activation Condition Is Built in pkg/workflow/threat_detection.go

The core logic resides in the buildThreatDetectionJob function within pkg/workflow/threat_detection.go. This function constructs the if expression by inspecting the outputs of the main agent job that produced the safe-output artifacts.

Gathering the Main Agent Job Outputs

The compiler first identifies the main agent job name, then checks two specific outputs that indicate whether threat detection should run:

  • output_types – A comma-separated string listing the types of safe-outputs the agent produced (e.g., files, secrets, command outputs). An empty string indicates no safe-outputs were generated.
  • has_patch – A boolean flag indicating whether the workflow includes a code patch that requires inspection.

Building the Comparison Nodes

The function creates two comparison nodes using the internal expression builder. The first checks if any output types exist, and the second verifies if a patch is present:

// output_types != ''
hasOutputTypes := BuildComparison(
    BuildPropertyAccess(fmt.Sprintf("needs.%s.outputs.output_types", mainJobName)),
    "!=", BuildStringLiteral(""),
)

// has_patch == 'true'
hasPatch := BuildComparison(
    BuildPropertyAccess(fmt.Sprintf("needs.%s.outputs.has_patch", mainJobName)),
    "==", BuildStringLiteral("true"),
)

Combining Conditions with Logical OR

These two conditions are combined using a logical OR operation. The resulting expression evaluates to true if either safe-outputs were produced or a patch exists:

// (output_types != '' || has_patch == 'true')
condition := BuildDisjunction(false, hasOutputTypes, hasPatch)

The compiler then assigns this rendered expression to the job's If field:

job := &Job{
    Name: string(constants.DetectionJobName),
    If:   condition.Render(),
    // … other fields …
}

When the Detection Job Appears in the Workflow

The detection job is not always present in the generated workflow. It is only created when specific configuration criteria are met:

  • Safe-outputs must be configured: The job is added only if data.SafeOutputs != nil && data.SafeOutputs.ThreatDetection != nil evaluates to true.
  • Threat detection must be enabled: If the user disables threat detection via threat-detection: false or enabled: false, the parseThreatDetectionConfig function returns nil, and the compiler omits the detection job entirely.

If these conditions are not satisfied, the workflow proceeds with only the main agent job, and no threat analysis step is generated.

Practical Example of the Generated if Condition

When the compiler generates the final YAML, the detection job includes the conditional expression that references the main job's outputs. Assuming the main job is named build, the generated workflow snippet appears as:

detection:
  if: |
    needs.build.outputs.output_types != '' ||
    needs.build.outputs.has_patch == 'true'
  runs-on: ubuntu-latest
  steps:
    - name: Download artifacts
      uses: actions/download-artifact@v4
    - name: Run threat detection
      run: ./threat-analysis-engine

In this example, if the build job produces no safe-outputs (output_types is empty) and contains no patch (has_patch is not 'true'), the entire detection job is skipped automatically by GitHub Actions' conditional logic.

Summary

  • The detection job activation is controlled by a dynamic if expression built in pkg/workflow/threat_detection.go.
  • The condition checks two outputs from the main agent job: output_types (non-empty string) and has_patch (equals 'true').
  • A logical OR combines these checks; the job runs if either condition is satisfied.
  • The job only appears in workflows when safe-outputs are configured and threat detection is enabled in the repository settings.
  • The generated YAML uses standard GitHub Actions needs context syntax to evaluate the condition at runtime.

Frequently Asked Questions

What happens if the main agent job produces no safe-outputs and has no patch?

If both output_types is an empty string and has_patch is not 'true', the logical OR condition evaluates to false. GitHub Actions skips the detection job entirely, and the workflow continues without executing the threat analysis step.

Can I force the detection job to run on every workflow execution?

No. The current implementation in pkg/workflow/threat_detection.go hardcodes the conditional logic based on the presence of outputs or patches. To run detection unconditionally, you would need to modify the source code to remove the If condition from the job definition in buildThreatDetectionJob.

Where is the threat detection configuration validated?

The configuration is parsed and validated in pkg/workflow/threat_detection.go within the parseThreatDetectionConfig function. This function checks whether the user has enabled threat detection and whether safe-outputs are properly configured. If validation fails or detection is disabled, the function returns nil, and the compiler omits the detection job from the generated workflow.

How does the detection job access the main job's outputs?

The detection job uses the GitHub Actions needs context to reference outputs from the main agent job. The compiler constructs property access expressions like needs.<main-job-name>.outputs.output_types and needs.<main-job-name>.outputs.has_patch, which GitHub Actions evaluates at runtime to determine the job's execution status.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →