> ## Documentation Index
> Fetch the complete documentation index at: https://bintzgavin-apastra-14.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub workflows reference

> Reference for all 13 GitHub Actions workflows included with apastra, including triggers, job steps, and branch protection setup.

Apastra ships 13 GitHub Actions workflows in `.github/workflows/`. They cover eval gating, releases, promotion, delivery, governance, and operational scanning.

<Note>
  You do not need all 13 workflows. Start with the two basic CI workflows (`prompt-eval.yml` and `prompt-release.yml`) and add the full enterprise set when you need fine-grained control.
</Note>

***

## Basic CI (start here)

These two workflows give you regression gating and immutable releases with minimal setup.

### `prompt-eval.yml`

Runs on every pull request that touches `promptops/**`. Evaluates changed prompts and blocks merge if a regression is detected.

**Trigger:**

```yaml theme={null}
on:
  pull_request:
    paths:
      - 'promptops/**'
```

**What it does:** delegates to `regression-gate.yml` via `workflow_call`.

```yaml theme={null}
jobs:
  eval:
    uses: ./.github/workflows/regression-gate.yml
```

**Branch protection setup:** add `eval` (or the check name from `regression-gate.yml`) as a required status check on your `main` branch. PRs that fail the gate cannot merge.

***

### `prompt-release.yml`

Runs when any tag is pushed. Creates an immutable GitHub Release with SLSA provenance.

**Trigger:**

```yaml theme={null}
on:
  push:
    tags:
      - '*'
```

**What it does:** delegates to `immutable-release.yml` via `workflow_call`.

```yaml theme={null}
jobs:
  release:
    uses: ./.github/workflows/immutable-release.yml
```

***

## Full CI (enterprise)

Use these when you need separate regression gating, manual promotion, governed delivery, auto-merge, and human approval recording.

### `regression-gate.yml`

Dedicated regression gating job. Can be called directly or via `workflow_call` from `prompt-eval.yml`.

**Triggers:**

```yaml theme={null}
on:
  pull_request:
    branches: [main]
  workflow_call:
```

**Key job steps:**

1. **Checkout** the repository with `fetch-depth: 2`.
2. **Detect changed files** — checks `promptops/harnesses/**`, `promptops/prompts/**`, `promptops/datasets/**`, and `promptops/policies/*.yaml`.
3. **Skip if no evaluable changes** — exits cleanly when only non-promptops files changed.
4. **Fetch artifacts branch** — checks out `regression_report.json` and `run_manifest.json` from `origin/promptops-artifacts`.
5. **Check regression report status** — reads `reports/regression_report.json`:
   * If the file is missing, fails with `::error::Regression report missing. Merges are blocked.`
   * Prints a summary table of evidence rows (metric, status, candidate, baseline, delta, message) to `$GITHUB_STEP_SUMMARY`.
   * Emits `::error` annotations for any failing metrics.
   * Reads `status` field: exits 0 on `pass`, exits 1 on any other value.
6. **Check cost budget** — if `run_manifest.json` contains `total_cost`, validates it against `budgets.cost_budget` in every suite file.

<Warning>
  This workflow reads from `origin/promptops-artifacts`. Your harness must write the regression report to that branch before the gate runs. If you use an agent-based harness, the agent writes `reports/regression_report.json` to the artifacts branch as part of the eval workflow.
</Warning>

***

### `promote.yml`

Creates an append-only promotion record binding an approved digest to a delivery channel, then triggers `deliver.yml`.

**Triggers:**

```yaml theme={null}
on:
  workflow_dispatch:
    inputs:
      digest:         # content digest of the promoted package
      channel:        # e.g., prod, staging
      evidence_refs:  # comma-separated URIs to regression reports
  release:
    types: [published]
```

**Key job steps (`record-promotion`):**

1. **Checkout** the `promptops-artifacts` branch.
2. **Enforce approval** — reads `approvals/*.json` from the artifacts branch and verifies a matching approval record (matching `revision_ref` and `decision == "approved"` and `checks_passed == true`) exists. Fails if no approval is found.
3. **Generate promotion record** — writes a JSON file to `promotions/<timestamp>-<id>.json` on the artifacts branch:
   ```json theme={null}
   {
     "id": "...",
     "timestamp": "...",
     "digest": "...",
     "channel": "prod",
     "approver": "...",
     "evidence_refs": ["..."]
   }
   ```
4. **Commit and push** the promotion record to `promptops-artifacts`.
5. **Trigger `deliver.yml`** — passes the promotion record path as an input.

