diff --git a/.agents/skills/pr-watch/SKILL.md b/.agents/skills/pr-watch/SKILL.md index b397ce9a..18ee787a 100644 --- a/.agents/skills/pr-watch/SKILL.md +++ b/.agents/skills/pr-watch/SKILL.md @@ -12,12 +12,14 @@ scripts/pr-watch [--expect ] [--timeout ] [--interval /dev/null 2>&1 && gh --version 2>/dev/null | grep -q '^gh version'; then - gh pr view "$PR" --json headRefOid,mergeable,mergeStateStatus,statusCheckRollup + gh pr view "$PR" --json state,headRefOid,mergeable,mergeStateStatus,statusCheckRollup else nix shell nixpkgs#gh --command gh pr view "$PR" \ - --json headRefOid,mergeable,mergeStateStatus,statusCheckRollup + --json state,headRefOid,mergeable,mergeStateStatus,statusCheckRollup fi } @@ -74,6 +75,11 @@ while :; do echo "pr-watch: head is $head, expected $EXPECT*" >&2 exit 2 fi + # A merged or closed PR reports UNKNOWN/UNKNOWN for good (improvise-1qaa). + pr_state="$(printf '%s' "$json" | jqf '.state')" + case "$pr_state" in + MERGED|CLOSED) echo "pr-watch: PR $PR is $pr_state, nothing to wait for" >&2; exit 5 ;; + esac if [ -z "$pinned" ]; then pinned="$head" elif [ "$head" != "$pinned" ]; then diff --git a/scripts/pr-watch-test.sh b/scripts/pr-watch-test.sh index 172d5717..cc2e9b25 100755 --- a/scripts/pr-watch-test.sh +++ b/scripts/pr-watch-test.sh @@ -153,6 +153,26 @@ run_case "the needs-gap before the required check registers is not a failure" 0 gap_state "$work/gap-never" last.json MERGEABLE BLOCKED "$not_yet" run_case "a required check that never registers is a timeout" 3 "$work/gap-never" +# improvise-9pfs. Here a failed REQUIRED check reports MERGEABLE/BLOCKED, not +# UNSTABLE. The table above holds only green rollups, so this red was unpinned. +gap_state "$work/blocked-red" last.json MERGEABLE BLOCKED \ + 'map(if .name == "Test and lint (host)" then .conclusion = "FAILURE" else . end)' +run_case "a failed required check under MERGEABLE/BLOCKED fails without waiting" 1 \ + "$work/blocked-red" --timeout 0 + +# improvise-1qaa. GitHub reports a merged or closed PR as UNKNOWN/UNKNOWN, so +# waiting on UNKNOWN slept the full timeout there. One poll, then exit 5. +for s in MERGED CLOSED; do + d="$work/ended-$s" + mkdir -p "$d" + printf '{"headRefOid":"aaaaaaaaaaaa","state":"%s","mergeable":"UNKNOWN","mergeStateStatus":"UNKNOWN","statusCheckRollup":%s}\n' \ + "$s" "$green_rollup" > "$d/last.json" + run_case "a $s PR ends the watch as no longer open" 5 "$d" --timeout 5 + polls="$(cat "$d/.poll" 2>/dev/null || echo 0)" + [ "$polls" = 1 ] && ok "a $s PR is read once, not waited on" \ + || bad "a $s PR is read once, not waited on (polls)" "$polls" 1 +done + # improvise-h3ni. The head must stay pinned across the UNKNOWN wait, or a # verdict about b is reported as a verdict about a. state "$work/unk-moved" 1.json '"UNKNOWN"' '"UNKNOWN"' "$green_rollup"