CI checks

Block unreviewed permission and coverage drift in GitHub Actions.

CI should reproduce the same named scenarios and compare them with the committed granttrace.lock.json.

GitHub Actions example

.github/workflows/granttrace.yml
name: GrantTrace

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  permission-contract:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
        with:
          node-version: 22

      - run: npm ci
      - run: >-
          npx granttrace record --no-review issue-triage --
          npm test -- issue-triage
      - run: >-
          npx granttrace record --no-review release-read --
          npm test -- release-read
      - run: npx granttrace check --github-step-summary

The example pins reviewed third-party actions to full commit SHAs. Re-review those revisions deliberately when upgrading them. It uses npm for portability; keep your project's existing package manager and lockfile if you use pnpm or Yarn instead.

Never accept in CI

--no-review is important when CI records multiple scenarios: it prevents the first recording from comparing an intentionally incomplete aggregate against the committed contract. Use it only when a final granttrace check is guaranteed.

Without --no-review, record detects the noninteractive environment, prints the diff, never prompts, and never accepts it. check --accept is refused when CI is enabled or GITHUB_ACTIONS=true, before the contract or local operation state can be written. The same guard refuses frontier select and manual-keep mutation; their read-only list operations remain available. Exit 6 from the final check is the intended signal that a human must review permission or coverage drift locally and commit the resulting lockfile deliberately.

--github-step-summary is explicit opt-in. It appends a safe Markdown report to the absolute runner-provided GITHUB_STEP_SUMMARY file while preserving the normal text and exit code. Container jobs may mount that file outside RUNNER_TEMP; GrantTrace validates the target itself. For machine processing instead, use --format json; structured output always goes to stdout, including when the check exits 6 or 7.

Structured reports omit free-form reasons, scenario names, concrete URLs and paths, errors, and credentials. Permission identifiers remain visible because they are the machine-readable contract, including user-supplied manual-keep keys. Do not encode identities or secrets in a permission name.

This also applies when a previously selected permission-frontier candidate is no longer valid. CI reports the proposed deterministic fallback as a normal contract diff and never persists a replacement selection.

Exit 7 means the evidence is unknown, unsupported, malformed, or contradictory. Fix the scenario or contribute an official-docs-backed catalog change; do not guess a permission and force the pipeline green.

Keep live proof separate

The deterministic record-and-check workflow can run without GitHub App broker credentials when your scenarios use controlled responses. Optional live proof belongs in a trusted, explicitly triggered workflow against a disposable fixture. Never expose fixture credentials to untrusted pull requests.

See CLI reference for all exit codes.