Problem
The tend-review run for a new PR triggers on pull_request_target: opened and runs in parallel with the tests workflow. Its Step 6 (CI monitoring) polls gh pr checks <number> --required in a loop and exits as soon as no required check is still pending/queued/in_progress.
In this repo, the only status context that reports as "required" at polling time is pre-commit.ci - pr. The branch's real required omnibus, check-ok-to-merge (defined in tests.yaml with if: always() and a long needs: list), is not registered as a check-run until its dependencies start completing. So the review's polling loop sees only pre-commit.ci - pr, watches it pass in a few minutes, and exits reporting "CI passed" while test-rust and siblings are still running.
Evidence
Most recent occurrence: PR #5807 (rust toolchain 1.93.1 → 1.94.1).
- 11:12:32Z — tend-review run 24562181409 started.
- 11:17:10Z — review session ended. Session JSONL shows
gh pr checks 5807 --required returned only pre-commit.ci - pr; check-ok-to-merge was not in the response (0 occurrences in the tool-result log vs. 3 for pre-commit.ci - pr).
- 11:18:59Z — first
test-rust failure (a new clippy::manual_saturating_arithmetic lint in Rust 1.94).
- 11:40:35Z —
check-ok-to-merge check-run finally registered on commit 8ff52aff, 23 minutes after the review had finished.
The bot's review therefore signalled "all required checks passed" while the PR was actually broken. A human maintainer had to flag it; reconstructing the root cause took 4 subsequent tend-mention runs on the PR thread.
Proposed fix
Add a short section to .claude/skills/running-tend/SKILL.md telling future bot sessions on this repo not to use gh pr checks --required in the review monitoring loop. Poll without --required so the loop waits for every in-flight tests job to finish.
--- a/.claude/skills/running-tend/SKILL.md
+++ b/.claude/skills/running-tend/SKILL.md
@@ -15,3 +15,32 @@
- Main CI workflow: `tests` (watched by tend-ci-fix)
- Dependency management: Dependabot (tend-weekly is disabled)
- Automerge: `pull-request-target.yaml` auto-merges single-commit `prql-bot` PRs
once CI passes
+
+## CI monitoring after review
+
+When polling CI after submitting a review (Step 6 of `tend-ci-runner:review`),
+**do not use `gh pr checks --required`** on this repo. The required check
+`check-ok-to-merge` is an `alls-green` omnibus defined in `tests.yaml` with
+`if: always()` and a long `needs:` list; its check-run is not registered
+until its dependencies (`test-rust`, `test-js`, etc.) complete. `tend-review`
+runs triggered by `pull_request_target: opened` run in parallel with `tests`,
+so at polling time `gh pr checks --required` sees only `pre-commit.ci - pr`;
+the loop exits as soon as it passes and misses in-flight test failures.
+
+Poll without `--required` instead:
+
+```bash
+for i in $(seq 1 15); do
+ sleep 60
+ gh pr checks <number> 2>&1 | grep -v "/runs/$GITHUB_RUN_ID/" \
+ | grep -q 'pending\|queued\|in_progress' || {
+ gh pr checks <number>
+ exit 0
+ }
+done
+echo "CI still running after 15 minutes"
+exit 1
+```
+
+The wider poll catches every `tests` job; budget up to 15 minutes since the
+full `tests` matrix runs longer than `pre-commit.ci - pr` alone.
I can't apply this myself — the CI sandbox marks .claude/skills/ as a sensitive path and blocks writes from both the Edit tool and Bash, even in a worktree under $TMPDIR. Same block was noted in #5802 for a different skill update; that diff ended up needing a maintainer to land it too.
Gate assessment
- Evidence level: Critical (wrong outcome — bot signalled CI passed while PR was broken).
- Structural: Yes — any
pull_request_target: opened-triggered review on this repo will see the same race.
- Occurrences: 1 this cycle; Critical + structural passes Gate 1 at 1.
- Magnitude: Targeted addition (~29 lines in an existing skill file).
Problem
The
tend-reviewrun for a new PR triggers onpull_request_target: openedand runs in parallel with thetestsworkflow. Its Step 6 (CI monitoring) pollsgh pr checks <number> --requiredin a loop and exits as soon as no required check is stillpending/queued/in_progress.In this repo, the only status context that reports as "required" at polling time is
pre-commit.ci - pr. The branch's real required omnibus,check-ok-to-merge(defined intests.yamlwithif: always()and a longneeds:list), is not registered as a check-run until its dependencies start completing. So the review's polling loop sees onlypre-commit.ci - pr, watches it pass in a few minutes, and exits reporting "CI passed" whiletest-rustand siblings are still running.Evidence
Most recent occurrence: PR #5807 (rust toolchain 1.93.1 → 1.94.1).
gh pr checks 5807 --requiredreturned onlypre-commit.ci - pr;check-ok-to-mergewas not in the response (0 occurrences in the tool-result log vs. 3 forpre-commit.ci - pr).test-rustfailure (a newclippy::manual_saturating_arithmeticlint in Rust 1.94).check-ok-to-mergecheck-run finally registered on commit8ff52aff, 23 minutes after the review had finished.The bot's review therefore signalled "all required checks passed" while the PR was actually broken. A human maintainer had to flag it; reconstructing the root cause took 4 subsequent
tend-mentionruns on the PR thread.Proposed fix
Add a short section to
.claude/skills/running-tend/SKILL.mdtelling future bot sessions on this repo not to usegh pr checks --requiredin the review monitoring loop. Poll without--requiredso the loop waits for every in-flighttestsjob to finish.I can't apply this myself — the CI sandbox marks
.claude/skills/as a sensitive path and blocks writes from both the Edit tool and Bash, even in a worktree under$TMPDIR. Same block was noted in #5802 for a different skill update; that diff ended up needing a maintainer to land it too.Gate assessment
pull_request_target: opened-triggered review on this repo will see the same race.