Skip to main content

check-heading-titles.yml

Diff-scoped check that flags a clickbait heading. It fails by default, since agents ignore warnings (Ezra, 2026-11-02). A heading is a plain noun phrase that names its content (Ezra, 2026-11-03): it is not a question, and neither it nor a phrase after a colon begins with who, whom, whose, what, when, where, why, how or which. The rule text is in psw, “Subsection headings”.

The check reports two mistakes:

The check also reads the title of a fenced div or callout: the #### heading inside a definition, example or exercise div, or a title="..." attribute on the div’s opening fence.

The check does not count:

Scope

A finding is reported only when the heading line was added in the pull request, so existing headings are not swept. With no base-ref, or when the diff cannot be computed, the check skips with a warning instead of scanning the whole tree. Set whole-tree: true to scan every tracked page in full on purpose, which measures how many headings a corpus carries that break the rule.

Inputs

Input Type Default Description
globs string '*.qmd' Space-separated git pathspecs of tracked files to check (recursive by default).
paths-ignore string '' Comma- or newline-separated glob patterns (relative paths) to skip. Supports *, ? and recursive **.
fail boolean true Fail the workflow when an added heading is clickbait. Set false for warning annotations only.
whole-tree boolean false Scan every tracked page in full instead of only a PR’s added lines.
python-version string '3.x' Python version to set up.
runs-on string empty (CI_RUNNER variable, else ubuntu-latest) Runner label for the job. Use a self-hosted label only for a private repository; never for a public one, whose pull requests can run arbitrary code on it (#1167).

Permissions

Only contents: read (the default), so no permissions: block is needed.

Example

# Copy to .github/workflows/check-heading-titles.yml in your repo.
# Diff-scoped check: flags a clickbait heading when a PR adds it: a question, or a
# heading (or a phrase after a colon) that starts with who, whom, whose, what, when,
# where, why, how or which.
# Fails by default.
# Only contents:read is needed (the default), so no permissions block is required.
name: Check heading titles

on:
  push:
    branches:
      - main
  pull_request:
  workflow_dispatch:

jobs:
  check:
    uses: Morrison-Lab/gha/.github/workflows/check-heading-titles.yml@v3
    # with:
    #   globs: '*.qmd'                         # pathspecs to check (recursive)
    #   paths-ignore: 'chapters/outtakes/**'   # skip known-noisy paths
    #   fail: false                            # warn instead of block (defaults to true)
    #   whole-tree: true                       # scan every page, not just a PR's additions

See examples/check-heading-titles.yml for the full caller stub.