Skip to content

feat: label PRs by zone and issues by area, and enforce both lists in CI - #202

Merged
qiuethan merged 1 commit into
stagingfrom
qiuethan/Workflows
Aug 16, 2026
Merged

feat: label PRs by zone and issues by area, and enforce both lists in CI#202
qiuethan merged 1 commit into
stagingfrom
qiuethan/Workflows

Conversation

@qiuethan

Copy link
Copy Markdown
Collaborator

What this changes

PRs get a zone: * label from the paths they touch; issues get area/* labels from a new Area dropdown in the issue forms. A CI check fails the build when either list drifts between the files that carry it.

Ports the label automation from UTMIST/UTMIST and settles the zone/area question docs/CODE-OWNERSHIP.md left open.

Why

docs/CODE-OWNERSHIP.md named the gap directly:

"The one real gap: five files hold the list and nothing checks they agree... A CI step that extracts the zones from zone_for() and from CODEOWNERS and diffs the two would catch it mechanically."

Both drift incidents so far trace to that. In August 2026 five service directories had no zone entry, collapsed into the services/* catch-all, and a PR spanning llm and meeting counted as a single zone and never warned.

On zones vs areas: they are different axes, but the deciding factor is cardinality. A file has exactly one zone — that is the entire basis of pr-zone-check nagging about multi-zone PRs. Areas are multi-valued: 26 of the 56 labelled issues in this repo already carry two or three. Collapsing them would make zones meaningless or areas unusable. So an issue is never given a zone, and a PR is never given an area.

Zone

.github · docs · scripts · root

This PR spans four zones and pr-zone-check will warn. It can't be split usefully: the enforcement (scripts/), the thing enforced (.github/), and the documentation of the rule (docs/, README.md, AGENTS.md, Makefile) have to land together or label-consistency fails on its own first run.

What's new

File
zone-label.yml + labeler.yml zone: * on a PR from changed paths
area-label-issues.yml area/* on an issue from the form's Area dropdown
pr-size-label.yml size/xssize/xl, ported unchanged
label-consistency.yml + scripts/check-labels.mjs Fails when either list drifts

ISSUE_TEMPLATE/*.md become issue forms so the Area dropdown is machine-readable. blocked-ready-automation.yml gains a parser for the form-rendered ### Blocked by section.

How to verify

make labels          # same check CI runs
node scripts/check-labels.mjs

Drift detection was tested by injecting eight failures into a copy of the tree — new service directory with no zone, missing CODEOWNERS line, dropped zone from the PR template, mistyped labeler glob, missing ! negation, missing area from the array, missing area from a form, removed escape option. All eight fail with an actionable message; the clean tree passes.

The labeler.yml globs were cross-checked against zone_for() on all 497 tracked files plus synthetic paths for services/other, packages/other and root — every path resolves to exactly one zone, identical under both mechanisms.

Compatibility

The blocker parser handles both shapes:

  • form: ### Blocked by#40, #42
  • legacy inline: Blocked by: #40, #42

Verified to produce identical results to the previous parser on all 17 existing issue bodies, so no issue changes blocked/ready state as a result of this PR. 20 parser unit tests cover CRLF, _No response_, and the section-is-last case.

Checklist

  • Branched off staging and targeting staging.
  • make labels clean; all workflow/form YAML parses.
  • Issue forms validated against GitHub's 21 documented issue-form validation errors.
  • Docs updated in this PR — CODE-OWNERSHIP.md, README.md, AGENTS.md, Makefile.
  • Python suites not run — this PR touches no Python.

Deployment notes

  • Labels created — 14 zone: * (BFD4F2), 5 size/* (UTMIST's colours), plus blocked/ready, which blocked-ready-automation referenced but which did not exist. Already applied to the repo.
  • Not yet enforcinglabel-consistency is not a required status check. It is deliberately un-path-filtered so it can become one: GitHub creates no check run for a workflow skipped by a paths: filter, so a required check would wait forever.

Anything you're unsure about

None of this has executed on GitHub yet, and the UTMIST originals it derives from have never run either (added 2026-08-14, 0 runs). pr-size-label.yml is the exception — 6 successful upstream runs, copied unchanged.

Because of how GitHub resolves workflow files, only label-consistency can prove itself on this PR. zone-label and pr-size-label use pull_request_target (workflow read from the base branch), area-label-issues uses issues (read from the default branch), and the issue forms themselves are read from the default branch — so those four are only exercised after this merges. Worth a throwaway issue and one small PR immediately afterwards.

The labeler.yml globs are the piece I could verify least directly: the cross-check above models minimatch rather than running it.

🤖 Generated with Claude Code

Ports the label automation from UTMIST/UTMIST and settles the zone/area
split that docs/CODE-OWNERSHIP.md left open.

Zones label PRs, areas label issues. The two are different axes and, more
importantly, different cardinality: a file has exactly one zone (that is
what pr-zone-check nags about), while 26 of the 56 labelled issues in this
repo already carry two or three areas. Collapsing them would make zones
meaningless or areas unusable, so an issue is never given a zone and a PR
is never given an area.

New automation:
  zone-label.yml       zone: * on a PR, from the changed paths
  area-label-issues.yml area/* on an issue, from the form's Area dropdown
  pr-size-label.yml    size/xs .. size/xl, ported unchanged
  label-consistency.yml + scripts/check-labels.mjs

check-labels.mjs closes the gap CODE-OWNERSHIP.md named: the zone list is
copied across five files and the area list across four, and nothing
checked they agreed. Both drift incidents so far traced to that. It also
probes one path per zone through labeler.yml's globs, so a catch-all
bucket missing a `!` negation (which double-labels) or a mistyped glob
(which labels nothing) fails the build rather than looking correct.

The three issue templates become issue forms so the Area dropdown can be
read mechanically. blocked-ready-automation.yml gains a parser for the
form-rendered "### Blocked by" section; the pre-forms inline shape still
works, and produces identical results on all 17 existing issue bodies.

Not enforcing yet: label-consistency is not a required status check.
Deliberately un-path-filtered so it can become one — GitHub creates no
check run for a workflow skipped by a paths filter, which would leave
required checks waiting forever.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@qiuethan
qiuethan merged commit 9ca1ead into staging Aug 16, 2026
14 checks passed
@qiuethan
qiuethan deleted the qiuethan/Workflows branch August 16, 2026 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant