potiuk opened a new pull request, #75616:
URL: https://github.com/apache/airflow/pull/75616

   A pull request gated to fork CI (AIP-120) shows one aggregate `Fork CI / 
Tests (AMD)` status, so to see which jobs are running, which failed and which 
passed, a maintainer or the contributor has to open the run in the fork. This 
reports every job of the fork run as its own commit status on the pull request, 
each linking to the job in the fork, the way project CI shows its jobs.
   
   ## What it does
   
   - The fork CI monitor lists the jobs of the fork run it already tracks (`GET 
/repos/<fork>/actions/runs/<id>/jobs?filter=latest`) and posts one status per 
job as `Fork CI job / <job name>`: yellow while queued or running, green when 
passed, red when failed, timed out, cancelled or unable to start. Skipped jobs 
and the jobs every run has (`Fork CI gate`, `Check if the run is needed`, 
`Platform: AMD`, `CI passed`) are left out.
   - Only differences from the head commit's current statuses are posted, 
failures first. Queued and running share one status, so a job costs at most two 
writes.
   - Once every job of a completed run is reported, the run (`<run 
id>:<attempt>:<conclusion>`) is recorded in the state comment's marker, so 
later monitor runs don't read its jobs again. A run in progress is read on 
every monitor run, and a re-run in the fork is read again because its attempt 
changes.
   - A job context left over from an earlier run of the same commit, which the 
latest completed run doesn't have, is overwritten as `success` with "Not in the 
latest fork run - see Fork CI / Tests (AMD)", since commit statuses can't be 
deleted and have no neutral state.
   - The job statuses are informational. Merging still depends on `CI passed` 
and the state machine on `Fork CI / Tests (AMD)`; neither changes.
   
   ## Safety
   
   - Job names come from the contributor's workflow, so contexts get their own 
`Fork CI job / ` prefix. No job can post the aggregate status the reconciler 
reads its decisions back from, or `CI passed`, which the merge ruleset 
requires. Non-printable characters are replaced, names are shortened to 100 
characters, and contexts are grouped case-insensitively as GitHub compares 
them, keeping the worst result.
   - Reporting job statuses is caught on its own: a failure there is listed for 
Slack, and the pull request is still classified, so a `CI passed` that no 
longer holds is still revoked.
   - `read_head_status` now reads every page of the combined status, because 
with job statuses a commit can carry more than 100 contexts.
   
   ## Write budget, and turning it off
   
   GraphQL has no mutation for commit statuses (it can batch check runs, but a 
check run created by a workflow links to that workflow's run, not to the fork 
job), so each status is one REST write, counted against GitHub's limit of 500 
content-creating requests an hour that every workflow of `apache/airflow` 
shares.
   
   Measured on 2026-10-25 from `apache/airflow`'s own `Tests (AMD)` PR runs 
(the same workflow runs in forks):
   
   | Pull request | Run | Started jobs | Reported job statuses |
   |---|---|---|---|
   | Breeze test fix | 
[40138784120](https://github.com/apache/airflow/actions/runs/40138784120) | 11 
| 7 |
   | Documentation fix | 
[40169750323](https://github.com/apache/airflow/actions/runs/40169750323) | 16 
| 12 |
   | Google provider change | 
[40158907296](https://github.com/apache/airflow/actions/runs/40158907296) | 43 
| 39 |
   | CI change (full tests) | 
[40207881565](https://github.com/apache/airflow/actions/runs/40207881565) | 131 
| 127 |
   
   `apache/airflow` had 1898 `Tests (AMD)` PR runs from 2026-10-18 to 
2026-10-24, about 11 an hour. During the contributor pilot (a few gated pushes 
a day) job statuses use under 5% of the content-creating limit. With the gate 
enabled, and assuming about half of the runs come from gated pull requests, 
they would need about 400 writes an hour, so each monitor run posts at most 60 
(at most 240 an hour, 48% of the limit). What doesn't fit is posted by later 
runs; once the budget is spent, no further jobs or statuses are read in that 
run, and the monitor posts a Slack note (fixed wording, so once while deferrals 
persist). The share of gated runs and the jobs per push are assumptions; the 
measurements and the estimate are in the "Write budget" section of 
`dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md`.
   
   `job_statuses = false` in `.github/fork-ci-gate.toml` turns job statuses 
off; the aggregate status and `CI passed` are posted either way. 
`dev/AGENTS.md` says to do that when the budget is exceeded and lists the signs.
   
   ## Verified
   
   - The `dev/breeze` test suite passes (1854 passed, 7 skipped), with new 
tests for the job statuses, the budget, the marker key, stale contexts, 
case-insensitive contexts, the switch, the Slack note and the paginated head 
status.
   - prek pre-commit hooks pass (the Docker-based lychee link check was not 
run), and so does `mypy-dev`.
   - Not yet run against a real fork run: that needs the monitor on `main`, or 
a dry run with `breeze ci fork-ci-reconcile --dry-run` against a gated pull 
request.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes — Claude Code (Opus 5)
   
   Generated-by: Claude Code (Opus 5) following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to