labels: keep edited/reopened on issues; correct 0.3.0 adoption prose (#199 round)
Round fixes on #200. codex-1 (blocking): the issues narrowing dropped `edited`/`reopened`, but both carry a queue-state change an event uniquely carries — `edited` a body rewrite of the `Blocked by #N` declaration the sweep parses (issueflow-reconcile.sh:179), `reopened` a closed issue re-entering the queue. Dropping them tripped #199's must-fail. Narrow to `[opened, closed, edited, reopened]`, dropping only the churn/validation actions labeled/unlabeled/assigned/unassigned. Trigger tests now pin edited/reopened present and the four dropped; labels.test.sh exact-list updated. kimi (blocking): the "supersedes unreleased #144" prose was false — #144's edited/reopened shipped in 0.3.0. Dissolved: we now keep them. CONSUMERS prose rewritten to the real version history (0.2.0 #32 / 0.3.0 #144 / #199 narrows), and the #137 review-request line corrected from "unreleased" to shipped-in-0.3.0. kimi (non-blocking): reusable labels.yml comment no longer cites */15. codex-2 (AC1 after-measurement / closing) escalated to triage on #199 — held, not guessed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
155828a069
commit
0a812c4b19
6 changed files with 79 additions and 56 deletions
11
.github/workflows/labels.yml
vendored
11
.github/workflows/labels.yml
vendored
|
|
@ -10,11 +10,12 @@ name: labels
|
|||
# and reconcile checks out the BASE branch only. Keep it that way.
|
||||
#
|
||||
# There is no pull_request_review_target, so a review landing cannot wake this
|
||||
# workflow directly — and the */15 cron is advisory: GitHub deprioritises
|
||||
# short intervals hard enough that a quiet repo goes hours between ticks. The
|
||||
# handoff wakes the sweep itself: the author sets state:needs-human, and the
|
||||
# caller's `labeled` event confirms or corrects that optimistic write within
|
||||
# seconds. The cron stays as the last resort for a forgotten handoff.
|
||||
# workflow directly — and the caller's cron is a backstop, not the wake (#199
|
||||
# relaxed it from */15 to hourly): GitHub deprioritises short intervals anyway,
|
||||
# so a quiet repo goes a while between ticks. The handoff wakes the sweep
|
||||
# itself: the author sets state:needs-human, and the caller's `labeled` event
|
||||
# confirms or corrects that optimistic write within seconds. The cron stays as
|
||||
# the last resort for a forgotten handoff.
|
||||
#
|
||||
# This cannot loop: reconciler writes use GITHUB_TOKEN, and GitHub does not
|
||||
# create workflow runs from GITHUB_TOKEN-triggered events. Agent writes use a
|
||||
|
|
|
|||
20
.github/workflows/self-labels.yml
vendored
20
.github/workflows/self-labels.yml
vendored
|
|
@ -12,16 +12,20 @@ on:
|
|||
# billing floor, 52% of one private consumer's runs for near-zero real work.
|
||||
schedule: [{cron: "0 * * * *"}]
|
||||
workflow_dispatch: # bootstraps missing labels on a fresh repo
|
||||
# Narrowed to the two actions with promptness-critical reconcile behavior
|
||||
# (#199): `opened` drives the mint→needs-triage check (issueflow's opened-only
|
||||
# path), `closed` drives the blocker-closes→ready self-heal (crew#96/#98). The
|
||||
# dropped actions — labeled/unlabeled/assigned/unassigned/edited/reopened —
|
||||
# only feed validation and the 48h claim clock, which the hourly cron catches
|
||||
# within one cadence; labeled/unlabeled were also the dominant issues-churn
|
||||
# source. The PR handoff wake is pull_request_target:labeled, NOT issues, so
|
||||
# Narrowed (#199) to the actions that carry a queue-state change the hourly
|
||||
# cron cannot wait one cadence for — dropping only labeled/unlabeled/assigned/
|
||||
# unassigned, which feed validation and the 48h claim clock (caught within one
|
||||
# cadence) and were the dominant issues-churn source. Kept: `opened` (the
|
||||
# mint→needs-triage check, issueflow's opened-only path), `closed` (the
|
||||
# blocker-closes→ready self-heal, crew#96/#98), `edited` (a body rewrite of the
|
||||
# `Blocked by #N` declaration the sweep parses — issueflow-reconcile.sh:179),
|
||||
# `reopened` (a closed issue re-entering the queue wearing labels derived when
|
||||
# it closed). The must-fail in #199 is exactly "a queue-state transition waits
|
||||
# on the schedule when an event could have carried it", so edited/reopened stay
|
||||
# on events. The PR handoff wake is pull_request_target:labeled, NOT issues, so
|
||||
# this does not touch the handoff.
|
||||
issues:
|
||||
types: [opened, closed]
|
||||
types: [opened, closed, edited, reopened]
|
||||
pull_request_target:
|
||||
# Every PR arrives from a fork, so these carry the head/draft/review facts
|
||||
# the sweep derives state:* from. labeled/unlabeled are the handoff wake —
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@
|
|||
Events still carry every state change in seconds; the cron only backstops a
|
||||
forgotten handoff, so a full-board sweep is billed far less often without
|
||||
delaying any real transition past one cadence.
|
||||
- `labels` — the caller's `issues:` trigger narrows to `[opened, closed]`
|
||||
(#199), the two actions with reconcile behavior the cron cannot wait a
|
||||
cadence for. The churn actions — labeled/unlabeled/assigned/unassigned/
|
||||
edited/reopened — come off; the PR handoff wake is unaffected.
|
||||
- `labels` — the caller's `issues:` trigger narrows to
|
||||
`[opened, closed, edited, reopened]` (#199), the actions that carry a
|
||||
queue-state change the cron cannot wait a cadence for. The churn/validation
|
||||
actions — labeled/unlabeled/assigned/unassigned — come off; the PR handoff
|
||||
wake is unaffected.
|
||||
- `labels` — each caller trigger now carries a comment saying why it is
|
||||
subscribed, and reconcile keeps `cancel-in-progress: false` (#199) —
|
||||
cancelling a sweep mid-board is the race that guard exists to prevent.
|
||||
|
|
|
|||
|
|
@ -306,20 +306,21 @@ on:
|
|||
# Fork PRs; these carry the head/draft/review facts state:* derives from.
|
||||
# labeled/unlabeled are the handoff wake (state:needs-human confirmed here);
|
||||
# synchronize re-derives on every push. review_requested/review_request_removed
|
||||
# are unreleased — not in 0.2.0; add them with the pin bump to the first tag
|
||||
# carrying ceremony#137. They wake the sweep that clears blocker:unrequested
|
||||
# when the panel is asked.
|
||||
# (shipped in 0.3.0, ceremony#137) wake the sweep that clears
|
||||
# blocker:unrequested when the panel is asked.
|
||||
types: [opened, reopened, ready_for_review, converted_to_draft, synchronize, labeled, unlabeled, review_requested, review_request_removed]
|
||||
# Available at 0.2.0 and later (the first tag carrying ceremony#32); a
|
||||
# consumer pinned to 0.1.0 omits this block.
|
||||
issues:
|
||||
# Narrowed to the two promptness-critical actions (#199): opened → the
|
||||
# mint→needs-triage check, closed → the blocker-closes→ready self-heal. The
|
||||
# rest (labeled/unlabeled/assigned/unassigned/edited/reopened) only feed
|
||||
# validation and the 48h claim clock, which the hourly cron catches within
|
||||
# one cadence; labeled/unlabeled were the issues-churn source. The handoff
|
||||
# wake is pull_request_target:labeled, not issues, so this leaves it intact.
|
||||
types: [opened, closed]
|
||||
# Narrowed (#199) to the actions carrying a queue-state change the hourly
|
||||
# cron cannot wait one cadence for: opened → the mint→needs-triage check,
|
||||
# closed → the blocker-closes→ready self-heal, edited → a body rewrite of the
|
||||
# `Blocked by #N` declaration the sweep parses, reopened → a closed issue
|
||||
# re-entering the queue. Dropped: labeled/unlabeled/assigned/unassigned —
|
||||
# validation + the 48h claim clock, caught within one cadence, and
|
||||
# labeled/unlabeled were the issues-churn source. The handoff wake is
|
||||
# pull_request_target:labeled, not issues, so this leaves it intact.
|
||||
types: [opened, closed, edited, reopened]
|
||||
permissions:
|
||||
contents: read
|
||||
checks: read # mergeability/check-rollup read for PR state
|
||||
|
|
@ -340,29 +341,32 @@ needs all three explicit reads above; without them the failure appears as an emp
|
|||
The `issues:` trigger is available at `0.2.0` and later — `0.2.0` is the
|
||||
first tag carrying ceremony#32. A consumer pinned to `0.1.0` omits it. Adopt
|
||||
it only by bumping every ceremony reference to `0.2.0` or later; never mix
|
||||
refs to adopt it early. `0.2.0` shipped the broad type list
|
||||
(`labeled`/`unlabeled`/`assigned`/`unassigned` alongside `opened`/`closed`);
|
||||
ceremony#199 narrows it to `[opened, closed]` and relaxes the cron to hourly,
|
||||
so a consumer picks up the smaller trigger surface at the pin bump to the
|
||||
first tag carrying ceremony#199. The narrowing supersedes the unreleased
|
||||
`edited`/`reopened` additions (#144) rather than shipping them — those actions
|
||||
are gone from the caller, not deferred — while keeping the stub and ceremony's
|
||||
own caller byte-for-byte parity that #144 established. Only two `issues`
|
||||
actions survive because only two carry reconcile behavior the cron cannot wait
|
||||
one cadence for: `opened` drives the mint→`needs-triage` check, and `closed`
|
||||
drives the blocker-closes→`ready` self-heal.
|
||||
refs to adopt it early. The type list has grown then narrowed across tags:
|
||||
`0.2.0` (ceremony#32) shipped `[opened, labeled, unlabeled, assigned,
|
||||
unassigned, closed]`; `0.3.0` (ceremony#144) added `edited` and `reopened`;
|
||||
ceremony#199 narrows it to `[opened, closed, edited, reopened]` and relaxes the
|
||||
cron to hourly, so a consumer picks up the smaller trigger surface at the pin
|
||||
bump to the first tag carrying ceremony#199. The narrowing drops
|
||||
`labeled`/`unlabeled`/`assigned`/`unassigned` — validation and the 48h claim
|
||||
clock, which the hourly cron catches within one cadence, and `labeled`/
|
||||
`unlabeled` were the issues-churn source — while **keeping** #144's `edited`/
|
||||
`reopened`: those carry a queue-state change an event uniquely carries (a body
|
||||
rewrite of `Blocked by #N`, and a closed issue re-entering the queue), so the
|
||||
must-fail in ceremony#199 keeps them on events. `opened` drives the
|
||||
mint→`needs-triage` check and `closed` the blocker-closes→`ready` self-heal;
|
||||
the stub and ceremony's own caller stay byte-for-byte identical, the parity
|
||||
#144 established.
|
||||
|
||||
`pull_request_target` is intentional: fork PRs need the base repository's
|
||||
token to write labels. The reusable workflow executes no PR code. It checks
|
||||
out only the consumer's base branch and the pinned ceremony implementation.
|
||||
The #52 ruling invariants ride exactly these triggers — but the caller above
|
||||
is no longer the #18 shape, so adopting current triggers is a stub edit, not
|
||||
a bare pin bump. The one pending `pull_request_target:` edit is
|
||||
`review_requested` and `review_request_removed` (#137) — the wake that
|
||||
a bare pin bump. `review_requested` and `review_request_removed` on
|
||||
`pull_request_target:` shipped in `0.3.0` (ceremony#137) — the wake that
|
||||
clears `blocker:unrequested` the moment the panel is asked, without which a
|
||||
quiet repo wears that flag until the advisory cron. Adopt it with the pin bump
|
||||
to the first tag carrying ceremony#137 — never before it and never through
|
||||
mixed refs.
|
||||
quiet repo wears that flag until the backstop cron; a consumer picks them up
|
||||
by pinning `0.3.0` or later, never through mixed refs.
|
||||
|
||||
`.github/labels.conf` has one mandatory panel setting, one mandatory
|
||||
`triage-actors` setting, and then zero or more scope rows:
|
||||
|
|
|
|||
|
|
@ -64,12 +64,24 @@ check "stub cron is hourly" 0 '0 * * * *' grep -F 'cron:' "$STUB"
|
|||
check "stub cron line no longer fires */15" 1 "" \
|
||||
bash -c 'grep -F "cron:" "$1" | grep -qF "*/15"' _ "$STUB"
|
||||
|
||||
# ---- issues: is narrowed to the two promptness-critical actions (#199) -------
|
||||
# opened → mint→needs-triage; closed → blocker-closes→ready self-heal. The
|
||||
# churn actions must not reappear on the issues surface without a fresh why.
|
||||
# (labels.test.sh owns the exact-list and caller<->stub parity assertions; here
|
||||
# we name each dropped action so its return produces a #199-specific failure.)
|
||||
for churn in labeled unlabeled assigned unassigned edited reopened; do
|
||||
# ---- issues: is narrowed to the queue-state-changing actions (#199) ----------
|
||||
# Kept because each carries a queue-state change an event uniquely carries, so
|
||||
# dropping it would trip #199's must-fail (a transition waiting on the schedule
|
||||
# when an event could have carried it): opened → mint→needs-triage; closed →
|
||||
# blocker-closes→ready self-heal; edited → a body rewrite of the `Blocked by #N`
|
||||
# declaration the sweep parses; reopened → a closed issue re-entering the queue.
|
||||
# (labels.test.sh owns the exact-list and caller<->stub parity assertions.)
|
||||
for keep in opened closed edited reopened; do
|
||||
# shellcheck disable=SC2016 # the awk program runs in the nested bash, not here
|
||||
check "self caller issues surface keeps '$keep'" 0 "" \
|
||||
bash -c 'trigger_types() {
|
||||
awk -v key="^ issues:\$" "\$0 ~ key{f=1;next} f&&/^ types:/{sub(/^ types:[[:space:]]*/,\"\");print;exit} f&&/^ [a-z]/{exit}" "$1"
|
||||
}; trigger_types "$1" | grep -qw "$2"' _ "$SELF" "$keep"
|
||||
done
|
||||
# The churn actions must not reappear on the issues surface without a fresh why.
|
||||
# labeled/unlabeled were the dominant issues-churn source; assigned/unassigned
|
||||
# only feed validation and the 48h claim clock, caught within one cadence.
|
||||
for churn in labeled unlabeled assigned unassigned; do
|
||||
# shellcheck disable=SC2016 # the awk program runs in the nested bash, not here
|
||||
check "self caller issues surface drops '$churn'" 1 "" \
|
||||
bash -c 'trigger_types() {
|
||||
|
|
|
|||
|
|
@ -69,11 +69,12 @@ check "LABELS.md enumerates no repo's scope labels" 1 "0" \
|
|||
# same lists. review_requested/review_request_removed are the wake that
|
||||
# clears blocker:unrequested — the label sat false for as long as a quiet
|
||||
# repo stayed quiet because the one event that falsifies it was never
|
||||
# listed (#137). The issues list narrowed to [opened, closed] (#199): those
|
||||
# two carry reconcile behavior the hourly cron cannot wait one cadence for —
|
||||
# opened drives mint→needs-triage, closed drives the blocker-closes→ready
|
||||
# self-heal — while the churn actions (labeled/unlabeled/assigned/unassigned/
|
||||
# edited/reopened) came off. The stub is prose, so nothing but these rows
|
||||
# listed (#137). The issues list narrowed to [opened, closed, edited, reopened]
|
||||
# (#199): each carries a queue-state change the hourly cron cannot wait one
|
||||
# cadence for — opened drives mint→needs-triage, closed the blocker-closes→ready
|
||||
# self-heal, edited a body rewrite of the `Blocked by #N` line the sweep parses,
|
||||
# reopened a closed issue re-entering the queue — while the churn/validation
|
||||
# actions (labeled/unlabeled/assigned/unassigned) came off. The stub is prose, so nothing but these rows
|
||||
# keeps the lists from drifting: a type in one file only is a wake that fires
|
||||
# at home and nowhere in the fleet, or the reverse — the drift #144 caught.
|
||||
# The NF guard keeps `issues: write` under permissions: from matching the
|
||||
|
|
@ -100,8 +101,8 @@ check "caller and stub pull_request_target lists are identical" 0 "" \
|
|||
check "the caller lists both review-request wakes" 0 "" pr_has_both_review_wakes
|
||||
check "caller and stub issues lists are identical" 0 "" \
|
||||
types_in_sync issues "$CALLER" "$STUB"
|
||||
check "the caller lists exactly the two promptness-critical issue types" 0 \
|
||||
"types: [opened, closed]" event_types "$CALLER" issues
|
||||
check "the caller lists exactly the queue-state-changing issue types" 0 \
|
||||
"types: [opened, closed, edited, reopened]" event_types "$CALLER" issues
|
||||
# the failing cases: drop a type from either file, or reorder one list only,
|
||||
# and the identity rows above go red — exercised here on mutated copies
|
||||
mut_caller="$TMP/mut-caller.yml" mut_stub="$TMP/mut-stub.md"
|
||||
|
|
|
|||
Loading…
Reference in a new issue