fix(labels): state:needs-human means a human could merge it right now #88

Merged
dan-claude-bot merged 5 commits from fix/labels-mergeability-aware into main 2026-07-20 17:02:57 +00:00
2 changed files with 28 additions and 31 deletions
Showing only changes of commit 108a944e1c - Show all commits

View file

@ -89,21 +89,21 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE
# replacement was still running, which is #136 again. # replacement was still running, which is #136 again.
# #
# So: date a run by when it BEGAN, discarding both spellings of absent # So: date a run by when it BEGAN, discarding both spellings of absent
# (null, and the zero sentinel), and fall back only if it never recorded a # (null, and the zero sentinel) and falling back only if it never recorded
# beginning. Deliberately `first` over the preference-ordered list and not # a beginning. NOT by the newest stamp of any kind: `max` compares the
# `max` of it: max mixes "when it started" with "when it ended", which are # completion of a finished run against the start of a live one, which are
# different quantities, so it is not an ordering on runs at all. A run # different quantities and not an ordering on runs. A run cancelled by the
# cancelled by the concurrency group does not stop the instant its # concurrency group does not stop the instant its replacement starts — the
# replacement starts — the runner has to receive the signal and wind down — # runner has to wind down — so predecessor.completedAt > successor.startedAt
# so predecessor.completedAt > successor.startedAt is the ordinary case # is the ordinary case, and `max` dated the dead predecessor newer than the
# (13s on the box#137 tip), and under max the dying predecessor # live run that replaced it, narrowing both failures above without closing
# out-dated its live replacement for the whole drain window. # them. The list is already in preference order, so `first` IS that rule.
# #
# An entry that carries no usable timestamp at all sorts LAST rather than # An entry that carries no usable timestamp at all sorts LAST rather than
# first — something we cannot date is most likely the thing just created, # first — something we cannot date is most likely the thing just created,
# and treating it as newest keeps an undateable in-flight run from being # and treating it as newest keeps an undateable in-flight run from being
# discarded in favour of a stale success. Every ambiguity here resolves # discarded in favour of a stale success. Every ambiguity resolves toward
# toward "not settled". # "not settled".
| [ (.statusCheckRollup // [])[] | [ (.statusCheckRollup // [])[]
| { ctx: [.workflowName // "", .name // .context // ""], | { ctx: [.workflowName // "", .name // .context // ""],
at: ([.startedAt, .createdAt, .completedAt] at: ([.startedAt, .createdAt, .completedAt]

View file

@ -291,26 +291,23 @@ expect "a finished re-run supersedes an earlier in-flight run" SUCCESS \
"$(rollup "[$(inflight_ build 2026-07-20T15:19:00Z),\ "$(rollup "[$(inflight_ build 2026-07-20T15:19:00Z),\
$(run_ build SUCCESS 2026-07-20T15:19:45Z)]" | checks_state)" $(run_ build SUCCESS 2026-07-20T15:19:45Z)]" | checks_state)"
# -- the DRAIN WINDOW. A run cancelled by the concurrency group does not stop # -- the wind-down window. A predecessor cancelled by the concurrency group
# the instant its replacement starts: the runner has to receive the signal # does not stop the instant its replacement starts, so its completion
# and wind down, so the predecessor's completion lands AFTER the successor's # routinely lands AFTER the successor's start — on box's aa5a6ba the
# start. On box#137's own tip that window was 13s (superseding run started # replacement started 15:19:38 and the run it cancelled finished 15:19:51.
# 15:19:38, the run it cancelled finished 15:19:51). `run_()` cannot express # Dating by "newest stamp of any kind" compares the dead run's completion
# it either — it carries no startedAt — so every fixture above spaces the # against the live run's start, which is not an ordering on runs, and the
# predecessor's completion safely before the successor's start, and the whole # predecessor wins. Every fixture above spaces completion before start, so
# window is invisible to them. This is why the run is dated by `first` of the # none of them can see it. run_() cannot express the overlap either — it
# preference-ordered stamps and not by `max` of them: max compares "when it # carries no startedAt — hence the explicit payloads.
# ended" against "when it began", which is not an ordering on runs, and the overlap_() { jq -n --arg n "$1" --arg o "$2" --arg s "$3" --arg c "$4" \
# dying predecessor out-dated its live replacement for the entire window. '{__typename:"CheckRun", workflowName:"ci", name:$n, conclusion:$o,
drained_() { jq -n --arg n "$1" --arg c "$2" --arg s "$3" --arg e "$4" \ startedAt:$s, completedAt:$c}'; }
'{__typename:"CheckRun", workflowName:"ci", name:$n, conclusion:$c, expect "a predecessor finishing after its replacement started is still older (CANCELLED)" PENDING \
startedAt:$s, completedAt:$e}'; } "$(rollup "[$(overlap_ scope CANCELLED 2026-07-20T15:19:00Z 2026-07-20T15:19:51Z),\
$(inflight_ scope 2026-07-20T15:19:38Z)]" | checks_state)"
expect "a predecessor still draining does not out-date its live replacement" PENDING \ expect "...and the same when it finished green — mid-flight is not mergeable" PENDING \
"$(rollup "[$(drained_ build CANCELLED 2026-07-20T15:19:29Z 2026-07-20T15:19:51Z),\ "$(rollup "[$(overlap_ build SUCCESS 2026-07-20T15:19:00Z 2026-07-20T15:19:51Z),\
$(inflight_ build 2026-07-20T15:19:38Z)]" | checks_state)"
expect "...and the same when the draining predecessor is green (the #136 shape)" PENDING \
"$(rollup "[$(drained_ build SUCCESS 2026-07-20T15:19:29Z 2026-07-20T15:19:51Z),\
$(inflight_ build 2026-07-20T15:19:38Z)]" | checks_state)" $(inflight_ build 2026-07-20T15:19:38Z)]" | checks_state)"
# -- the classifier feeds the state machine: a cancelled required check must # -- the classifier feeds the state machine: a cancelled required check must