# Open-PR Bound Pattern in Design-Control-Loop Phase G: Workflow Control Implementation

> Implement the open PR bound pattern in Phase G for efficient workflow control. Learn how this design enforces single pull requests per agent using GitHub queries and loop labels.

- Repository: [HumanLayer/skills](https://github.com/humanlayer/skills)
- Tags: how-to-guide
- Published: 2026-09-13

---

**The open-PR bound pattern in Phase G of the design-control-loop skill enforces a single-pull-request limit per agent by querying GitHub for existing open PRs matching a unique loop label, preventing scheduled runs from overwhelming reviewers while allowing manual overrides.**

The `humanlayer/skills` repository contains a sophisticated design-control-loop skill that manages autonomous agent workflows through GitHub Actions. Phase G of this loop implements the open-PR bound pattern, a critical flow-control mechanism that limits work-in-progress by ensuring only one pull request remains open per agent task at any time.

## Core Concept of the Open-PR Bound Pattern

### Bound Definition and Configuration

The default configuration restricts each agent to **at most one open PR**. This prevents the loop from creating new pull requests faster than human reviewers can inspect and merge them. The bound is documented in [`plugins/design-control-loop/skills/design-control-loop/SKILL.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/SKILL.md) under *Phase G — Flow control*.

### Label-Based Identification

Each loop instance tags its pull requests with a unique label defined by the `AGENT_LABEL` environment variable (format: `agent-<task-slug>`). This label serves as the primary filter for identifying existing work-in-progress items belonging to specific agent tasks across the repository.

## How the Workflow Detects Existing PRs

### The GitHub CLI Query

At the start of every scheduled execution, the workflow runs the **"Check iterate marker to bound work-in-progress"** step in [`plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml) (lines 55-63). This step uses the GitHub CLI to query for open PRs filtered by the agent's unique label:

```bash
EXISTING_PR=$(gh pr list --repo "$GITHUB_REPOSITORY" \
              --state open --label "$AGENT_LABEL" \
              --json number,url --limit 1)

```

### Early Exit Logic

The workflow parses the JSON output to determine if any open PRs exist. If the query returns results (array length > 0), the workflow sets `run_agent=false` and exits immediately, effectively no-op'ing the run:

```bash
if [ "$(echo "$EXISTING_PR" | jq 'length')" -gt 0 ]; then
  echo "run_agent=false" >> "$GITHUB_OUTPUT"
  echo "Open PR already exists for $AGENT_LABEL; no-op."
  exit 0
fi

```

This check ensures that scheduled runs respect the bound and do not spawn additional pull requests while one remains open.

## Manual Trigger Bypass Mechanism

Manual `workflow_dispatch` triggers bypass the open-PR bound entirely. The workflow detects non-scheduled events via the `EVENT_NAME` environment variable and immediately sets `run_agent=true` for events that are not `issue_comment`, allowing users to force execution when needed:

```yaml
if [ "$EVENT_NAME" != "issue_comment" ]; then
  echo "run_agent=true" >> "$GITHUB_OUTPUT"
  exit 0
fi

```

This bypass ensures that human-initiated runs always proceed regardless of existing open PRs, providing an escape hatch for urgent updates.

## Complete Implementation in workflow-template.yml

The following excerpt from [`plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml) demonstrates the full bound enforcement logic:

```yaml
- name: Check iterate marker to bound work-in-progress
  id: agent_gate
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    EVENT_NAME: ${{ github.event_name }}
    GITHUB_REPOSITORY: ${{ github.repository }}
    AGENT_LABEL: agent-<task-slug>
  run: |
    # Scheduled runs no-op when an open PR already exists for this agent

    if [ "$EVENT_NAME" = "schedule" ] || [ "$EVENT_NAME" = "workflow_call" ]; then
      EXISTING_PR=$(gh pr list --repo "$GITHUB_REPOSITORY" \
                    --state open --label "$AGENT_LABEL" \
                    --json number,url --limit 1)
      if [ "$(echo "$EXISTING_PR" | jq 'length')" -gt 0 ]; then
        echo "run_agent=false" >> "$GITHUB_OUTPUT"
        echo "Open PR already exists for $AGENT_LABEL; no-op."
        exit 0
      fi
    fi
    # Manual runs continue regardless of existing PRs

    if [ "$EVENT_NAME" != "issue_comment" ]; then
      echo "run_agent=true" >> "$GITHUB_OUTPUT"
      exit 0
    fi

```

When creating new pull requests, the workflow applies the same label to maintain consistency for future bound checks:

```yaml
- name: Create PR (if not existing)
  if: steps.agent_gate.outputs.run_agent == 'true'
  uses: peter-evans/create-pull-request@v5
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    title: "[agent-<task-slug>] Automated update"
    branch: ${{ env.BRANCH }}
    labels: ${{ env.AGENT_LABEL }}

```

## Summary

- The open-PR bound pattern limits each agent to one open pull request at a time, preventing reviewer overload in the `humanlayer/skills` design-control-loop.
- The workflow checks for existing PRs by querying GitHub for open issues labeled with the unique `AGENT_LABEL` assigned to each loop instance.
- If an open PR exists during a scheduled run, the workflow exits early with a no-op status, while manual triggers bypass this restriction entirely.
- The implementation relies on the GitHub CLI (`gh pr list`) with JSON output parsing to detect existing work-in-progress items.

## Frequently Asked Questions

### How does the open-PR bound pattern prevent multiple simultaneous PRs from the same agent?

The pattern enforces a single-PR limit by querying GitHub for existing open pull requests tagged with the agent's unique label before executing the main loop logic. If the query in [`workflow-template.yml`](https://github.com/humanlayer/skills/blob/main/workflow-template.yml) returns any results, the workflow terminates early with `run_agent=false`, ensuring only one PR remains active until it is merged or closed.

### What happens if I manually trigger the workflow while a PR is already open?

Manual `workflow_dispatch` triggers bypass the open-PR bound check entirely. The workflow detects non-scheduled events via the `EVENT_NAME` environment variable and automatically sets `run_agent=true` for events that are not `issue_comment`, allowing you to force execution even when an open PR already exists for that agent label.

### Where is the AGENT_LABEL defined in the workflow configuration?

The `AGENT_LABEL` environment variable is defined in the **"Check iterate marker to bound work-in-progress"** step within [`plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml), typically set to `agent-<task-slug>` to match the specific agent task identifier. This label is applied to newly created PRs and used as the filter criteria for existence checks.

### Can the bound limit be configured to allow more than one open PR?

The current implementation in Phase G enforces a hard limit of one open PR per agent through the `--limit 1` parameter in the GitHub CLI query and the JSON array length check. While the documentation describes this as the "default" bound, modifying the workflow would require adjusting the query logic and gate conditions in [`workflow-template.yml`](https://github.com/humanlayer/skills/blob/main/workflow-template.yml) to change the threshold value.