When triggered by a `release` event, the digest is set to `release-tag:<tag_name>` and the channel is set to `release`.

***

### `deliver.yml`

Syncs an approved version to delivery targets. Called by `promote.yml` via `workflow_call`.

**Trigger:** `workflow_call` only (not triggered directly).

**Input:** `promotion_record_path` — path to the promotion record on the artifacts branch.

**Key job steps:**

1. **Checkout** the main branch.
2. **Fetch promotion record** from `origin/promptops-artifacts`.
3. **Sync to targets** — reads `channel` and `digest` from the promotion record. Iterates over all `promptops/delivery/*.yaml` files and finds targets whose `channel` matches. For each match, reads `type` and `repo` and executes the appropriate sync.

Supported target types (from `delivery-target.schema.json`): `github_pr`, `oci_registry`.

***

### `immutable-release.yml`

Creates a GitHub Release with a content-addressed tarball and SLSA build provenance attestation.

**Triggers:**

```yaml theme={null}
on:
  push:
    tags:
      - '*'
  workflow_call:
```

**Key job steps:**

1. **Checkout** the repository.
2. **Package prompts** — creates `promptops.tar.gz` from the `promptops/` directory and computes its SHA-256 digest.
3. **Attest build provenance** — uses `actions/attest-build-provenance` to attach a SLSA provenance attestation to the tarball.
4. **Create GitHub Release** — uses `gh release create` with the tag name, attaches `promptops.tar.gz`, and includes the digest in the release notes.

**Required permissions:** `contents: write`, `id-token: write`, `attestations: write`, `artifact-metadata: write`.

***

### `auto-merge.yml`

Automatically enables squash merge for PRs opened by authorized users (Jules and the repo owner). Resolves `docs/PROGRESS.md` merge conflicts using a union merge strategy.

**Trigger:**

```yaml theme={null}
on:
  pull_request:
    types: [opened, reopened, synchronize]
```

**Condition:** only runs for PRs from `google-labs-jules`, `google-labs-jules[bot]`, or `BintzGavin`.

**Key job steps (`resolve-progress-conflicts`):**

1. Checks out the PR branch.
2. Fetches the base branch.
3. Attempts a merge; if `docs/PROGRESS.md` has conflicts, resolves them by concatenating both versions (union merge).
4. Pushes the resolved branch.

**Key job steps (`enable-auto-merge`):**

1. Marks the PR as ready for review.
2. Calls `gh pr merge --auto --squash` with a retry loop (5 attempts, backoff) to handle rate limits.

***

### `record-approval.yml`

Records a human approval or rejection decision as an append-only JSON file on the `promptops-artifacts` branch.

**Trigger:**

```yaml theme={null}
on:
  workflow_dispatch:
    inputs:
      revision_ref:   # digest or ID of the revision/package
      decision:       # approved | rejected | abstained
```

**Key job steps:**

1. **Checkout** the `promptops-artifacts` branch.
2. **Generate approval state record** — writes to `approvals/<timestamp>-<id>.json`:
   ```json theme={null}
   {
     "revision_ref": "...",
     "checks_passed": true,
     "decision": "approved",
     "human_review": {
       "reviewer": "...",
       "timestamp": "..."
     }
   }
   ```
3. **Commit and push** the approval record to `promptops-artifacts`.

The `promote.yml` workflow reads these records to enforce approval before creating a promotion record.

***

## Operational workflows

### `schema-validation.yml`

Validates changed prompt and dataset files against JSON schemas on every pull request.

**Trigger:**

```yaml theme={null}
on:
  pull_request:
    paths:
      - 'promptops/prompts/**'
      - 'promptops/datasets/**'
```

**Key job steps:**

1. **Detect changed files** using `tj-actions/changed-files`.
2. **Install dependencies** — `npm install -g ajv-formats ajv-cli`.
3. **Validate changed files:**
   * Prompt files: runs `promptops/validators/validate-prompt-spec.sh <file>`.
   * Dataset files: runs `promptops/validators/validate-dataset.sh <manifest> <cases>` for each dataset directory with both a manifest and a `cases.jsonl`. Skips incomplete datasets with a warning.
   * Emits `::error` annotations for any validation failures and exits with code 1.

***

### `canary-drift-detection.yml`

Runs canary suites on a schedule to detect post-ship quality erosion when model providers update silently.

**Triggers:**

