… the coverage
`.github/labeler.yml` named `package: *` for four of the 40 directories under
`packages/`. Measured on PR #7621, which changes `packages/fields/src/index.tsx`:
it carries `package: core`, `package: components` and `tests` — no fields label.
A package whose PRs are never labelled is invisible to any sweep keyed on
`package: *`, and such a sweep returns a short, confident, wrong answer.
Enumerated the full population rather than patching the one instance: 16 of 40
directories drew no label at all. Fourteen get a `package: *` entry here;
`test-support` and `vscode-extension` are `private: true` and are left out with
their reason recorded in the new gate's exemption list. The 19 `plugin-*`
directories and `data-objectstack` already draw `plugin` and `data-adapter`, so
they are covered by their own families and get no second label.
The workflow gains `issues: write`. That is load-bearing: `pull-requests: write`
only attaches labels that already exist, and 12 of the 14 labels added here were
not in this repository's label registry. actions/labeler applies the whole set in
one `setLabels` call, so without the permission the first PR touching e.g.
`packages/auth` would get NO labels at all.
`scripts/__tests__/labeler-package-coverage.test.ts` pins three directions:
a package drawing nothing, a rule targeting a deleted directory, and the
workflow losing either permission. Nothing but the action itself reads this
config, so a gap in it has no symptom — which is how this one survived.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
Fixes #7746
What was measured, not assumed
The card named the part that was not measured — whether
fieldswas the only missingentry — and that was the job.
packages/*was enumerated against.github/labeler.ymlusing minimatch, the same matcher
actions/labeleritself uses, rather than ahand-rolled approximation.
Population: 40 directories under
packages/. Lit control:packages/core/src/index.tsdraws
package: core;packages/plugin-kanban/src/index.tsdrawsplugin+plugin: kanban;packages/data-objectstack/src/index.tsdrawsdata-adapter;README.mddraws nopackage: core. The query works, so the empty readings below mean something.Before this change the config named
package: *for four directories (core,types,react,components). Sixteen of forty drew no label at all, named:app-shell·auth·cli·collaboration·create-plugin·fields·i18n·layout·mobile·permissions·providers·react-runtime·runner·sdui-parser·test-support·vscode-extensionThe card's own instance reproduces independently: PR #7621 (merged 2026-09-05) changes
packages/fields/src/index.tsxandpackages/fields/src/__tests__/datetime-compact-style-7443.test.tsx,and carries exactly
package: core,package: components,tests— no fields label.What was fixed, and what was deliberately left out
Fourteen entries added, in the shape the file already uses
(
'package: NAME'/changed-files/any-glob-to-any-file: 'packages/NAME/**/*'):app-shell,auth,cli,collaboration,create-plugin,fields,i18n,layout,mobile,permissions,providers,react-runtime,runner,sdui-parser.After the change, re-running the same enumeration leaves two directories drawing
nothing — exactly the two exempted below.
Left out on purpose, each with its reason recorded in the gate's
UNLABELLED_BY_DESIGN:test-support@object-ui/test-supportisprivate: true— an internal test helper, never published, so it has no consumer-facing surface apackage: *sweep looks for.vscode-extensionobject-ui(private: true), not as an@object-ui/*npm package — outside the scope the label family names.Whether the maintainer wants unpublished packages in the
package: *taxonomy is aquestion this PR must not answer by itself; each is a one-line config addition plus
deleting its exemption row.
The 19
plugin-*directories anddata-objectstackget nopackage: *entry — theyalready draw
pluginanddata-adapter. This config runs three parallel families keyedon directory prefix, and giving a plugin a
package:label would invent a conventionbeside the existing ones. That is why the gate asserts "at least one label" rather than
"a
package: *label".The workflow permission is load-bearing, not a drive-by
.github/workflows/labeler.ymlgainsissues: write. Measured, not assumed:package: fieldsandpackage: app-shell, but not the other twelve labels thisconfig now names.
pull-requests: writeonly attaches labels that already exist. Creating one needsissues: write— stated in theactions/labelerv7 README ("Recommended Permissions")and in its
src/labeler.ts, which on a 403 raises "The workflow does not havepermission to create labels. Ensure the 'issues: write' permission is granted".
setLabelscall. So without thepermission the failure is not "one label missing" — the call fails and the PR receives
no labels at all, which would be strictly worse than the gap this PR closes.
The alternative (hand-creating twelve labels in the registry) is a server-side change no
PR can carry, and upstream sanctions this route explicitly.
The pin, shown red before green
scripts/__tests__/labeler-package-coverage.test.ts(6 tests). Nothing but the actionreads this config, so a gap in it has no symptom — a gate is the only witness. Each
mutation was confirmed on disk by marker count before the run, and each restore proved by
git hash-objectagainst the HEAD blob (restores pinned toHEAD, never a baregit checkout --, which restores from the index):package: fieldsentrygives every package under packages/ at least one labelissues: writefrom the workflowkeeps the labeler workflow able to create labels this config namesdesignerexemption at a name the config lacksnever keeps a packages/ rule pointing at a directory that does not exist+keeps both exemption lists honestglobToRegExpso it matches nothingdraws the expected label ... (lit control)+ 2 moreA4 is the control on the control: it proves the lit control is not vacuous. After restore,
Tests 6 passed (6),git diff HEAD --statempty.Known-inert, deliberately not fixed here
The same enumeration found the mirror direction already live: the
designerrule targetspackages/designer, whosepackage.jsonwas deleted in21396ca4d. It is carried as anamed exemption pointing at objectui#7771 (filed separately, along with two label-registry
drifts), so the gate is honest about it today and reddens the moment that card is resolved
either way. objectui#7771 remains open and is not addressed here.
Verification
All runs below are on tree
69c1810e2, this branch's only commit.pnpm exec vitest run scripts/__tests__/labeler-package-coverage.test.ts— 6 passed.pnpm exec vitest run scripts/__tests__/— 102 files, 3009 tests, all passed, on atree with 0 of 40 packages built (no
packages/*/dist). No pre-existing red observedin that build state.
node scripts/check-changeset-presence.mjs— "No source or published contract of areleased package changed in this range, so no changeset is owed." Nothing here is under
packages/*/src.actions/labeleronly runs onGitHub, so its real effect on this config will first be observable on this PR's own
Auto Label PRsrun. Everything above is a claim about the config and the workflow file,which is what the gate reads.
Generated by Claude Code