Uh oh!
There was an error while loading. Please reload this page.
fix(labels): the triage labels exist fleet-wide, and now a check says so (backend#2598) - #364
fix(labels): the triage labels exist fleet-wide, and now a check says so (backend#2598)#364LukasWodka wants to merge 4 commits into
Conversation
… so (backend#2598) A caller without its label is wired in exactly the way that does nothing, and nothing could tell. `caller-drift.py` asserts a repo CARRIES `customer-priority-bump.yml`; it says nothing about whether the labels that workflow fires on exist there -- and GitHub silently DROPS a template label the target repo lacks: no error, no annotation, no run. The issue is filed unlabelled, `bug-to-ready` never sees its label, and the card sits in `Backlog`. Measured 2026-08-27 over the 20 repos `repo-inventory.yml` declares: `design-system-v2`, `release-train` and `rfcs` had ZERO of the seven triage labels; `e2e-test-agent` had TWO of seven. The 26 missing labels were created out of band with the fleet's own names, colours and descriptions, so this lands green rather than as a red gate. `e2e-test-agent` was invisible to the ticket as filed, because that derived over the `work-type:*` PREFIX. Deriving a domain from a prefix rather than from the producers is CLAUDE.md rule 6's vocabulary gap -- committed by the ticket written to close it. So the check holds no label list: it parses this reusable's `*-label` input defaults and its `--add-label` writes, plus every `.github/ISSUE_TEMPLATE/*.yml` `labels:` entry, and takes its repo scope from the inventory. Add a template or a third label rule and the domain moves with it. Fails closed throughout: an unreadable inventory, workflow, template or repo, a zero-length derived domain, an empty fleet, and a stale matcher are each exit 2. A 403 is UNKNOWN, never "this repo has no labels". The exception sentence in `org-standards.md` is deleted rather than kept accurate by hand -- it named two repos while the inventory said three -- and this repo's CLAUDE.md is re-spliced from the canon by `standards-sync.py`'s own splice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit babb48c. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
…d#2598) Bugbot on .github#364, and it is right in the way this repo keeps finding: `stale_idiom` asked whether the strings `-label` and `--add-label` appeared anywhere in the workflow TEXT -- which a comment satisfies, including the comments the same PR added. The cost is specific, not stylistic. Rename the `*-label` inputs away and the backstop stays quiet; the caller family then silently loses `from:customer`, which NO template applies, so the derived domain shrinks and the check goes GREEN over a dead `bump` rule. That is the inert-verification shape backend#1729 catalogued, and `kanban-columns-check.py`'s `names_in` carries the same lesson from e2e#176. Both halves are now answered from the PARSED document, by the same functions the derivation itself uses, so no comment anywhere can feed either: * `label_inputs(doc)` -- the `workflow_call` inputs whose names end in `-label`. Also handles `on: push` parsing to a string rather than crashing. * `run_scripts(doc)` -- every `run:` body with shell comment lines stripped. `ADD_LABEL` matches over this, never over the file, so a commented-out `--add-label ghost` cannot put a name that exists nowhere under a fleet-wide assertion. Three new selftest cases, each pinning a direction the raw-text version got wrong: a `*-label` in a comment must not satisfy the guard (false green), an `--add-label` in a comment must not raise a finding (false red), and a commented-out `--add-label` must not enter the domain. Suite 32/32; mutation tier 25/25 caught, including a mutation that reverts each half to the exact raw-text shape Bugbot flagged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LukasWodka
commented
Aug 27, 2026
Bugbot finding addressed in e0cbc7d — and it was a real defect of the class this repo catalogues, not a nit.
Both halves are now answered from the parsed document, by the same functions the derivation itself uses, so no comment can feed either:
Three new cases, one per direction the raw-text version got wrong: The second exists because the two halves fail in opposite directions — the Evidence: suite 32/32; mutation tier 25/25 caught, including a mutation that reverts each half to the precise raw-text shape flagged here. |
…ackend#2598) The residual @LukasWodka recorded on .github#364. `caller_labels` returned `{"priority": ...}` from the `--add-label` idiom alone when the `*-label` inputs were gone -- a domain silently missing `from:customer`, which no template applies. Unreachable as a SILENT failure today only because `main()` calls `stale_idiom` before the sweep. "Unreachable via its sibling" is a weaker property than "refuses on its own", and it stops being true the day someone reorders `main()`. CLAUDE.md rule 7: a claim that something cannot happen belongs in a machine check where one fits, and here one fits in four lines. Suite 33/33; mutation tier 26/26, the new one deleting the refusal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both PRs added a runner to MUTATION_TARGETS and SELFTEST_TARGETS, so both lists conflicted. Resolved as the UNION rather than by picking a side: `selftests-cover` fails closed on any file under scripts/tests/ that no target names, so keeping one side would have made the other branch runner invisible AND reddened the coverage guard. Verified on the merged tree: selftests-cover 21 selftests, 11 mutation runners, all wired triage-labels 33/33 selftest, 26/26 mutations conflict-gate 115 assertions Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

What
caller-drift.pyasserts a repo carriescustomer-priority-bump.yml. It saysnothing about whether the labels that workflow fires on exist there — and GitHub
silently drops a template label the target repo lacks: no error, no annotation, no
run. So a repo can pass the conformance audit as fully wired while the bug-to-ready
rule cannot fire in it at all. The issue is filed unlabelled and the card sits in
Backlog, the refinement queue nobody pulls from.Measured before / after
Derived from
repo-inventory.yml(20 repos declarecustomer-priority-bump.yml: required, none exempt) — not from a hand-written list.design-system-v2release-trainrfcse2e-test-agente2e-test-agentis a fourth repo the ticket could not see. backend#2598 derivedover the
work-type:*prefix and reported three repos. That repo hadwork-type:bugandpriorityand none of the other five, so a prefix sweep called itcovered while four labels its templates apply were being dropped on the floor.
Deriving a domain from a prefix rather than from the producers is CLAUDE.md rule 6's
vocabulary gap — committed by the ticket written to close it. Two of the labels it was
missing (
from:customer, and the three template labels) mean thebumpjob wasequally dead there and in the other three; the ticket recorded only the bug half.
The labels were created out of band
26 labels created with
gh label create, plus one colour/description alignment(
e2e-test-agent'swork-type:bugwas GitHub's ad-hoc greyedededwith nodescription — the tell that it was created by hand rather than by the rollout). Names,
colours and descriptions were read from the fleet, not invented: 16 of 17
already-labelled repos agreed byte-for-byte on all seven.
That is deliberate sequencing, per arm while green: had the check landed first it
would have been red on four repos on its first run. The labels are a repo-settings
change and cannot travel in a PR, so this PR carries only the check and the prose.
No branch protection, ruleset or other repo setting was touched.
The check
scripts/triage-labels-check.py+.github/workflows/triage-labels.yml, in thecaller-drift/kanban-columnsaudit tier (selftest job gates the audit job; PRpaths on both producers, plus a daily cron — a label can be deleted in a repo's
Settings UI with no PR to hang a check on).
It holds no label list. The domain is derived from two producers:
customer-priority-bump.yml— everyworkflow_callinput whose name ends in-labelcontributes itsdefault, and every--add-label Xitsrun:blocksexecute contributes
X..github/ISSUE_TEMPLATE/*.yml— every entry of each template's top-levellabels:.Add a template, rename an input, or add a third label rule and the domain moves with
it. Deriving over the suffix rather than over two known input names is what makes
that true, and there is a case for it.
Fails closed. Exit 2 — never 0, and deliberately never 1 — on: an unreadable or
unparseable inventory / workflow / template; zero enrolled repos; a zero-length
derived domain in either family; a stale matcher (
--add-labelpresent but unmatched);and any repo whose label list cannot be read. A 403 is UNKNOWN, never "this repo
has no labels" — that conflation is the backend#1415 failure one layer along. A repo
answering 200 with an empty list is a successful read, and every label is then missing
by the ordinary path.
Evidence
make check— green (ruff, shellcheck, house-rules, action-pins, mint-scope,actionlint, mutations-dry, selftests-cover, 20 selftests):
make credential-scan:438 commits scanned. no leaks found.The live check, green across the fleet:
Mutation proof, two tiers.
make mutation-triage-labels— 21/21 caught, every anchor resolved exactlyonce. Every mutation edits
scripts/triage-labels-check.pyororg-standards.mdand the suite is re-run against them; there is no inline copy of the rule
(CLAUDE.md rule 9). Covered: an exempt repo audited as enrolled, an empty fleet
passing, the suffix derivation broken,
--add-labeldropped, each fail-closedrefusal softened,
audit()folding an unreadable repo into "complies", thecomparison inverted, and the canon renaming the label out from under the workflow.
The real thing, on a real repo. Deleted
needs-refinementfromtracebloc/rfcs, ran the live check, restored it:Prose this change makes false
org-standards.md:37— the exception sentence is deleted, not kept accurate byhand. It named two repos while the inventory said three, which is the argument the
reviewer on client#848 made: a hand-written exception list drifts on every entry, so
empty it rather than re-write it. This repo's
CLAUDE.mdis re-spliced from thecanon using
standards-sync.py's ownbuild_desired, so the block isIN_SYNC; theother 19 copies need a
standards-sync.ymlcreate-prsdispatch..github/workflows/customer-priority-bump.ymlheader — the block recording the livedefect now records the fix, the daily assertion, and the two derivation lessons.
Not done, stated rather than left implicit
permission-issues: read,org-scoped, no
repositories:narrowing. The evidence that the installation coversall twenty at that scope is
add-to-kanban.yml— a per-repo copy in every enrolledrepo minting exactly
permission-issues: read(backend#2166). An under-scoped tokenfails at the call, not at the mint, so the first scheduled run is the test. This is
an audit, not a required status context, so a red first run is a finding to fix that
day rather than a merge blocker.
today, but a wrong colour drops no label and blocks no automation.
from:customer's description is stale across all 21 repos: "auto-bumps Priorityto P1", while RFC-BACKEND-0008 D5 removed the
Priorityfield and the workflow addsa binary
prioritylabel. Replicated verbatim into the four new repos deliberately —a fleet that agrees on a wrong string is fixable in one sweep, four repos disagreeing
is not. Worth its own ticket.
repo-inventory.ymlwas not extended with a label family. That was the ticket'sopen question. Adding a property means a row for all 20 repos and passing
conformance-gate.ymlon an exact head sha every other merge invalidates — the samereason
customer-priority-bump.yml's own header gives for not splitting the reusable.A standalone sibling audit gets the assertion running now and does not stand in the
way of folding it in later.
Closes tracebloc/backend#2598
Note
Low Risk
New advisory CI audit and local test tooling; no changes to merge gates or runtime triage workflows beyond documentation comments.
Overview
Adds ongoing enforcement that every repo declaring
customer-priority-bump.ymlactually has the labels automation and issue templates depend on—closing the gap wherecaller-driftonly proved the workflow file existed while GitHub could still drop missing labels with no error.scripts/triage-labels-check.pyderives the required label set from two producers (no hard-coded list):customer-priority-bump.yml*-labeldefaults plus live--add-labelwrites (parsed YAML/shell, not comments), and.github/ISSUE_TEMPLATE/*.ymllabels:. It audits enrolled repos fromrepo-inventory.ymlviagh apiand fails closed (exit 2) on unreadable inputs, stale matchers, API failures, or gaps..github/workflows/triage-labels.ymlruns a selftest-gated fleet check on PRs touching producers/inventory/check code and on a daily cron (labels deleted in Settings have no PR trigger). It is an advisory audit tier likekanban-columns, not a required merge context.Makefile wires
selftest-triage-labelsandmutation-triage-labelsinto the existing selftest/mutation tiers.org-standards.md/CLAUDE.mddrop the drifting per-repo bug-label exception and document fleet-wide labels plus this guard.customer-priority-bump.ymlheader comments are updated to record wiring complete, label gap closed, and derivation lessons (work-type:*prefix sweeps miss partial coverage).Offline selftest (29 cases) and mutation harness (21 anchors) cover derivations, staleness guards (including parsed-vs-raw-text fixes from .github#364), and fail-open API paths.
Reviewed by Cursor Bugbot for commit f27a2e5. Bugbot is set up for automated code reviews on this repo. Configure here.