ceremony/.github/workflows/self-labels-sweep.yml
dan-claude-bot 45aa806207 labels: detach the reconcile sweep from PR-triggered runs (#209)
The sweep rode the same workflow run as the PR event that woke it, so
every displacement in the shared labels-reconcile queue recorded a
CANCELLED reconcile check on some PR — fake red CI that held review
requests. The reconcile + issueflow jobs move, unchanged, to a new
reusable labels-sweep.yml behind their own caller; labels.yml gains a
trigger job that dispatches the consumer's sweep caller with the plain
GITHUB_TOKEN (workflow_dispatch is a documented no-retrigger exemption)
on every event that used to run reconcile. A displaced sweep now cancels
on the Actions tab, attached to no PR; PR checks show scope + trigger.

Because every trigger-driven wake arrives as workflow_dispatch, the event
name alone no longer separates the operator's manual bootstrap from an
event-woken sweep: the sweep caller's bootstrap dispatch input does — the
trigger passes no, a bare manual dispatch defaults to yes. The sweep
reusable also takes pr_workflow_name, exported as SELF_WORKFLOW for the
#208 reconciler (harmless to earlier ones; zero file overlap with #208).

The trigger is deliberately loud: a pin bumped without the sweep caller,
its bootstrap input, or actions: write on the labels caller goes red at
the trigger job instead of silently never sweeping again — documented in
docs/CONSUMERS.md with the split stubs and the atomic-adoption note.

Refs #209

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 16:20:00 +00:00

46 lines
2.3 KiB
YAML

name: labels-sweep
# Ceremony's own sweep caller (#209) — self-labels.yml's detached half,
# wearing the same local-`uses:` deviation and the same warning: consumers
# must NEVER copy the local form (it rides main, unpinned — correct only
# for the repo that IS the source). Consumers write:
# uses: heavy-duty/ceremony/.github/workflows/labels-sweep.yml@<pinned-tag>
on:
# The consumer owns this cadence (#203). Hourly is the recommended default
# when no other engine drives board state: the cron is then the sweep's ONLY
# wake for four transition classes — a review verdict landing (there is no
# pull_request_review trigger on the labels caller), blocker:ci-red set or
# cleared (no check_suite/check_run/workflow_run), a blocker:conflict when
# ANOTHER PR merges under this one, and the time-based stale / 48h
# claim-reclaim. The labels caller's events carry the rest in seconds, one
# trigger-job dispatch away. Hourly trades ≤1h of latency on those four
# while cutting nominal scheduled sweeps from four an hour to one at
# GitHub's 1-minute billing floor. Do not delete the cron: it is their
# discovery path. If another engine writes some of those transitions, only
# the classes with no other writer bound the cadence; relax it only as that
# list shrinks.
schedule: [{cron: "0 * * * *"}]
# A manual full-board sweep. A bare dispatch (input default "yes") also
# bootstraps the taxonomy on a fresh repo — what dispatching the labels
# caller did before #209. The reusable's trigger job wakes this workflow
# with bootstrap=no on every board event — an event-woken sweep must not
# re-upsert ~20 labels each time — so declaring this input is part of the
# caller contract: a dispatch naming an undeclared input is refused, and
# the trigger job goes loudly red.
workflow_dispatch:
inputs:
bootstrap:
description: Bootstrap the label taxonomy before sweeping
type: choice
options: ["yes", "no"]
default: "yes"
permissions:
contents: read
checks: read # mergeability/check-rollup read for PR state
statuses: read # commit-status rollup read for PR state
issues: write
pull-requests: write
jobs:
sweep:
# pr_workflow_name keeps its default: ceremony's PR-facing caller is
# named `labels` (self-labels.yml).
uses: ./.github/workflows/labels-sweep.yml