Skip to content

CI workflows

CI workflows are excluded from the end user tarball by .gaia/release-exclude. They run only on the GAIA template repo’s clone. chromatic.yml and tests.yml ship to end users and are out of scope here. code-review-audit.yml is also release-excluded, but unlike the contributor-only workflows below, adopters can install it on demand with /setup-gaia-ci; the template repo keeps it in-tree as the live pre-merge gate. The agent it runs is documented in the agent reference.

Triggers on v*.*.* tag pushes. Builds and publishes the end user tarball.

The job sequence:

  1. Checkout at the tag with full git history.
  2. Derive version from the tag ref.
  3. Verify .gaia/VERSION matches the tag. Fails the workflow if they diverge, so a stray tag cannot publish.
  4. Extract the CHANGELOG section for the release. Fails if the version’s heading is missing.
  5. Verify the manifest is fresh to confirm .gaia/manifest.json reflects the current file set. Catches stale manifests.
  6. Stage the release tree. Drives the file set from git ls-files so untracked artifacts stay out, then filters .gaia/release-exclude patterns. Copies the surviving files into /tmp/gaia-<tag>/ with rsync.
  7. Bundle-time scrub of the staging directory: gaia:maintainer-only blocks are stripped and leak checks run.
  8. Verify runtime dependencies against the staged tree.
  9. Distribution test gate runs .gaia/tests/distribution/run-all.sh against an independently-staged tree (Layers 0+1+2). Halts the release if any scenario fails.
  10. Build the tarball as gaia-<tag>.tar.gz.
  11. Create the GitHub Release with the extracted CHANGELOG section as release notes.

The pre-publish distribution gate uses the org CLAUDE_CODE_OAUTH_TOKEN secret. A broken release cannot publish: the gate runs before gh release create.

Triggers on both push to main and pull_request against main. The job is gated to pull_request events via a job-level if: condition; push events keep the required status check green on merge commits without rerunning anything. Runs the Vitest suite under .gaia/cli/.

PRs that don’t touch .gaia/cli/** or this workflow file report green without installing or running anything. The path filter is dorny/paths-filter.

When the filter triggers, the job:

  1. Sets up pnpm via pnpm/action-setup (pinned to v6.0.5).
  2. Sets up Node from .node-version with pnpm cache keyed to .gaia/cli/pnpm-lock.yaml.
  3. Installs CLI dependencies with --frozen-lockfile.
  4. Runs pnpm -C .gaia/cli typecheck.
  5. Runs pnpm -C .gaia/cli test --run.

The required-check posture is intentional: contributors who don’t touch the CLI source see the check as required-and-green without paying the install cost.

Triggers on both push to main and pull_request against main, with the job gated to pull_request events via a job-level if: condition. Runs the bats suites for the .github/audit/ helpers that code-review-audit.yml consumes: check-trailer.sh (the skip-logic gate) and resolve-audit-base.sh (the incremental-review base).

PRs that don’t touch .github/audit/**, this workflow file, or .claude/hooks/lib/repo-scope.sh report green without installing or running anything. The path filter is dorny/paths-filter, mirroring cli-tests.yml.

Adopters receive the audit scripts as GAIA-controlled code they never edit, so the suite guards only maintainer changes.

Triggers only on workflow_dispatch. Used to run the .gaia/tests/distribution/ harness on a feature branch for ad-hoc verification of harness changes themselves.

The production gate against the harness lives inside release.yml. There is no automatic trigger on this workflow on purpose: release.published fires after publish (too late to gate a release), and pull_request is unsafe for forks.

The workflow does not use pull_request_target. That trigger exposes secrets to fork PRs, which would let any contributor exfiltrate CLAUDE_CODE_OAUTH_TOKEN.

The job verifies the bundled .gaia/cli/gaia is present and executable, then runs bash .gaia/tests/distribution/run-all.sh with a 15-minute timeout.

Triggers on issue events (opened, labeled) when the issue carries the gaia-forensics label. Runs autonomous Claude Code triage against the issue.

Permissions are scoped per-job: issues: write, contents: write, pull-requests: write, actions: read, id-token: write. Concurrency is keyed per-issue.

The workflow has helpers under .github/forensics/: scope checks, classifier prompts, fixtures, and bats tests. All of .github/forensics/ is contributor-only and excluded from the end user tarball.

Steps include an idempotency early-exit (skip if gaia-triaged is already on the issue), classification, optional apply-fix path that runs the project quality gate, and a final fallback step that ensures gaia-triaged lands on the issue regardless of path.

The workflow is on the canonical denylist: future autonomous triage runs must not modify it. The workflow’s check-scope.sh rejects any attempt to edit any path under .github/workflows/. Self-modifying triage is a security boundary violation.

GAIA can install a set of automation workflows into an adopter’s repository on demand. They are not committed to the template repo’s .github/workflows/; their templates live in the CLI source at .gaia/cli/src/automation/templates/workflows/ and are rendered into an adopter’s repo by /setup-gaia-ci.

The installable set:

  • gaia-ci-wiki.yml: daily /gaia-wiki sync, consolidate, and lint chain
  • gaia-ci-update-deps.yml: weekly dependency updates
  • gaia-ci-pnpm-audit.yml: weekly pnpm audit security pass
  • gaia-ci-stale-branches.yml: monthly cleanup of long-merged branches
  • code-review-audit.yml: the pre-merge audit gate, installed unconditionally (not a cron tool)

The cron workflows are generated from .gaia/automation.json, so which tools run is per-project. The /setup-gaia-ci reference covers the adopter flow, tool modes, and schedules. The template repo keeps its own code-review-audit.yml in-tree as a live gate, so the gaia-ci-* cron workflows are an adopter-side surface rather than workflows that run on the GAIA template itself.