ci(fleet-status): page check runs; pull_request_target workflows refresh the lanes - #83
Conversation
…esh the lanes - The check-runs list was read as one page of 100. Every PR event adds Fleet status check runs at the head, so a busy head passes 100 and its push-time required checks drop off the page, sending fleet/verify back to pending. Paging is one pure helper, collectPages(), used for check runs and the list endpoints, with a test that finds a required check on page 2. - A pull_request_target run's checks land on the PR head (its head_sha is the PR's), so such a workflow can produce a required check. The status job now accepts workflow_run events from pull_request and pull_request_target runs, the list names PR triage, and the list test covers both kinds and the job's condition.
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: APPROVE — no blocking issues found in the live-head diff.
I reviewed the workflow event filter, the paginated check-run/status collection, and the focused regression coverage. The pagination helper terminates on the first short page and the newly paginated check-run request preserves the existing ordering and required-check evaluation. The workflow change includes PR-triage completions and admits both pull-request event types. Required CI is passing at fac49cc2bd61758e4f8c61094584be06b77ab573 (analyze, docker-build, and test).
What's good: the change centralizes the page-walking behavior in a small reusable helper and covers the key page-boundary cases, including a required check appearing on page two.
The fleet now labels PRs whose author answers their own reviews with `author-fixes`, and the label exists on the repository. The label-manifest check compares .github/labels.json with the live labels, so every PR's check failed until the manifest declares it too.
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (independent second read; the gating review is posted separately).
Verdict: no blocking issues at 1be3ec0. Paging and the pull_request_target widening are correct, and the new tests fail without the change.
Note: the ticket named head fac49cc. The PR has since moved to 1be3ec0 (chore(labels): declare author-fixes in the label manifest), so this read covers the live head 1be3ec0.
Boundaries (rebuilt from the diff)
collectPages at scripts/fleet-status.mjs:145-152, stop predicate if (rows.length < size) return out;
| Input | Behaviour | Pinned by |
|---|---|---|
| empty first page (0 rows) | returns [] after one call |
not directly. The same 0 < 100 branch runs on the empty third page in pager([100, 100]) |
| short first page (5) | one page, 5 rows | a short first page is the only page |
exactly size (100), then 1 |
reads page 2, 101 rows | 101 rows over two pages are all read |
| full last page (100, 100) | reads an empty page 3, 200 rows | a full last page reads one more, empty, page |
| required check only on page 2 | counted | a required check on page 2 counts |
check-runs body missing check_runs |
?? [] gives 0 rows, loop stops |
not tested (inline in the main block) |
| HTTP error on page n | gh() throws, the existing try marks it pending |
pre-existing behaviour, unchanged |
Row order across pages does not matter. runs is still sorted by id after collection (:257). statuses keep their .reverse() over the whole concatenation, so the last-state-wins order in requiredCiState does not change.
fleet-status.yml condition, contains(fromJSON('["pull_request","pull_request_target"]'), github.event.workflow_run.event):
pull_requestworks as before.pull_request_targetis now accepted. I checked this against a real run: the PR triage run 36096549203 on this PR hasevent=pull_request_target,head_sha=1be3ec0…andpull_requests=[83]. SoPR:resolves and its checks land on the PR head, as the body says.- Any other event is still dropped.
- Fork PR via
pull_request_target: still gated byhead_repository.full_name == github.repository, andreadFactsre-checksp.head.repo.full_nameanyway. The job checks outscripts/fleet-status.mjsfrom the default branch, not from the PR, so the wider trigger does not run PR code withstatuses: write.
Tests fail without the change
- The paging block imports
collectPages, which does not exist on base, so the whole file fails there. workflow_run lists "PR triage"fails against the base yml, which lacks'PR triage'.- The condition regex
/\["pull_request","pull_request_target"\]/fails against the base== 'pull_request'. - The
if (listed.length)guard does not make the condition check vacuous. An emptylistedwould already fail the per-workflowlisted.includes(...)checks for build/ci/CodeQL/labels.
PR body claims vs diff
- "one pure helper,
collectPages(), used for check runs and the list endpoints alike": true.ghAll(:220-222) and the check-runs read (:255-256) both go through it. - "accepts
workflow_runevents from both": true (yml:29-30). - "the
workflow_runlist names PR triage again": true (yml:18).
Info (non-blocking)
.github/labels.json:1adds{"color":"BFD4F2","description":"The PR's author answers its reviews; no fleet fix ticket","name":"author-fixes"}, but the PR body does not mention it. I checked it against the live label: color and description match exactly, it is in alphabetical order, andmanifest matches the repopasses at1be3ec0. It changes no behaviour, but a one-line mention in the body would keep the description accurate for the squash message.
What's good
- Pulling the stop rule into a pure, injectable helper removes the duplicate loop and makes the page-2 case testable without the network.
- The rewritten workflow-list test is simpler than the two-branch version it replaces and still guards the new rule.
Checked: the full diff (4 files, +42/−21), the surrounding readFacts/gh/requiredCiState code, pr-triage.yml's trigger, a live pull_request_target run payload, the live label, and gh pr checks (all required checks pass). I did not run the suite locally; CI test and self-test pass at this head.
SECOND READ: READY
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: approve — no blocking issues found.
I reviewed the live-head diff in .github/workflows/fleet-status.yml, scripts/fleet-status.mjs, and scripts/fleet-status.test.mjs. The shared collectPages() helper now reads through the first short page for both list endpoints and check runs; the new cases cover a second-page required check, a full final page followed by an empty page, and a short first page. The workflow filter admits both pull_request and pull_request_target completion events while retaining the repository and PR association guards. The label-manifest update matches the live author-fixes label.
What's good: required CI at 1be3ec0ae522f5bafc73f5a47a7e392604d2f48d is green (analyze (javascript-typescript), docker-build, and test). I did not run the local test suite, per review environment policy.
What does this PR do?
Two fixes to
scripts/fleet-status.mjsand its workflow. Both must be in place before thefleet/*contexts become required checks.fleet/verifygoes back to pending. Paging is now one pure helper,collectPages(), used for check runs and the list endpoints alike.pull_request_targetworkflows refresh the lanes too. Apull_request_targetrun's checks land on the PR head (the run'shead_shais the PR's), so such a workflow can produce a required check. The status job now acceptsworkflow_runevents from bothpull_requestandpull_request_targetruns, and theworkflow_runlist names PR triage again.How to test
node scripts/fleet-status.test.mjs. Paging tests: 101 rows over two pages, a full last page, a short first page, and a required check found on page 2. The list test requires everypull_requestandpull_request_targetworkflow to be listed, and checks that the status job's condition accepts both events.fleet/*statuses come from the copy already on the default branch.