StateAnchor
Documentation
Back to app →
Platform

GitHub Action

The StateAnchor GitHub Action validates your stateanchor.yaml spec, evaluates breaking changes through the gate engine, and blocks PRs that violate your API contract policy. It runs on every push and pull request to your configured branches.

Overview

On each triggering event, the action performs three steps in sequence:

  1. OIDC exchange — requests a GitHub OIDC token and exchanges it with StateAnchor for a short-lived action token. This authenticates the repo without storing secrets.
  2. Validate — sends stateanchor.yaml to /api/action/validate for schema and structural validation.
  3. Gate check — calls /api/action/gate-check to diff the spec against the previous IR, score breaking changes, and return a gate decision.

Pull requests are always evaluated in audit mode — they report drift but never block. Only pushes to protected branches can run in sync orenforce mode, which blocks on breaking changes.

Installation

Create .github/workflows/stateanchor-sync.yml in your repository:

name: StateAnchor Sync

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  stateanchor-sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: movermon/wrapforge-action@main
        id: sa
        with:
          mode: enforce
          config-path: stateanchor.yaml
          api-base-url: https://wrapforge.dev

Inputs

InputRequiredDefaultDescription
modeNoauditExecution mode: audit (report only), sync or enforce (block on breaking changes).
config-pathNostateanchor.yamlPath to the spec file relative to repo root.
api-base-urlNohttps://wrapforge.devStateAnchor API base URL. Do not change unless self-hosting.
oidc-audienceNowrapforge-sync-actionOIDC audience claim for token exchange.
timeout-msNo15000Request timeout in milliseconds (1000–20000).
outage-policyNofail-closedfail-closed treats service outage as a block. fail-open allows sync to pass.
local-fallbackNotrueRun local YAML validation when remote is unavailable.
annotate-prNotruePost validation results as PR annotations.

Required permissions

The action requires two GitHub permissions in your workflow:

PermissionWhy
id-token: writeRequired to request a GitHub OIDC token for authentication with StateAnchor. Without this, the action falls back to local validation only.
contents: readRequired by actions/checkout@v4 to read the repository contents including stateanchor.yaml.

Outputs

OutputTypeDescriptionExample
resultstringOverall result of the action.pass, fail, soft-pass
gate-actionstringGate engine decision.allow, approval_required, block
gate-scoreintegerEffective breaking change score.0, 40, 65
sync-run-idUUIDID of the sync run created by gate-check.fb87c28a-4509-...
mode-effectivestringActual mode executed (may differ from requested).audit, sync
remote-executedbooleanWhether remote validation ran.true
fallback-executedbooleanWhether local fallback ran.false

Using outputs in your workflow

Reference outputs using the step ID to add conditional steps:

- uses: movermon/wrapforge-action@main
  id: sa
  with:
    mode: enforce

- name: Comment on PR if blocked
  if: steps.sa.outputs.gate-action == 'block'
  uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: '⛔ StateAnchor blocked this PR. Breaking score: ' +
              '${steps.sa.outputs.gate-score}'
      })

- name: Deploy if gate allows
  if: steps.sa.outputs.gate-action == 'allow'
  run: echo "Gate passed — deploying"

Fail behavior

When gate-action is block, the action exits with a non-zero status code, causing the workflow to fail. The Actions log includes:

  • The block reason (human-readable explanation)
  • The effective breaking score
  • A table of breaking operations with their individual scores
  • The sync run ID for debugging in the StateAnchor dashboard

When outage-policy is fail-closed and the StateAnchor API is unreachable, the action also exits non-zero. With fail-open, it returns soft-pass instead.

Troubleshooting

OIDC exchange failed

The action could not obtain a GitHub OIDC token. Check that id-token: write is in your workflow permissions. Fork PRs cannot obtain OIDC tokens — the action falls back to local validation automatically.

Config not found

The spec file was not found at the configured config-path. Verify the file exists at the repo root (or the custom path) and is committed. Check for typos in the filename — it must be stateanchor.yaml, not .yml.

Gate blocked unexpectedly

Check the breaking operations table in the Actions log. If the block is intentional, you can either fix the breaking change, create a drift exception in project settings, or temporarily switch to audit mode. Review the Gate Engine docs for scoring details.

Action times out

The default timeout is 15 seconds. If the StateAnchor API is slow, increase timeout-ms up to 20000. If timeouts persist, check the outage-policy setting — fail-open will allow the workflow to pass during outages.