Skip to content

Hooks and CI

Run canon where mistakes are cheapest to fix: as the agent edits, before a commit, and on every pull request.

Claude Code hook

canon hook reads a PostToolUse payload on stdin, checks the file the agent just edited, and exits 2 with the findings on stderr so the agent fixes them before moving on. Add it to .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [{ "type": "command", "command": "canon hook" }]
      }
    ]
  }
}

What the agent sees:

canon found 1 issue(s) in Dockerfile. Fix them before continuing; run `canon explain <rule>` for the full reasoning.
- Dockerfile:6 [error canon-final-stage-user] Final stage sets no USER, so it runs as root (why: A process that escapes a non-root container starts with no privileges on the host.) fix: USER 65532:65532

Only findings at or above --min-severity (default error) block. Set it with CANON_HOOK_MIN_SEVERITY=warning to be stricter. The hook uses the payload's working directory as the root unless you pass --root.

Git pre-commit

Given paths, canon hook checks those files:

#!/usr/bin/env bash
# .git/hooks/pre-commit
canon hook $(git diff --cached --name-only --diff-filter=ACM)

GitHub Actions

Check only the files a pull request changed and upload the result as SARIF, so findings appear as code scanning alerts:

name: canon
on:
  pull_request:
permissions: {}
jobs:
  canon:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@<40-char commit SHA> # <version>
        with:
          fetch-depth: 0
          persist-credentials: false
      - uses: actions/setup-go@<40-char commit SHA> # <version>
        with:
          go-version: stable
      - run: go install github.com/rossedman/canon@latest
      - run: canon check --changed-only --base origin/${{ github.base_ref }} --sarif > canon.sarif
      - if: always()
        uses: github/codeql-action/upload-sarif@<40-char commit SHA> # <version>
        with:
          sarif_file: canon.sarif

canon check exits 1 when a finding reaches --fail-on (default error), which fails the job; the SARIF step still runs. Pin the actions to full commit SHAs, as canon's own ci-pin-actions-sha record requires.

Review before you push

canon review adds the concerns no linter reports. Run it locally, or feed it a diff: git diff main | canon review --diff-file -.