Skip to main content

check-one-div-per-subfile.yml

Diff-scoped, warn-only check that flags a Quarto subfile holding more than one top-level div. The lab’s rule (Ezra, 2026-10-18) is that new and revised content gets one div per subfile. One exception (Ezra, 2026-10-23): a proof may share a file with its claim, and a solution with its exercise. That includes a proof wrapped in a .content-hidden or .content-visible div, which is how the lab hides a proof from slides. This implements gha#1092.

A subfile is a .qmd file whose name starts with _ and which sits under a _subfiles directory. The check reports a subfile that has more than one top-level fenced div when the pull request adds a line to it. The finding sits on the opening line of the second div.

The check does not count:

It accepts any _*.qmd file under a _subfiles directory and does not check the name form (the older _def-name.qmd or the newer _name_def.qmd).

A touched subfile with a div, code fence or HTML comment that is never closed is reported too, as an error when fail is true, because the divs after it would go uncounted without notice. A stray closing ::: hides nothing and is ignored. A file that matches globs but is not a subfile is not examined. The run prints a notice with how many such files it skipped, which includes any non-underscore .qmd file under _subfiles because the default glob matches those.

Scope

Only a subfile with a line a pull request adds is reported. A touched subfile counts as revised, so the whole file is parsed (a div opens far above the changed line) and a one-line fix to a file that still holds two claims is reported. An untouched subfile keeps its layout. An added line whose exact text was also deleted in the same diff counts as moved rather than new, and a blank added line is not content.

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 subfile in full on purpose, which measures how many existing subfiles hold several divs. Every run prints how many lines, subfiles and divs it examined.

Inputs

Input Type Default Description
globs string '*_subfiles/*.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 false Fail the workflow when a subfile holds more than one top-level div. The default gives warning annotations only.
whole-tree boolean false Scan every tracked subfile 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-one-div-per-subfile.yml in your repo.
# Diff-scoped check: flags a subfile (_subfiles/_*.qmd) that holds more than one
# top-level div when a PR adds a line to it. New and revised content gets one div
# per subfile (a proof may share a file with its claim, a solution with its
# exercise). Warn-only by default.
# Only contents:read is needed (the default), so no permissions block is required.
name: Check one div per subfile

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

jobs:
  check:
    uses: Morrison-Lab/gha/.github/workflows/check-one-div-per-subfile.yml@v3
    # with:
    #   globs: '*_subfiles/*.qmd'              # pathspecs to check (recursive)
    #   paths-ignore: 'chapters/outtakes/**'   # skip known-noisy paths
    #   fail: true                             # block instead of warn (defaults to false)
    #   whole-tree: true                       # scan every subfile, not just a PR's additions
    #   runs-on: private-runner                # self-hosted runner label (private repositories only)

See examples/check-one-div-per-subfile.yml for the full caller stub.