PR auto-labeling¶
.github/workflows/labeler.yml applies area labels (ci, docs,
nix, scripts, tests, security, pin, renovate) per
.github/labeler.yml globs. .github/labels.yml is the catalog of
record — canonical color and description source — for the area,
release-note (bug, enhancement, idea), and size/* labels. Some
labels stay out of that manifest deliberately: every label a workflow
hands to the notify-workflow-result composite is created on demand by
the composite, ci-watchdog.yml and
docs-audit-reminder.yml each create their own escalation label
(ci-watchdog, docs-audit) inline, reproducibility-check.yml
applies a reproducibility label that nothing in the tree creates, and
Renovate and update-flake-lock.yml both apply a dependencies PR
label that, like reproducibility, nothing in the tree creates.
Auto-labeling is not a required check — it is
cosmetic and must not block merge.
There is no label-sync workflow (one would require allowlisting
crazy-max/*); see Label bootstrap for the manual
gh label create --force loop.
.github/release.yml groups auto-generated release notes by the area
labels above plus enhancement, bug, and idea, with a * catch-all
collecting everything else.
Size labels¶
PRs are auto-labeled with size/XS, size/S, size/M, size/L, or
size/XL by the size job in .github/workflows/labeler.yml using
pascalgn/size-label-action. Thresholds (total changed lines, additions plus deletions):
| Label | Range |
|---|---|
size/XS |
0-9 |
size/S |
10-29 |
size/M |
30-99 |
size/L |
100-499 |
size/XL |
500+ |
Ignore-list parity¶
Generator-owned files (changelog, flake-outputs, derived reference docs,
fixture trees) are excluded from the size calculation so that
auto-regenerations do not inflate PR size. The ignore list is supplied
to pascalgn/size-label-action via the IGNORED env var inline in
the workflow file.
Ownership is declared at the generator, in the header annotation block
scripts/_script_docs.awk reads:
| Annotation | Meaning | On the ignore list |
|---|---|---|
@generates <path> |
the script owns that file's content | required |
@generates-block <path> |
the script splices a block into a hand-authored file | forbidden |
Most generators own their output file outright. The minority splice a
<!-- BEGIN x --> block into a doc that also carries hand-authored
prose: refresh-precommit-table.sh, refresh-pin-parity.sh,
refresh-just-recipes.sh, refresh-ci-summary.sh, and
refresh-ephemeral-refs-gap.sh. refresh-just-recipes.sh is on both
sides — it owns docs/reference/just-recipes.md and splices a block into
README.md. Which side of the line a doc falls on is therefore a
judgment: several @generates pages also splice between markers into a fixed
hand-written header — docs/architecture/ci-dag.md,
docs/reference/flake-outputs.md, docs/reference/just-recipes.md,
docs/reference/scripts.md, docs/reference/treefmt-config.md. Their
generators refuse to run when the marker is absent, so that header is preserved
rather than emitted; they are annotated @generates because the block is the
page, while the four files named below are annotated the other way because the
prose is. (docs/security/enforcement-matrix.md and
docs/reference/test-harnesses.md are the pure cases among the
@generates doc pages — their generators write the whole file, header
included.)
Recording it at the generator keeps that judgment reviewable
next to the code that makes it, where a coverage threshold would be a
magic number that moves as the prose around the block grows. Files whose
substance is hand-written prose around a block
(README.md, docs/architecture/auto-update.md,
docs/development/git.md, docs/development/linting.md) stay off the
list on purpose: ignoring them wholesale would also drop hand-edits to the
surrounding prose from the size count, which is the same failure in the
opposite direction.
Invariant: every @generates path is on the ignore list, no
@generates-block path is, and every remaining entry is one of the
exemptions scripts/check-size-label-ignores.sh names — CHANGELOG.md
(git-cliff renders it from commit history), flake.lock (nix writes
it), and tests/fixtures/** (fixture trees are authored as data, not
as reviewable change). Drift in one direction lets a regeneration mask
a legitimately-large change behind a spuriously-small size label; drift
in the other scores a hand-authored file's every edit at zero.
Label bootstrap¶
The five size/* labels are tracked in .github/labels.yml. The
repo does not currently sync that manifest to GitHub automatically;
labels are created manually with the loop below whenever a new
size/* label is added to that manifest (the same
gh label create --force pattern applies to the area and release-note
labels):
gh label create size/XS --force --color 009800 --description "0-9 lines changed"
gh label create size/S --force --color 7cfc00 --description "10-29 lines changed"
gh label create size/M --force --color fbca04 --description "30-99 lines changed"
gh label create size/L --force --color ff9800 --description "100-499 lines changed"
gh label create size/XL --force --color d73a4a --description "500+ lines changed"