From ce3bdcdef6e0898eb6a27032fabf0453ae0cba42 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:22:59 +0100 Subject: [PATCH] feat(labels): estate label tooling + auto-triage for new issues Ships the canonical label set and the classifier that labels newly-filed issues. Additive only: it never removes a label, never overrides a human's classification, stays silent when unsure, and never fails an issue. Also adds this repo's two new workflows to .github/workflows/actions.lock as '[]'. That lock is keyed by workflow path and refuses any workflow it does not list -- a startup_failure, which produces no check run and is therefore silent. `gh actions-lock` cannot add these: it records action versions, and both workflows deliberately use no actions. Co-Authored-By: Claude Opus 5 --- .github/label-classifier.json | 33 ++++++++------------- .github/workflows/label-triage.yml | 11 +++++-- .github/workflows/labels.yml | 47 +++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/.github/label-classifier.json b/.github/label-classifier.json index e1bd06cc..d349eaad 100644 --- a/.github/label-classifier.json +++ b/.github/label-classifier.json @@ -437,14 +437,6 @@ "dispatch", "self-heal" ], - "testing": [ - "fuzz", - "bench", - "property-based", - "coverage", - "test suite", - "proptest" - ], "performance": [ "latency", "throughput", @@ -452,17 +444,6 @@ "memory", "hot path", "regression" - ], - "documentation": [ - "readme", - "adoc", - "docs/", - "changelog", - "explainme", - "quickstart", - "wiki", - "docstring", - "doc tree" ] }, "keyword_type": { @@ -491,7 +472,14 @@ "docs", "readme", "adoc", - "prose" + "prose", + "docs/", + "changelog", + "explainme", + "quickstart", + "wiki", + "docstring", + "doc tree" ], "testing": [ "test", @@ -502,7 +490,10 @@ "crash-consistency", "linearizability", "equivalence", - "property-correspondence" + "property-correspondence", + "property-based", + "test suite", + "proptest" ], "bug": [ "broken", diff --git a/.github/workflows/label-triage.yml b/.github/workflows/label-triage.yml index b5379b4f..9886e920 100644 --- a/.github/workflows/label-triage.yml +++ b/.github/workflows/label-triage.yml @@ -103,7 +103,14 @@ jobs: fi printf 'applying: %s\n' "${apply[*]}" - gh issue edit "$NUM" -R "$GITHUB_REPOSITORY" \ - $(printf -- '--add-label %q ' "${apply[@]}") \ + # Build the arguments as an ARRAY. The previous form was an unquoted + # command substitution, so the shell re-split its output on spaces and + # a label name containing whitespace would arrive as several broken + # arguments. No canonical label contains a space today, which is + # exactly why this would have failed quietly the first time one did. + # (Also clears actionlint SC2046.) + edit_args=() + for lab in "${apply[@]}"; do edit_args+=(--add-label "$lab"); done + gh issue edit "$NUM" -R "$GITHUB_REPOSITORY" "${edit_args[@]}" \ || echo "label apply failed - not failing the run" exit 0 diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index d1f24dcb..c80b676c 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -36,6 +36,13 @@ jobs: - name: Apply canonical labels env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # ⚠ LOAD-BEARING. This workflow deliberately does not check the repo + # out (no `uses:`, so no actions.lock entry can drift), which means + # `gh label create` / `gh label edit` have no git remote to infer a + # target from. Without GH_REPO every mutation fails, and because the + # errors used to be discarded the step still exited 0 reporting + # "created=0 updated=0" -- a silent, estate-wide no-op. + GH_REPO: ${{ github.repository }} run: | set -uo pipefail work=$(mktemp -d); PAYLOAD=$work/labels.json @@ -46,7 +53,7 @@ jobs: [ -s "$PAYLOAD" ] || { echo "no .github/labels.json - nothing to do"; exit 0; } mapfile -t FROZEN < <(jq -r '.frozen[]' "$PAYLOAD") - created=0; updated=0; skipped=0 + created=0; updated=0; skipped=0; failed=0 existing=$(gh api "repos/$GITHUB_REPOSITORY/labels" --paginate \ --jq '.[] | [.name, .color, (.description // "")] | @tsv') @@ -55,20 +62,44 @@ jobs: [ -z "$name" ] && continue frozen=0 for f in "${FROZEN[@]}"; do [ "$f" = "$name" ] && frozen=1 && break; done - if [ "$frozen" -eq 1 ]; then skipped=$((skipped+1)); continue; fi cur=$(printf '%s\n' "$existing" | awk -F'\t' -v n="$name" '$1==n{print;exit}') if [ -z "$cur" ]; then - gh label create "$name" --color "$color" --description "$desc" >/dev/null 2>&1 \ - && created=$((created+1)) + # A MISSING label is created even when frozen. "Frozen" protects a + # label's DEFINITION from being renamed or recoloured -- it was + # never meant to stop the label existing. Skipping creation broke + # `security`, the one canonical label that is also frozen: it was + # absent from 10 of 12 sampled repos, and label-triage drops any + # label the repo does not define, so every `security` finding was + # silently discarded estate-wide. + if err=$(gh label create "$name" --color "$color" \ + --description "$desc" 2>&1 >/dev/null); then + created=$((created+1)); sleep 0.4 + else + echo " create failed: $name -- ${err:-unknown}"; failed=$((failed+1)) + fi else + # Present AND frozen: leave it exactly as it is. + if [ "$frozen" -eq 1 ]; then skipped=$((skipped+1)); continue; fi ccol=$(cut -f2 <<<"$cur"); cdesc=$(cut -f3- <<<"$cur") if [ "${ccol,,}" != "${color,,}" ] || [ "$cdesc" != "$desc" ]; then - gh label edit "$name" --color "$color" --description "$desc" >/dev/null 2>&1 \ - && updated=$((updated+1)) + if err=$(gh label edit "$name" --color "$color" \ + --description "$desc" 2>&1 >/dev/null); then + updated=$((updated+1)); sleep 0.4 + else + echo " edit failed: $name -- ${err:-unknown}"; failed=$((failed+1)) + fi fi fi - sleep 0.4 done < <(jq -r '.labels[] | [.name, .color, .description] | @tsv' "$PAYLOAD") - echo "created=$created updated=$updated frozen-skipped=$skipped" + echo "created=$created updated=$updated frozen-skipped=$skipped failed=$failed" + + # Fail ONLY on the misconfiguration shape: work was attempted, every + # attempt failed. That is the silent-no-op signature. A single flaky + # label must not turn the whole estate's CI red. + if [ "$failed" -gt 0 ] && [ "$((created + updated))" -eq 0 ]; then + echo "every label mutation failed - the sync did nothing. Check GH_REPO and token scope." + exit 1 + fi + exit 0