```yaml theme={null}
on:
  schedule:
    - cron: '0 * * * *'   # every hour
  workflow_dispatch:
```

**Key job steps:**

1. Checkout the repository.
2. Run the canary suite harness.
3. Parse `reports/drift_report.json`.
4. Check the `drift_detected` field.
5. Trigger an alert and auto-rollback if `drift_detected == true`.

<Note>
  The canary harness integration is a placeholder in the current implementation. You supply your own harness command in step 2.
</Note>

***

### `community-reporting.yml`

Triages community moderation reports submitted as GitHub Issues with the `moderation-report` label.

**Trigger:**

```yaml theme={null}
on:
  issues:
    types: [opened]
```

**Condition:** only runs if the issue has the `moderation-report` label.

**Key job steps:**

1. **Add acknowledgment comment** — posts a standard acknowledgment to the issue.
2. **Assign to governance admins** — adds `apastra` and `governance-admins` as assignees.

***

### `moderation-scan.yml`

Scans changed prompt and dataset files for blocked keywords on every pull request.

**Trigger:**

```yaml theme={null}
on:
  pull_request:
    paths:
      - 'promptops/prompts/**'
      - 'promptops/datasets/**'
```

**Key job steps:**

1. Runs a `grep` scan for blocked keywords across `promptops/prompts/` and `promptops/datasets/`.
2. Exits 1 (failing the check) if any blocked keywords are found.

Replace the placeholder keyword pattern with your organization's content policy patterns.

***

### `secret-scan.yml`

Detects secrets accidentally embedded in prompt files or datasets.

**Trigger:**

```yaml theme={null}
on:
  pull_request:
    paths:
      - 'promptops/prompts/**'
      - 'promptops/datasets/**'
```

**Key job steps:**

1. Scans all prompt and dataset files for patterns matching `api_key:`, `secret:`, `password:`, or `token:`.
2. Exits 1 if any matches are found.

<Warning>
  This is a lightweight pattern scan, not a full secret-scanning solution. For production use, consider adding a dedicated secret scanning tool (e.g., `truffleHog`, GitHub's built-in secret scanning, or `gitleaks`) alongside this workflow.
</Warning>

***

## Branch protection setup

To enforce required status checks on `main`:

<Steps>
  <Step title="Go to branch protection settings">
    In your GitHub repo, go to **Settings → Branches → Add branch protection rule** for `main`.
  </Step>

  <Step title="Enable required status checks">
    Check **Require status checks to pass before merging**. Search for and add the check names you want to require.

    For basic CI, add:

    * `eval / gate` (from `regression-gate.yml`)
    * `validate-schemas / validate-schemas` (from `schema-validation.yml`)

    For full CI, also add:

    * `scan / scan-secrets` (from `secret-scan.yml`)
    * `scan / scan` (from `moderation-scan.yml`)
  </Step>

  <Step title="Enable required reviews">
    Optionally require at least one approving review from a CODEOWNERS reviewer before merge.
  </Step>

  <Step title="Set up CODEOWNERS">
    Add a `CODEOWNERS` file to define who must review which parts of the repo:

    ```
    promptops/prompts/   @your-org/prompt-authors
    promptops/policies/  @your-org/platform-team
    .github/workflows/   @your-org/platform-team
    ```
  </Step>
</Steps>

***

## Workflow summary table

| Workflow                     | Trigger                                     | Group       |
| ---------------------------- | ------------------------------------------- | ----------- |
| `prompt-eval.yml`            | PR touching `promptops/**`                  | Basic CI    |
| `prompt-release.yml`         | Tag push                                    | Basic CI    |
| `regression-gate.yml`        | PR to `main` / `workflow_call`              | Full CI     |
| `promote.yml`                | Manual dispatch / release published         | Full CI     |
| `deliver.yml`                | `workflow_call` from `promote.yml`          | Full CI     |
| `immutable-release.yml`      | Tag push / `workflow_call`                  | Full CI     |
| `auto-merge.yml`             | PR opened/updated                           | Full CI     |
| `record-approval.yml`        | Manual dispatch                             | Full CI     |
| `schema-validation.yml`      | PR touching prompts or datasets             | Operational |
| `canary-drift-detection.yml` | Hourly schedule / manual dispatch           | Operational |
| `community-reporting.yml`    | Issue opened with `moderation-report` label | Operational |
| `moderation-scan.yml`        | PR touching prompts or datasets             | Operational |
| `secret-scan.yml`            | PR touching prompts or datasets             | Operational |
