# GitHub gh-aw Pre-Activation Checks: Team Membership, Rate Limiting, and Skip Conditions Explained

> Understand GitHub gh-aws pre-activation checks for team membership, rate limiting, and skip conditions. Optimize your workflows with boolean expressions.

- Repository: [GitHub/gh-aw](https://github.com/github/gh-aw)
- Tags: deep-dive
- Published: 2026-02-16

---

**The `github/gh-aw` compiler generates a dedicated pre-activation job that evaluates team membership, rate limits, stop-times, and skip conditions via AND-combined boolean expressions before allowing the main agent workflow to execute.**

The `github/gh-aw` repository provides a workflow compiler that automatically injects **pre-activation checks** into generated GitHub Actions. These checks determine whether a workflow should proceed based on configurable conditions such as team membership, rate limiting, and temporal constraints. When any check fails, the workflow halts immediately without consuming agent resources.

## What Are Pre-Activation Checks in gh-aw?

When a workflow is compiled, **gh-aw creates a dedicated "pre-activation" job** that runs before the main agent job. This job aggregates a set of checks that are evaluated in a single boolean expression. If any check fails, the workflow stops without running the agent.

The pre-activation checks are built in [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) by the function `buildPreActivationJob`. Each check lives in its own helper function or inline logic, and all enabled checks produce a **single output `activated`** whose value is a GitHub Actions expression built from the individual step outputs.

## Available Pre-Activation Checks

The compiler supports eight distinct pre-activation checks, each triggered by specific front-matter configuration.

### Team Membership (Permission) Check

**File:** [`pkg/workflow/role_checks.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/role_checks.go) – `generateMembershipCheck`  
**Script:** `actions/setup/js/check_membership.cjs`  
**Front-matter key:** `roles` or `bots`

This check executes via `actions/github-script` to verify the triggering user belongs to one of the required GitHub teams or roles. It also supports optional bot allowlisting. The check is added when `needsPermissionCheck == true`, which occurs when a `roles` or `bots` front-matter entry is present.

### Rate Limit Check

**File:** [`pkg/workflow/role_checks.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/role_checks.go) – `generateRateLimitCheck`  
**Script:** `actions/setup/js/check_rate_limit.cjs`  
**Front-matter key:** `rate-limit`

This check enforces a per-user limit on how many times the workflow may be triggered in a configurable time window. It is added when `data.RateLimit != nil`, meaning a `rate-limit` block is defined in the front-matter.

### Stop-Time Check

**File:** [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (lines 106-120)  
**Script:** `actions/setup/js/check_stop_time.cjs`  
**Front-matter key:** `stop-time`

This check stops the workflow after a given datetime (UTC). It runs `check_stop_time.cjs` to compare the current time against the configured threshold.

### Skip-If-Match Check

**File:** [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (lines 124-138)  
**Script:** `actions/setup/js/check_skip_if_match.cjs`  
**Front-matter key:** `skip-if-match`

This check executes a searchable query. If the query returns any matches, the workflow is **skipped**. It is useful for preventing runs when blocking issues or labels exist.

### Skip-If-No-Match Check

**File:** [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (lines 140-154)  
**Script:** `actions/setup/js/check_skip_if_no_match.cjs`  
**Front-matter key:** `skip-if-no-match`

Conversely, this check skips the workflow when a query returns **no** matches. It ensures the workflow only runs when specific preconditions (like required labels) are present.

### Skip-Roles Check

**File:** [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (lines 157-172)  
**Script:** `actions/setup/js/check_skip_roles.cjs`  
**Front-matter key:** `skip-roles`

This check skips the workflow when the triggering user belongs to any of the listed roles. It acts as a negative permission filter.

### Skip-Bots Check

**File:** [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (lines 174-188)  
**Script:** `actions/setup/js/check_skip_bots.cjs`  
**Front-matter key:** `skip-bots`

This check skips the workflow when the actor is a bot matching the supplied identifiers, preventing automated accounts from triggering expensive runs.

### Command-Position Check

**File:** [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (lines 190-202)  
**Script:** `actions/setup/js/check_command_position.cjs`  
**Front-matter key:** `command`

For command-driven workflows, this check validates that the comment triggering the workflow contains the command at the expected position.

## How Pre-Activation Checks Are Combined

All enabled checks produce boolean outputs that are AND-combined into a single `activated` expression. The compiler assembles this expression within `buildPreActivationJob` in [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go).

The generated expression follows this pattern:

```go
// Simplified representation of the expression assembly
${{ steps.check_membership.outputs.is_team_member == 'true' &&
    steps.check_rate_limit.outputs.rate_limit_ok == 'true' &&
    steps.check_stop_time.outputs.stop_time_ok == 'true' &&
    steps.check_skip_if_match.outputs.skip_ok == 'true' &&
    steps.check_skip_if_no_match.outputs.skip_ok == 'true' &&
    steps.check_skip_roles.outputs.skip_roles_ok == 'true' &&
    steps.check_skip_bots.outputs.skip_bots_ok == 'true' &&
    steps.check_command_position.outputs.command_ok == 'true' }}

```

If the list of enabled checks is empty, the compiler panics, as this represents a developer error. The step IDs referenced in these expressions are defined as constants in [`pkg/constants/constants.go`](https://github.com/github/gh-aw/blob/main/pkg/constants/constants.go), including `CheckMembershipStepID`, `CheckRateLimitStepID`, `CheckStopTimeStepID`, and others.

## Configuring Pre-Activation Checks in Workflow Front-Matter

You enable checks by adding specific keys to the YAML front-matter of your workflow definition file (typically `.github/workflows/*.md`).

Here is a comprehensive configuration example:

```yaml
---

# Team membership requirement

roles: ["maintainer", "team-ops"]

# Optional bot allowlist

bots: ["github-actions[bot]"]

# Rate limiting: max 5 triggers per hour per user

rate-limit:
  max: 5
  window: 60
  events: ["issue_comment", "pull_request_review_comment"]

# Hard stop after UTC datetime

stop-time: "2026-03-01T12:00:00Z"

# Skip if blocking issues exist

skip-if-match:
  query: "repo:github/gh-aw is:open label:blocked"
  max: 1

# Skip if no ready items found

skip-if-no-match:
  query: "repo:github/gh-aw is:open label:ready"

# Skip for specific roles

skip-roles: ["admin", "owner"]

# Skip specific bots

skip-bots: ["dependabot[bot]"]

# Command trigger validation

command: ["/gh aw run"]
---

```

When the compiler processes this front-matter, it detects each non-nil block and generates the corresponding check step in the pre-activation job.

## Programmatic Access to Pre-Activation Logic

If you need to invoke the compiler programmatically and inspect the generated pre-activation job, use the following Go snippet:

```go
package main

import (
    "fmt"
    "github.com/github/gh-aw/pkg/workflow"
)

func main() {
    // Load a test workflow file (or construct WorkflowData manually)
    data, _ := workflow.LoadWorkflowFile("example.md")

    // Instantiate the compiler (default settings)
    comp := workflow.NewCompiler()

    // Build the pre-activation job; the second arg indicates whether a permission check is required
    job, err := comp.buildPreActivationJob(data, len(data.Roles) > 0 || len(data.Bots) > 0)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Pre-activation job has %d steps and the expression:\n%s\n",
        len(job.Steps), job.Outputs["activated"])
}

```

Running this snippet prints the list of generated steps (membership, rate-limit, etc.) and the combined `${{ … }}` expression that the activation job evaluates at runtime.

## Summary

- **gh-aw** generates a dedicated pre-activation job that runs before the main agent workflow to validate execution conditions.
- **Eight distinct checks** are available: team membership, rate limiting, stop-time, skip-if-match, skip-if-no-match, skip-roles, skip-bots, and command-position.
- **Configuration** occurs via YAML front-matter in workflow definition files, with each check triggered by specific keys like `roles`, `rate-limit`, or `skip-if-match`.
- **Implementation** resides in [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) (job assembly) and [`pkg/workflow/role_checks.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/role_checks.go) (membership and rate-limit logic), with step IDs defined in [`pkg/constants/constants.go`](https://github.com/github/gh-aw/blob/main/pkg/constants/constants.go).
- **Runtime execution** uses JavaScript scripts located in `actions/setup/js/*.cjs`, with outputs combined into a single boolean `activated` expression that determines workflow continuation.

## Frequently Asked Questions

### How do I enable team membership validation in gh-aw?

Add a `roles` array to your workflow's front-matter. The compiler invokes `generateMembershipCheck` in [`pkg/workflow/role_checks.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/role_checks.go), which creates a step running `check_membership.cjs` to verify the triggering user belongs to at least one specified GitHub team. You can optionally include a `bots` array to allowlist specific automated accounts.

### What happens when multiple pre-activation checks are configured?

The compiler builds a single boolean expression in `buildPreActivationJob` that AND-combines all enabled check outputs. Each step (membership, rate-limit, stop-time, etc.) produces an output that must equal `'true'` for the final `activated` output to evaluate as true. If any check fails, the workflow stops immediately without running the agent job.

### How does the rate limiting check work?

When you define a `rate-limit` block in front-matter with `max`, `window`, and `events` fields, the compiler calls `generateRateLimitCheck` from [`pkg/workflow/role_checks.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/role_checks.go). This generates a step that executes `check_rate_limit.cjs` to enforce per-user trigger limits within the specified time window, preventing workflow abuse or excessive API consumption.

### Can I skip workflows based on search queries?

Yes. Use `skip-if-match` to skip the workflow when a GitHub search query returns results (e.g., existing blocked issues), or `skip-if-no-match` to skip when no results are found (e.g., missing required labels). These checks run `check_skip_if_match.cjs` and `check_skip_if_no_match.cjs` respectively, and are implemented in [`pkg/workflow/compiler_activation_jobs.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/compiler_activation_jobs.go) around lines 124-154.