actions/* + lib/* + CHANGELOG — merge upstream 0.6.0 onto the forge tree, and port every gh call site it brought (#198) #204
2 changed files with 48 additions and 16 deletions
|
|
@ -469,28 +469,56 @@ deliverable_keys() { # title on stdin -> its deliverable keys, one per line
|
||||||
# segment: `TRIAGE.md + RELEASES.md` carries both keys.
|
# segment: `TRIAGE.md + RELEASES.md` carries both keys.
|
||||||
local segments=()
|
local segments=()
|
||||||
IFS='+' read -r -a segments <<<"$prefix"
|
IFS='+' read -r -a segments <<<"$prefix"
|
||||||
for segment in "${segments[@]}"; do
|
# An issue answers a SET of keys, never a multiset. Normalization is
|
||||||
key="$(deliverable_key "$segment")"
|
# many-to-one by design — `issueflow-reconcile.sh + issueflow-reconcile.test.sh`
|
||||||
[ -z "$key" ] || printf '%s\n' "$key"
|
# is one deliverable spelled twice, which is exactly the `+` shape D2 wrote
|
||||||
done
|
# the segment rule for — and a repeated key makes `collision_flags`' scan
|
||||||
|
# find the issue adjacent to itself, chaining it to its own number: the
|
||||||
|
# comment would ask #402 to declare `Blocked by #402`. It corrupts the chain
|
||||||
|
# between two such issues too, since each contributes two rows to one key.
|
||||||
|
# Deduping here rather than in the index keeps the set property with the
|
||||||
|
# function whose contract it is.
|
||||||
|
{ for segment in "${segments[@]}"; do
|
||||||
|
key="$(deliverable_key "$segment")"
|
||||||
|
[ -z "$key" ] || printf '%s\n' "$key"
|
||||||
|
done
|
||||||
|
} | awk '!seen[$0]++'
|
||||||
}
|
}
|
||||||
|
|
||||||
collision_in_scope() { # $1 = comma-joined labels -> 0 in the collision set
|
unblocked_claimable() { # $1 = comma-joined labels -> 0 when the issue is unblocked
|
||||||
|
# THE one definition of `unblocked`, because #293 gives both flags one word
|
||||||
|
# and one gloss on it: D2 as corrected reads "`unblocked` means open and not
|
||||||
|
# `blocked` — carrying `ready` or `claimed`, with or without an open PR",
|
||||||
|
# and D3b's first line says D3 uses D2's corrected `unblocked` and names the
|
||||||
|
# domain as the claimable set. Two spellings of one spec word is how the
|
||||||
|
# flags came to disagree about `needs-triage`, so there is one predicate and
|
||||||
|
# both flags call it.
|
||||||
|
#
|
||||||
# `blocked` is out: a chained issue is the GOAL state of #288's rule, and
|
# `blocked` is out: a chained issue is the GOAL state of #288's rule, and
|
||||||
# flagging it would report the fix as the defect. `epic` and `post-merge`
|
# flagging it would report the fix as the defect. Anything else without
|
||||||
# are out by #288 D6 — neither is picked by a builder — and they carry no
|
# `ready` or `claimed` is out because it is not claimable — `needs-triage`
|
||||||
# queue label to admit them here anyway.
|
# and a label-less issue are not states a builder can pick up, and an
|
||||||
|
# unlabeled one is getting `needs-triage` from this very pass. `epic` and
|
||||||
|
# `post-merge` are out by #288 D6 and #292 D1 alike — neither is picked by a
|
||||||
|
# builder — and they carry no queue label to admit them here anyway.
|
||||||
case ",$1," in *,blocked,*) return 1 ;; esac
|
case ",$1," in *,blocked,*) return 1 ;; esac
|
||||||
case ",$1," in *,ready,*|*,claimed,*) return 0 ;; esac
|
case ",$1," in *,ready,*|*,claimed,*) return 0 ;; esac
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collision_in_scope() { # $1 = comma-joined labels -> 0 in the collision set
|
||||||
|
unblocked_claimable "$1"
|
||||||
|
}
|
||||||
|
|
||||||
window_in_scope() { # $1 = comma-joined labels -> 0 subject to the window rule
|
window_in_scope() { # $1 = comma-joined labels -> 0 subject to the window rule
|
||||||
# `epic` and `post-merge` are outside the claimable set and exempt by name
|
# The same `unblocked`, not a second reading of it. Excluding only
|
||||||
# (#292 D1); `blocked` is already placed behind something and is what the
|
# `blocked`/`epic`/`post-merge` here admitted `needs-triage` and a
|
||||||
# non-member leg of the mint-time call writes.
|
# label-less issue, which left the sweep adding `needs-triage` to an
|
||||||
case ",$1," in *,blocked,*|*,epic,*|*,post-merge,*) return 1 ;; esac
|
# unlabeled issue and then, in the same pass, telling it about a membership
|
||||||
return 0
|
# call made at mint time. Neither is claimable; #292's invariant is stated
|
||||||
|
# over the claimable set (D3b), and its exemptions say why — `epic` and
|
||||||
|
# `post-merge` are exempt *because neither is claimable*.
|
||||||
|
unblocked_claimable "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
collision_key_index() { # board records on stdin -> "key<TAB>number" in scope
|
collision_key_index() { # board records on stdin -> "key<TAB>number" in scope
|
||||||
|
|
@ -760,7 +788,11 @@ same parse every \`blocked\` issue is gated on, echoed on that issue.
|
||||||
|
|
||||||
*Comment only: nothing on this path writes a label or changes a state. The
|
*Comment only: nothing on this path writes a label or changes a state. The
|
||||||
marker carries the window itself, so an unchanged one never re-posts.*" >/dev/null
|
marker carries the window itself, so an unchanged one never re-posts.*" >/dev/null
|
||||||
log "#$n: window flag — a ready non-member under $state"
|
# "unblocked", not "ready": the flag fires on `claimed` too, PR in
|
||||||
|
# flight or not, which is the one wording #293 D3b went out of its way
|
||||||
|
# to correct. The log line is read by a human deciding whether the
|
||||||
|
# sweep understood the board, so it says what the predicate says.
|
||||||
|
log "#$n: window flag — an unblocked non-member under $state"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2218,8 +2218,8 @@ check "the morning board draws exactly three collision flags" 0 "3" \
|
||||||
flag_count collision "$morning_out"
|
flag_count collision "$morning_out"
|
||||||
# D3's corpus: the six `ready` non-members that raced the emptying gate.
|
# D3's corpus: the six `ready` non-members that raced the emptying gate.
|
||||||
for nonmember in 257 264 266 276 281 284; do
|
for nonmember in 257 264 266 276 281 284; do
|
||||||
check "#$nonmember is flagged as a ready non-member under #249" 0 \
|
check "#$nonmember is flagged as an unblocked non-member under #249" 0 \
|
||||||
"issueflow: #$nonmember: window flag — a ready non-member under #249" \
|
"issueflow: #$nonmember: window flag — an unblocked non-member under #249" \
|
||||||
printf '%s\n' "$morning_out"
|
printf '%s\n' "$morning_out"
|
||||||
done
|
done
|
||||||
check "the morning board draws exactly six window flags" 0 "6" \
|
check "the morning board draws exactly six window flags" 0 "6" \
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue