Recipes
Copy-paste workflow recipes for common Canon Action setups. Each recipe is self-contained — drop it into your .github/workflows/ directory and adjust to taste.
PR checks bundle
The recommended PR-time setup. Runs spec-lint and verify in parallel on every PR via the pr-checks.yml reusable workflow.
# .github/workflows/canon-pr.yml
name: Canon PR Checks
on:
pull_request:
branches: [main]
jobs:
canon:
uses: canonhq/canon/.github/workflows/reusable/pr-checks.yml@v1
with:
fail-on-lint: error
fail-on-verify: neverThe two underlying jobs (Spec Lint and Verify ACs) emit standard check-runs you can mark required in branch protection rules.
Tightening the gate
Once your specs are clean, tighten the verify threshold to fail PRs that introduce uncovered ACs:
with:
fail-on-lint: error
fail-on-verify: warning # any not_started or unknown blocks the mergePer-PR coverage delta comment
Post a sticky comment on every PR showing the base-vs-head coverage diff, broken out per spec.
# .github/workflows/canon-coverage-delta.yml
name: Canon Coverage Delta
on:
pull_request:
branches: [main]
jobs:
delta:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # need history to reach the base ref
- uses: canonhq/canon/actions/coverage-delta@v1The action posts a single comment with a magic marker and updates it in place on subsequent runs. To suppress the comment and rely on the step summary instead, set comment: false.
Weekly coverage report — markdown PR
Render a coverage snapshot to docs/coverage.md every Monday and open a PR with the changes. Reviewers see exactly what moved.
# .github/workflows/canon-coverage.yml
name: Canon Weekly Coverage
on:
schedule:
- cron: "0 9 * * 1" # Mondays at 09:00 UTC
workflow_dispatch:
jobs:
report:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/coverage-report@v1
with:
output: file
path: docs/coverage.md
commit-mode: prWeekly coverage report — rolling issue
If you'd rather track coverage in a single tracking issue than churn the repo with PRs, use output: issue. Each run updates the same issue in place.
name: Canon Weekly Coverage Issue
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch:
jobs:
report:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/coverage-report@v1
with:
output: issue
issue-title: "Canon weekly coverage"
issue-labels: "canon,coverage"Weekly drift audit — rolling issue
Detect drift between code and spec statuses every week. Updates a single tracking issue in place rather than spamming with new issues.
# .github/workflows/canon-audit.yml
name: Canon Weekly Audit
on:
schedule:
- cron: "0 9 * * 1" # Mondays at 09:00 UTC
workflow_dispatch:
jobs:
audit:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/audit@v1
with:
canon-token: ${{ secrets.CANON_TOKEN }}
mode: issue
rolling-issue-title: "Canon weekly audit"The first run opens an issue titled "Canon weekly audit" with a markdown breakdown of every recommendation. Subsequent runs update the same issue in place. To reset, close the existing issue and the next run will open a fresh one.
Slack coverage notification
Post a coverage summary to a Slack channel via incoming webhook. Use this when you want push notifications without polluting your repo.
name: Canon Coverage Notification
on:
schedule:
- cron: "0 9 * * 1"
jobs:
notify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/coverage-report@v1
with:
output: webhook
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}Set SLACK_WEBHOOK_URL as a repository secret pointing at a Slack incoming webhook for the destination channel.
Weekly audit bundle
Run the audit action and the coverage-report action together on a single weekly cron via the weekly-audit.yml reusable workflow:
# .github/workflows/canon-weekly.yml
name: Canon Weekly
on:
schedule:
- cron: "0 9 * * 1" # Mondays at 09:00 UTC
workflow_dispatch:
jobs:
canon:
uses: canonhq/canon/.github/workflows/reusable/weekly-audit.yml@v1
with:
audit-mode: issue
coverage-output: issue
secrets:
CANON_TOKEN: ${{ secrets.CANON_TOKEN }}Both jobs run in parallel and post to their respective rolling issues. To use webhook-based coverage notifications instead of an issue, set coverage-output: webhook and pass coverage-webhook-url.
Auto-generate release notes on tag
Update the GitHub Release body automatically with a markdown summary of every spec section that transitioned to done since the previous tag. Triggered on release: published.
# .github/workflows/canon-release-notes.yml
name: Canon Release Notes
on:
release:
types: [published]
jobs:
notes:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required to reach the previous tag
- uses: canonhq/canon/actions/release-notes@v1The action detects the previous tag automatically via git describe --tags --abbrev=0 HEAD^ and walks the spec tree at both refs to find completed sections.
CI-side ticket sync
Sync spec sections to GitHub Issues / Jira / Linear from your own workflow rather than via the Canon server proxy. Dry-run on PRs, apply on push to main.
# .github/workflows/canon-sync.yml
name: Canon Ticket Sync
on:
pull_request:
branches: [main]
paths:
- "docs/specs/**"
- "CANON.yaml"
push:
branches: [main]
paths:
- "docs/specs/**"
- "CANON.yaml"
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/sync@v1
with:
direction: both
env:
# GitHub Issues uses GITHUB_TOKEN automatically; for Jira or
# Linear, pass the secret here:
# JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }}
# LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}The action defaults to dry-run on PR triggers (with a sticky comment showing the would-be sync plan) and to apply mode on push triggers.
Scaffold a new spec via dispatch
Workflow_dispatch trigger that lets PMs and engineers scaffold a spec without learning the CLI. The action opens a PR with the new file ready for editing.
# .github/workflows/canon-new-spec.yml
name: Canon New Spec
on:
workflow_dispatch:
inputs:
title:
description: "Spec title"
required: true
type:
description: "Document type"
required: false
default: "spec"
type: choice
options: [spec, proposal, design, adr]
owner:
description: "Owner GitHub handle"
required: false
team:
description: "Owning team"
required: false
jobs:
scaffold:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/new-spec@v1
with:
title: ${{ inputs.title }}
type: ${{ inputs.type }}
owner: ${{ inputs.owner }}
team: ${{ inputs.team }}Generate a task plan from a spec
Pair the new-spec action with the plan action so authors can scaffold a spec, fill it in, then dispatch a plan that opens a tracking issue.
# .github/workflows/canon-plan.yml
name: Canon Plan
on:
workflow_dispatch:
inputs:
spec:
description: "Spec file path or substring (e.g. auth-hardening)"
required: true
assignees:
description: "GitHub handles to assign (comma-separated)"
required: false
jobs:
plan:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/plan@v1
with:
spec: ${{ inputs.spec }}
assignees: ${{ inputs.assignees }}Weekly stale spec check
Find specs whose realization files have churned but the spec hasn't been updated. Cheap git heuristic; pairs naturally with the audit action on the same schedule.
# .github/workflows/canon-stale.yml
name: Canon Stale Specs
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: canonhq/canon/actions/stale-spec-check@v1
with:
stale-days: 90
code-churn-threshold: 50The action handles the fetch-depth: 0 checkout automatically. To run alongside the audit + coverage-report bundle, add it as a third job in the same workflow file.
Weekly dedup hygiene
Detect duplicate tickets created by spec sync and report them via a rolling issue. Start in dry-run mode and switch to apply once trusted.
# .github/workflows/canon-dedup.yml
name: Canon Dedup
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch:
jobs:
dedup:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/dedup@v1
# apply: "true" # uncomment once you trust the dry-run outputMonthly compliance export
Generate a monthly audit trail of every AC across every spec. Uploads as a workflow artifact and (optionally) commits to a long-lived compliance/ branch for retention.
# .github/workflows/canon-compliance.yml
name: Canon Compliance Export
on:
schedule:
- cron: "0 9 1 * *" # 1st of every month at 09:00 UTC
workflow_dispatch:
jobs:
export:
runs-on: ubuntu-latest
permissions:
contents: write # only needed if commit-to-branch is set
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/compliance-export@v1
with:
format: json
commit-to-branch: compliance/audit-trail # optionalThe artifact is named canon-compliance-report by default and retained for 90 days.
Monthly canonhq upgrade
Auto-bump the pinned canonhq CLI version on a monthly cron, run migrations, and open a PR for review.
# .github/workflows/canon-upgrade.yml
name: Canon Monthly Upgrade
on:
schedule:
- cron: "0 9 1 * *"
workflow_dispatch:
jobs:
upgrade:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: canonhq/canon/actions/upgrade@v1The action auto-detects pyproject.toml or requirements.txt, queries PyPI for the latest version, runs canon db migrate, and opens a PR titled chore(canon): upgrade canonhq to <version>.
Bootstrap a new repo
Add Canon to a fresh repo on demand. Trigger via workflow_dispatch once and merge the resulting PR.
name: Bootstrap Canon
on:
workflow_dispatch:
inputs:
team:
description: "Team that owns this repo"
required: true
jobs:
setup:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon@v1
with:
team: ${{ inputs.team }}
ticket-system: githubLint-only on spec changes
Cheapest possible Canon hookup — only runs when a spec file changes, fails the build on parser errors:
name: Spec Lint
on:
pull_request:
paths:
- "docs/specs/**"
- "CANON.yaml"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/spec-lint@v1
with:
fail-on: errorCombining with your own jobs
Canon Actions slot into existing workflows alongside your tests and linters. Example: a single ci.yml that runs ruff, pytest, and Canon checks together.
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e .[dev]
- run: ruff check
- run: pytest
canon:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: canonhq/canon/actions/spec-lint@v1
with:
fail-on: error
- uses: canonhq/canon/actions/verify@v1
with:
fail-on: neverThe two jobs run in parallel and report independently — a Canon failure won't mask a test failure or vice versa.