This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch fork-ci-diagrams-prek
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 64c72f8e64acde905382ebb8ef50702c79dc16db
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Nov 10 06:30:19 2026 -0700

    Generate the fork CI diagrams from one source with the diagrams prek hook
    
    The fork CI design document drew its architecture as ASCII art and its 
reconciler states and pull request lifecycle in Mermaid, while the ADIP-12 wiki 
page, which cannot render Mermaid, needs images. Keeping both copies would let 
them drift. Make Graphviz sources (diagram_fork_ci_*.py under 
dev/breeze/doc/ci/images/) the only copy: the generate-airflow-diagrams prek 
hook regenerates each SVG when its source changes, like the other diagrams in 
the repository, and the design document an [...]
    
    Generated-by: Claude Opus 5
---
 .pre-commit-config.yaml                            |   3 +-
 .../doc/ci/08_fork_ci_for_external_contributors.md |  98 +--------
 .../ci/images/diagram_fork_ci_architecture.md5sum  |   1 +
 .../doc/ci/images/diagram_fork_ci_architecture.py  |  80 +++++++
 .../doc/ci/images/diagram_fork_ci_architecture.svg | 157 ++++++++++++++
 .../doc/ci/images/diagram_fork_ci_lifecycle.md5sum |   1 +
 .../doc/ci/images/diagram_fork_ci_lifecycle.py     |  88 ++++++++
 .../doc/ci/images/diagram_fork_ci_lifecycle.svg    | 234 +++++++++++++++++++++
 .../doc/ci/images/diagram_fork_ci_states.md5sum    |   1 +
 dev/breeze/doc/ci/images/diagram_fork_ci_states.py |  92 ++++++++
 .../doc/ci/images/diagram_fork_ci_states.svg       | 230 ++++++++++++++++++++
 11 files changed, 893 insertions(+), 92 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 28a88003170..7e92cf418ca 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1039,7 +1039,8 @@ repos:
           ^airflow-core/docs/.*/diagram_[^/]*\.py$|
           ^contributing-docs/images/diagram_[^/]*\.py$|
           ^docs/images/.*\.py$|
-          ^airflow-ctl/docs/images/diagrams/.*\.py$
+          ^airflow-ctl/docs/images/diagrams/.*\.py$|
+          ^dev/breeze/doc/ci/images/diagram_[^/]*\.py$
         pass_filenames: true
       - id: prevent-deprecated-sqlalchemy-usage
         name: Prevent deprecated sqlalchemy usage
diff --git a/dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md 
b/dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md
index 36118c452c6..f060c4199fe 100644
--- a/dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md
+++ b/dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md
@@ -78,26 +78,7 @@ Non-goals:
 
 ## Overview
 
-```
-             contributor fork                               apache/airflow
-   ┌──────────────────────────────────┐      
┌───────────────────────────────────────────┐
-   │ git push feature-branch          │      │ pull_request event              
          │
-   │   └─ ci-amd.yml (push trigger)   │      │   └─ ci-amd.yml                 
          │
-   │      ├─ gate job: fork -> run    │      │      └─ gate job: eligibility 
-> skip     │
-   │      ├─ build-info: fork mode    │      │         (build-info and 
everything after  │
-   │      │  changed files from       │      │          are skipped for gated 
PRs)       │
-   │      │  compare API vs upstream  │      │                                 
          │
-   │      └─ public runners only      │      │ fork-ci-monitor.yml (every 15 
minutes)    │
-   └──────────────────────────────────┘      │   └─ breeze ci 
fork-ci-reconcile          │
-                     ▲                       │      ├─ discover new gated PRs 
-> label   │
-                     │ GraphQL: checkSuites  │      ├─ per labelled PR: fork 
state ->    │
-                     │ REST: workflow state  │      │    draft/undraft, 
labels, comment, │
-                     └───────────────────────│      │    commit status with 
run link,    │
-                                             │      │    close after 30 days 
or when the │
-                                             │      │    PR tampered with the 
gate       │
-                                             │      └─ unknown failures -> 
Slack         │
-                                             
└───────────────────────────────────────────┘
-```
+![Fork CI architecture](images/diagram_fork_ci_architecture.svg)
 
 Four pieces, in dependency order:
 
@@ -109,6 +90,10 @@ Four pieces, in dependency order:
 
 Plus `breeze ci audit` for the contributor's local setup, and documentation.
 
+The diagrams in this document are SVG files in [`images/`](images/), generated 
from their
+`diagram_fork_ci_*.py` Graphviz sources by the `generate-airflow-diagrams` 
prek hook whenever a
+source changes. The ADIP-12 wiki page embeds the same files. Edit the source, 
not the SVG.
+
 ### Pull request lifecycle
 
 What a gated pull request shows, and who acts next, from the moment it is 
opened until it is merged
@@ -116,39 +101,7 @@ or closed. The [state machine](#state-machine) below is 
the reconciler's view of
 diagram is the contributor's and the maintainer's view. Pull requests of 
committers, collaborators
 and exempted organisations are not gated and follow the usual project CI flow.
 
-```mermaid
-%%{init: {'theme': 'neutral'}}%%
-stateDiagram-v2
-    state "Waiting for the author: draft, fork CI not set up or no run for the 
head commit" as waiting
-    state "Fork CI running" as running
-    state "Fork CI failed: out of the review queue" as failed
-    state "Green: ready for maintainer review, CI passed" as ready
-    state "Green, base too old: ready for maintainer review, CI passed pending 
until a rebase" as rebase
-    state "Green, CI files changed: needs project ci" as needs_project_ci
-    state "Project CI: use project ci" as project_ci
-    state "Approved" as approved
-    state "Merged" as merged
-    state "Closed after 30 days without fork CI activity" as closed
-
-    [*] --> waiting: an external contributor opens a pull request
-    waiting --> running: the author enables the fork workflow and pushes
-    running --> failed: a job fails, times out or is cancelled, or the run 
cannot start
-    running --> ready: the run passes on a current base
-    running --> rebase: the run passes, but the base is 200+ commits behind or 
changed the same files
-    running --> needs_project_ci: the run passes, and the pull request changes 
CI-defining files
-    failed --> running: the author pushes a fix
-    rebase --> running: the author rebases and pushes
-    ready --> running: the author pushes
-    ready --> approved: a maintainer approves
-    rebase --> approved: a maintainer approves, the merge waits for the rebase
-    ready --> project_ci: a maintainer adds use project ci
-    needs_project_ci --> project_ci: a maintainer reviews the CI change and 
adds use project ci
-    project_ci --> approved: project CI passes and a maintainer approves
-    approved --> merged: merged, or auto-merged once CI passed is green
-    waiting --> closed: 30 days without fork CI activity
-    closed --> waiting: the author reopens it
-    merged --> [*]
-```
+![Pull request lifecycle](images/diagram_fork_ci_lifecycle.svg)
 
 - **Draft** is used only while the pull request waits for its author to set up 
fork CI or push. A
   failed run leaves the draft state unchanged and removes `ready for 
maintainer review`, so a flaky
@@ -589,44 +542,7 @@ pull requests without a fork run for their head commit, 
not by the total.
 
 ### State machine
 
-```mermaid
-%%{init: {'theme': 'neutral'}}%%
-stateDiagram-v2
-    [*] --> gated: discovery adds fork ci required
-    state gated {
-        [*] --> setup_required: workflow not enabled in the fork
-        [*] --> awaiting_push: workflow enabled, no run for the head commit
-        [*] --> running: fork run in progress
-        setup_required --> awaiting_push: author enables the workflow
-        awaiting_push --> running: author pushes
-        running --> failed: run fails, is cancelled or times out
-        running --> green: run passes, no CI-defining change
-        running --> green_unverified: run passes, CI-defining change
-        failed --> running: author pushes
-        green --> running: author pushes
-        green_unverified --> running: author pushes
-        running --> setup_required: workflow disabled
-        awaiting_push --> setup_required: workflow disabled
-        state green {
-            [*] --> green_ready: not a draft, or the bot's draft
-            [*] --> green_author_draft: the author's draft
-            green_ready --> green_author_draft: author converts to draft
-            green_author_draft --> green_ready: author marks it ready
-        }
-        state green_unverified {
-            [*] --> unverified_ready: not a draft, or the bot's draft
-            [*] --> unverified_author_draft: the author's draft
-            unverified_ready --> unverified_author_draft: author converts to 
draft
-            unverified_author_draft --> unverified_ready: author marks it ready
-        }
-    }
-    gated --> closed: setup_required or awaiting_push for close_after_days (30 
days)
-    gated --> released: maintainer adds use project ci
-    gated --> ungated: base branch without fork CI, e.g. v3-3-test
-    gated --> gate_modified: pull request changes the gate
-    gated --> gate_bypassed: project CI ran without use project ci (gate 
tampering)
-    closed --> gated: reopened
-```
+![Fork CI reconciler states](images/diagram_fork_ci_states.svg)
 
 The draft state and the two review labels depend only on the state and on 
whether the pull
 request is the author's draft: a draft whose last convert-to-draft event was 
not by
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.md5sum 
b/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.md5sum
new file mode 100644
index 00000000000..49c405e73ef
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.md5sum
@@ -0,0 +1 @@
+0680ff8001f6f267b5b900af33ac16e3
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.py 
b/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.py
new file mode 100644
index 00000000000..6efe475797d
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.py
@@ -0,0 +1,80 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# /// script
+# requires-python = ">=3.11"
+# dependencies = [
+#    "rich>=13.6.0",
+#    "graphviz>=0.20.1",
+# ]
+# ///
+"""Where fork CI runs: the contributor fork and apache/airflow. Used in 
dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md and on the ADIP-12 
wiki page."""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+import graphviz
+from rich.console import Console
+
+MY_DIR = Path(__file__).parent
+MY_FILENAME = Path(__file__).with_suffix("").name
+
+console = Console(width=400, color_system="standard")
+
+DOT = r"""
+digraph arch {
+  graph [rankdir=LR, fontname="Helvetica", fontsize=11, nodesep=0.4, 
ranksep=0.7, compound=true, bgcolor="white"];
+  node  [shape=box, style="rounded,filled", fillcolor="#f2f4f7", 
color="#5b6b80", fontname="Helvetica", fontsize=11];
+  edge  [fontname="Helvetica", fontsize=9, color="#5b6b80", 
fontcolor="#33404d"];
+
+  subgraph cluster_fork {
+    label="contributor's fork (GitHub-hosted runners, contributor's token)"; 
style="rounded"; color="#3d8b57"; fontname="Helvetica";
+    push   [label="git push feature-branch"];
+    ci     [label="Tests (AMD) workflow\npush trigger"];
+    gatef  [label="gate job:\nfork -> run"];
+    bi     [label="build-info in fork mode\nchanged files vs 
upstream\n(compare API)"];
+    tests  [label="tests and checks\non public runners"];
+    push -> ci -> gatef -> bi -> tests;
+  }
+
+  subgraph cluster_apache {
+    label="apache/airflow"; style="rounded"; color="#5b6b80"; 
fontname="Helvetica";
+    pr     [label="pull_request event"];
+    gatea  [label="gate job:\neligibility -> skip\n(build-info and all later 
jobs skipped)"];
+    mon    [label="fork-ci-monitor.yml\n(scheduled, never runs PR code)"];
+    rec    [label="breeze ci fork-ci-reconcile"];
+    out    [label="per gated PR: draft state, labels,\nstate comment, commit 
statuses\n(Fork CI / Tests (AMD), CI passed),\nclose after 30 days or on gate 
tampering"];
+    slack  [label="unknown failures -> Slack", fillcolor="#eceff3"];
+    pr -> gatea;
+    mon -> rec -> out;
+    rec -> slack [style=dashed];
+  }
+
+  tests -> rec [label="GraphQL: check suites\nREST: runs, jobs, workflow 
state\n(read by the monitor)", dir=back, style=dashed];
+}
+"""
+
+
+def generate_diagram() -> None:
+    image_file = MY_DIR / f"{MY_FILENAME}.svg"
+    console.print(f"[bright_blue]Generating {image_file}")
+    graphviz.Source(DOT).render(outfile=image_file, format="svg", cleanup=True)
+    console.print(f"[green]Generated {image_file}")
+
+
+if __name__ == "__main__":
+    generate_diagram()
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.svg 
b/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.svg
new file mode 100644
index 00000000000..2ce33697709
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_architecture.svg
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<!-- Generated by graphviz version 16.1.0 (20260904.0139)
+ -->
+<!-- Title: arch Pages: 1 -->
+<svg width="1502pt" height="269pt"
+ viewBox="0.00 0.00 1502.00 269.00" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink";>
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 265)">
+<title>arch</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-265 1497.75,-265 
1497.75,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_fork</title>
+<path fill="white" stroke="#3d8b57" d="M20,-173C20,-173 807.88,-173 
807.88,-173 813.88,-173 819.88,-179 819.88,-185 819.88,-185 819.88,-241 
819.88,-241 819.88,-247 813.88,-253 807.88,-253 807.88,-253 20,-253 20,-253 
14,-253 8,-247 8,-241 8,-241 8,-185 8,-185 8,-179 14,-173 20,-173"/>
+<text xml:space="preserve" text-anchor="middle" x="413.94" y="-238.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">contributor&#39;s fork 
(GitHub&#45;hosted runners, contributor&#39;s token)</text>
+</g>
+<g id="clust2" class="cluster">
+<title>cluster_apache</title>
+<path fill="white" stroke="#5b6b80" d="M679.5,-8C679.5,-8 1473.75,-8 
1473.75,-8 1479.75,-8 1485.75,-14 1485.75,-20 1485.75,-20 1485.75,-153 
1485.75,-153 1485.75,-159 1479.75,-165 1473.75,-165 1473.75,-165 679.5,-165 
679.5,-165 673.5,-165 667.5,-159 667.5,-153 667.5,-153 667.5,-20 667.5,-20 
667.5,-14 673.5,-8 679.5,-8"/>
+<text xml:space="preserve" text-anchor="middle" x="1076.62" y="-150.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">apache/airflow</text>
+</g>
+<!-- push -->
+<g id="node1" class="node">
+<title>push</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M131,-221C131,-221 28,-221 28,-221 
22,-221 16,-215 16,-209 16,-209 16,-197 16,-197 16,-191 22,-185 28,-185 28,-185 
131,-185 131,-185 137,-185 143,-191 143,-197 143,-197 143,-209 143,-209 
143,-215 137,-221 131,-221"/>
+<text xml:space="preserve" text-anchor="middle" x="79.5" y="-198.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">git push 
feature&#45;branch</text>
+</g>
+<!-- ci -->
+<g id="node2" class="node">
+<title>ci</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M303.75,-221C303.75,-221 206,-221 
206,-221 200,-221 194,-215 194,-209 194,-209 194,-197 194,-197 194,-191 
200,-185 206,-185 206,-185 303.75,-185 303.75,-185 309.75,-185 315.75,-191 
315.75,-197 315.75,-197 315.75,-209 315.75,-209 315.75,-215 309.75,-221 
303.75,-221"/>
+<text xml:space="preserve" text-anchor="middle" x="254.88" y="-204.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">Tests (AMD) workflow</text>
+<text xml:space="preserve" text-anchor="middle" x="254.88" y="-192.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">push trigger</text>
+</g>
+<!-- push&#45;&gt;ci -->
+<g id="edge1" class="edge">
+<title>push&#45;&gt;ci</title>
+<path fill="none" stroke="#5b6b80" d="M143.09,-203C155.85,-203 169.35,-203 
182.39,-203"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="182.18,-206.5 192.18,-203 
182.18,-199.5 182.18,-206.5"/>
+</g>
+<!-- gatef -->
+<g id="node3" class="node">
+<title>gatef</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M421,-221C421,-221 378.75,-221 
378.75,-221 372.75,-221 366.75,-215 366.75,-209 366.75,-209 366.75,-197 
366.75,-197 366.75,-191 372.75,-185 378.75,-185 378.75,-185 421,-185 421,-185 
427,-185 433,-191 433,-197 433,-197 433,-209 433,-209 433,-215 427,-221 
421,-221"/>
+<text xml:space="preserve" text-anchor="middle" x="399.88" y="-204.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">gate job:</text>
+<text xml:space="preserve" text-anchor="middle" x="399.88" y="-192.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">fork &#45;&gt; run</text>
+</g>
+<!-- ci&#45;&gt;gatef -->
+<g id="edge2" class="edge">
+<title>ci&#45;&gt;gatef</title>
+<path fill="none" stroke="#5b6b80" d="M316.11,-203C329.18,-203 342.77,-203 
355.06,-203"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="354.88,-206.5 364.88,-203 
354.88,-199.5 354.88,-206.5"/>
+</g>
+<!-- bi -->
+<g id="node4" class="node">
+<title>bi</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M612.5,-225C612.5,-225 496,-225 
496,-225 490,-225 484,-219 484,-213 484,-213 484,-193 484,-193 484,-187 
490,-181 496,-181 496,-181 612.5,-181 612.5,-181 618.5,-181 624.5,-187 
624.5,-193 624.5,-193 624.5,-213 624.5,-213 624.5,-219 618.5,-225 612.5,-225"/>
+<text xml:space="preserve" text-anchor="middle" x="554.25" y="-210.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">build&#45;info in fork 
mode</text>
+<text xml:space="preserve" text-anchor="middle" x="554.25" y="-198.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">changed files vs 
upstream</text>
+<text xml:space="preserve" text-anchor="middle" x="554.25" y="-186.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">(compare API)</text>
+</g>
+<!-- gatef&#45;&gt;bi -->
+<g id="edge3" class="edge">
+<title>gatef&#45;&gt;bi</title>
+<path fill="none" stroke="#5b6b80" d="M433.19,-203C444.84,-203 458.56,-203 
472.49,-203"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="472.16,-206.5 482.16,-203 
472.16,-199.5 472.16,-206.5"/>
+</g>
+<!-- tests -->
+<g id="node5" class="node">
+<title>tests</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M799.88,-219C799.88,-219 725.38,-219 
725.38,-219 719.38,-219 713.38,-213 713.38,-207 713.38,-207 713.38,-195 
713.38,-195 713.38,-189 719.38,-183 725.38,-183 725.38,-183 799.88,-183 
799.88,-183 805.88,-183 811.88,-189 811.88,-195 811.88,-195 811.88,-207 
811.88,-207 811.88,-213 805.88,-219 799.88,-219"/>
+<text xml:space="preserve" text-anchor="middle" x="762.62" y="-202.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">tests and checks</text>
+<text xml:space="preserve" text-anchor="middle" x="762.62" y="-190.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">on public runners</text>
+</g>
+<!-- bi&#45;&gt;tests -->
+<g id="edge4" class="edge">
+<title>bi&#45;&gt;tests</title>
+<path fill="none" stroke="#5b6b80" d="M624.83,-202.33C649.77,-202.08 
677.65,-201.81 701.7,-201.58"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="701.69,-205.08 711.66,-201.48 
701.62,-198.08 701.69,-205.08"/>
+</g>
+<!-- rec -->
+<g id="node9" class="node">
+<title>rec</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M1181.25,-137C1181.25,-137 1067,-137 
1067,-137 1061,-137 1055,-131 1055,-125 1055,-125 1055,-113 1055,-113 1055,-107 
1061,-101 1067,-101 1067,-101 1181.25,-101 1181.25,-101 1187.25,-101 
1193.25,-107 1193.25,-113 1193.25,-113 1193.25,-125 1193.25,-125 1193.25,-131 
1187.25,-137 1181.25,-137"/>
+<text xml:space="preserve" text-anchor="middle" x="1124.12" y="-114.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">breeze ci 
fork&#45;ci&#45;reconcile</text>
+</g>
+<!-- tests&#45;&gt;rec -->
+<g id="edge9" class="edge">
+<title>tests&#45;&gt;rec</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M823.16,-187.42C886.81,-172.9 987.61,-149.91 1054.55,-134.64"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="822.56,-183.97 813.58,-189.6 
824.11,-190.79 822.56,-183.97"/>
+<text xml:space="preserve" text-anchor="middle" x="940" y="-195.18" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">GraphQL: 
check suites</text>
+<text xml:space="preserve" text-anchor="middle" x="940" y="-185.43" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">REST: runs, 
jobs, workflow state</text>
+<text xml:space="preserve" text-anchor="middle" x="940" y="-175.68" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">(read by the 
monitor)</text>
+</g>
+<!-- pr -->
+<g id="node6" class="node">
+<title>pr</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M802.5,-62C802.5,-62 722.75,-62 
722.75,-62 716.75,-62 710.75,-56 710.75,-50 710.75,-50 710.75,-38 710.75,-38 
710.75,-32 716.75,-26 722.75,-26 722.75,-26 802.5,-26 802.5,-26 808.5,-26 
814.5,-32 814.5,-38 814.5,-38 814.5,-50 814.5,-50 814.5,-56 808.5,-62 
802.5,-62"/>
+<text xml:space="preserve" text-anchor="middle" x="762.62" y="-39.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">pull_request event</text>
+</g>
+<!-- gatea -->
+<g id="node7" class="node">
+<title>gatea</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M1206,-66C1206,-66 1042.25,-66 
1042.25,-66 1036.25,-66 1030.25,-60 1030.25,-54 1030.25,-54 1030.25,-34 
1030.25,-34 1030.25,-28 1036.25,-22 1042.25,-22 1042.25,-22 1206,-22 1206,-22 
1212,-22 1218,-28 1218,-34 1218,-34 1218,-54 1218,-54 1218,-60 1212,-66 
1206,-66"/>
+<text xml:space="preserve" text-anchor="middle" x="1124.12" y="-51.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">gate job:</text>
+<text xml:space="preserve" text-anchor="middle" x="1124.12" y="-39.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">eligibility &#45;&gt; 
skip</text>
+<text xml:space="preserve" text-anchor="middle" x="1124.12" y="-27.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">(build&#45;info and all 
later jobs skipped)</text>
+</g>
+<!-- pr&#45;&gt;gatea -->
+<g id="edge5" class="edge">
+<title>pr&#45;&gt;gatea</title>
+<path fill="none" stroke="#5b6b80" d="M814.75,-44C867.53,-44 951.37,-44 
1018.32,-44"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="1018.24,-47.5 1028.24,-44 
1018.24,-40.5 1018.24,-47.5"/>
+</g>
+<!-- mon -->
+<g id="node8" class="node">
+<title>mon</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M837.75,-137C837.75,-137 687.5,-137 
687.5,-137 681.5,-137 675.5,-131 675.5,-125 675.5,-125 675.5,-113 675.5,-113 
675.5,-107 681.5,-101 687.5,-101 687.5,-101 837.75,-101 837.75,-101 843.75,-101 
849.75,-107 849.75,-113 849.75,-113 849.75,-125 849.75,-125 849.75,-131 
843.75,-137 837.75,-137"/>
+<text xml:space="preserve" text-anchor="middle" x="762.62" y="-120.55" 
font-family="Helvetica,sans-Serif" 
font-size="11.00">fork&#45;ci&#45;monitor.yml</text>
+<text xml:space="preserve" text-anchor="middle" x="762.62" y="-108.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">(scheduled, never runs PR 
code)</text>
+</g>
+<!-- mon&#45;&gt;rec -->
+<g id="edge6" class="edge">
+<title>mon&#45;&gt;rec</title>
+<path fill="none" stroke="#5b6b80" d="M850.19,-119C909.02,-119 986.24,-119 
1043.53,-119"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="1043.2,-122.5 1053.2,-119 
1043.2,-115.5 1043.2,-122.5"/>
+</g>
+<!-- out -->
+<g id="node10" class="node">
+<title>out</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M1465.75,-72C1465.75,-72 1281,-72 
1281,-72 1275,-72 1269,-66 1269,-60 1269,-60 1269,-28 1269,-28 1269,-22 
1275,-16 1281,-16 1281,-16 1465.75,-16 1465.75,-16 1471.75,-16 1477.75,-22 
1477.75,-28 1477.75,-28 1477.75,-60 1477.75,-60 1477.75,-66 1471.75,-72 
1465.75,-72"/>
+<text xml:space="preserve" text-anchor="middle" x="1373.38" y="-57.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">per gated PR: draft state, 
labels,</text>
+<text xml:space="preserve" text-anchor="middle" x="1373.38" y="-45.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">state comment, commit 
statuses</text>
+<text xml:space="preserve" text-anchor="middle" x="1373.38" y="-33.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">(Fork CI / Tests (AMD), CI 
passed),</text>
+<text xml:space="preserve" text-anchor="middle" x="1373.38" y="-21.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">close after 30 days or on 
gate tampering</text>
+</g>
+<!-- rec&#45;&gt;out -->
+<g id="edge7" class="edge">
+<title>rec&#45;&gt;out</title>
+<path fill="none" stroke="#5b6b80" d="M1185.93,-100.56C1210.54,-93.09 
1239.68,-84.25 1267.52,-75.81"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="1268.52,-79.16 1277.08,-72.91 
1266.49,-72.46 1268.52,-79.16"/>
+</g>
+<!-- slack -->
+<g id="node11" class="node">
+<title>slack</title>
+<path fill="#eceff3" stroke="#5b6b80" d="M1431.25,-137C1431.25,-137 
1315.5,-137 1315.5,-137 1309.5,-137 1303.5,-131 1303.5,-125 1303.5,-125 
1303.5,-113 1303.5,-113 1303.5,-107 1309.5,-101 1315.5,-101 1315.5,-101 
1431.25,-101 1431.25,-101 1437.25,-101 1443.25,-107 1443.25,-113 1443.25,-113 
1443.25,-125 1443.25,-125 1443.25,-131 1437.25,-137 1431.25,-137"/>
+<text xml:space="preserve" text-anchor="middle" x="1373.38" y="-114.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">unknown failures &#45;&gt; 
Slack</text>
+</g>
+<!-- rec&#45;&gt;slack -->
+<g id="edge8" class="edge">
+<title>rec&#45;&gt;slack</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M1193.67,-119C1224.09,-119 1260.1,-119 1291.8,-119"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="1291.75,-122.5 1301.75,-119 
1291.75,-115.5 1291.75,-122.5"/>
+</g>
+</g>
+</svg>
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.md5sum 
b/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.md5sum
new file mode 100644
index 00000000000..b424a0be35d
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.md5sum
@@ -0,0 +1 @@
+f0006cdaf4d566c6ded754fd92f8abad
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.py 
b/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.py
new file mode 100644
index 00000000000..33e9691dfe1
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.py
@@ -0,0 +1,88 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# /// script
+# requires-python = ">=3.11"
+# dependencies = [
+#    "rich>=13.6.0",
+#    "graphviz>=0.20.1",
+# ]
+# ///
+"""What a gated pull request shows, and who acts next, from opening to merge 
or close. Used in dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md and 
on the ADIP-12 wiki page."""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+import graphviz
+from rich.console import Console
+
+MY_DIR = Path(__file__).parent
+MY_FILENAME = Path(__file__).with_suffix("").name
+
+console = Console(width=400, color_system="standard")
+
+DOT = r"""
+digraph lifecycle {
+  graph [rankdir=TB, fontname="Helvetica", fontsize=11, nodesep=0.5, 
ranksep=0.55, bgcolor="white"];
+  node  [shape=box, style="rounded,filled", fillcolor="#f2f4f7", 
color="#5b6b80", fontname="Helvetica", fontsize=11, margin="0.15,0.08"];
+  edge  [fontname="Helvetica", fontsize=9, color="#5b6b80", 
fontcolor="#33404d"];
+
+  start [shape=circle, label="", width=0.22, fillcolor="#33404d", 
color="#33404d"];
+  done  [shape=doublecircle, label="", width=0.18, fillcolor="#33404d", 
color="#33404d"];
+
+  waiting [label="Waiting for the author\n(draft: fork CI not set up,\nor no 
run for the head commit)"];
+  running [label="Fork CI running"];
+  failed  [label="Fork CI failed\n(out of the review queue)", 
fillcolor="#fde8e8", color="#b54848"];
+  ready   [label="Green\nready for maintainer review\nCI passed", 
fillcolor="#e6f4ea", color="#3d8b57"];
+  rebase  [label="Green, base too old\nready for maintainer review\nCI passed 
pending until a rebase", fillcolor="#fff6db", color="#b38a1d"];
+  needs   [label="Green, CI files changed\nneeds project ci", 
fillcolor="#fff6db", color="#b38a1d"];
+  project [label="Project CI\n(use project ci)"];
+  approved[label="Approved"];
+  merged  [label="Merged", fillcolor="#e6f4ea", color="#3d8b57"];
+  closed  [label="Closed after 30 days\nwithout fork CI activity", 
fillcolor="#eceff3", color="#8a96a3"];
+
+  start -> waiting [label="external contributor\nopens a pull request"];
+  waiting -> running [label="author enables the fork\nworkflow and pushes"];
+  running -> failed [label="a job fails, times out,\nis cancelled, or the\nrun 
cannot start"];
+  running -> ready [label="passes on a\ncurrent base"];
+  running -> rebase [label="passes, base 200+ commits\nbehind or changed\nthe 
same files"];
+  running -> needs [label="passes, changes\nCI-defining files"];
+  failed -> running [label="author pushes a fix"];
+  rebase -> running [label="author rebases\nand pushes"];
+  ready -> running [label="author pushes", style=dashed];
+  ready -> approved [label="maintainer approves"];
+  rebase -> approved [label="maintainer approves;\nmerge waits for the 
rebase", style=dashed];
+  ready -> project [label="maintainer adds\nuse project ci", style=dashed];
+  needs -> project [label="maintainer reviews the\nCI change, adds use project 
ci"];
+  project -> approved [label="project CI passes,\nmaintainer approves"];
+  approved -> merged [label="merged, or auto-merged\nonce CI passed is green"];
+  waiting -> closed [label="30 days without\nfork CI activity"];
+  closed -> waiting [label="author reopens", style=dashed];
+  merged -> done;
+}
+"""
+
+
+def generate_diagram() -> None:
+    image_file = MY_DIR / f"{MY_FILENAME}.svg"
+    console.print(f"[bright_blue]Generating {image_file}")
+    graphviz.Source(DOT).render(outfile=image_file, format="svg", cleanup=True)
+    console.print(f"[green]Generated {image_file}")
+
+
+if __name__ == "__main__":
+    generate_diagram()
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.svg 
b/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.svg
new file mode 100644
index 00000000000..95c91474c00
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_lifecycle.svg
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<!-- Generated by graphviz version 16.1.0 (20260904.0139)
+ -->
+<!-- Title: lifecycle Pages: 1 -->
+<svg width="790pt" height="692pt"
+ viewBox="0.00 0.00 790.00 692.00" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink";>
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 
687.59)">
+<title>lifecycle</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-687.59 786.19,-687.59 
786.19,4 -4,4"/>
+<!-- start -->
+<g id="node1" class="node">
+<title>start</title>
+<ellipse fill="#33404d" stroke="#33404d" cx="524.8" cy="-675.67" rx="7.92" 
ry="7.92"/>
+</g>
+<!-- waiting -->
+<g id="node3" class="node">
+<title>waiting</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M596.35,-608.25C596.35,-608.25 
453.25,-608.25 453.25,-608.25 447.25,-608.25 441.25,-602.25 441.25,-596.25 
441.25,-596.25 441.25,-572.73 441.25,-572.73 441.25,-566.73 447.25,-560.73 
453.25,-560.73 453.25,-560.73 596.35,-560.73 596.35,-560.73 602.35,-560.73 
608.35,-566.73 608.35,-572.73 608.35,-572.73 608.35,-596.25 608.35,-596.25 
608.35,-602.25 602.35,-608.25 596.35,-608.25"/>
+<text xml:space="preserve" text-anchor="middle" x="524.8" y="-592.04" 
font-family="Helvetica,sans-Serif" font-size="11.00">Waiting for the 
author</text>
+<text xml:space="preserve" text-anchor="middle" x="524.8" y="-580.04" 
font-family="Helvetica,sans-Serif" font-size="11.00">(draft: fork CI not set 
up,</text>
+<text xml:space="preserve" text-anchor="middle" x="524.8" y="-568.04" 
font-family="Helvetica,sans-Serif" font-size="11.00">or no run for the head 
commit)</text>
+</g>
+<!-- start&#45;&gt;waiting -->
+<g id="edge1" class="edge">
+<title>start&#45;&gt;waiting</title>
+<path fill="none" stroke="#5b6b80" d="M524.8,-667.37C524.8,-656.98 
524.8,-637.23 524.8,-619.74"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="528.3,-619.92 524.8,-609.92 
521.3,-619.92 528.3,-619.92"/>
+<text xml:space="preserve" text-anchor="middle" x="566.42" y="-639.2" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">external 
contributor</text>
+<text xml:space="preserve" text-anchor="middle" x="566.42" y="-629.45" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">opens a pull 
request</text>
+</g>
+<!-- done -->
+<g id="node2" class="node">
+<title>done</title>
+<ellipse fill="#33404d" stroke="#33404d" cx="529.8" cy="-10.48" rx="6.48" 
ry="6.48"/>
+<ellipse fill="none" stroke="#33404d" cx="529.8" cy="-10.48" rx="10.48" 
ry="10.48"/>
+</g>
+<!-- running -->
+<g id="node4" class="node">
+<title>running</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M426.1,-501.23C426.1,-501.23 
353.5,-501.23 353.5,-501.23 347.5,-501.23 341.5,-495.23 341.5,-489.23 
341.5,-489.23 341.5,-477.23 341.5,-477.23 341.5,-471.23 347.5,-465.23 
353.5,-465.23 353.5,-465.23 426.1,-465.23 426.1,-465.23 432.1,-465.23 
438.1,-471.23 438.1,-477.23 438.1,-477.23 438.1,-489.23 438.1,-489.23 
438.1,-495.23 432.1,-501.23 426.1,-501.23"/>
+<text xml:space="preserve" text-anchor="middle" x="389.8" y="-478.78" 
font-family="Helvetica,sans-Serif" font-size="11.00">Fork CI running</text>
+</g>
+<!-- waiting&#45;&gt;running -->
+<g id="edge2" class="edge">
+<title>waiting&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" d="M440.92,-562.86C429.15,-557.17 
418.02,-549.93 409.05,-540.73 401.67,-533.16 397.11,-522.64 394.3,-512.73"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="397.73,-512.03 392.08,-503.07 
390.91,-513.6 397.73,-512.03"/>
+<text xml:space="preserve" text-anchor="middle" x="455.93" y="-532.18" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
enables the fork</text>
+<text xml:space="preserve" text-anchor="middle" x="455.93" y="-522.43" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">workflow and 
pushes</text>
+</g>
+<!-- closed -->
+<g id="node12" class="node">
+<title>closed</title>
+<path fill="#eceff3" stroke="#8a96a3" d="M634.1,-501.23C634.1,-501.23 
531.5,-501.23 531.5,-501.23 525.5,-501.23 519.5,-495.23 519.5,-489.23 
519.5,-489.23 519.5,-477.23 519.5,-477.23 519.5,-471.23 525.5,-465.23 
531.5,-465.23 531.5,-465.23 634.1,-465.23 634.1,-465.23 640.1,-465.23 
646.1,-471.23 646.1,-477.23 646.1,-477.23 646.1,-489.23 646.1,-489.23 
646.1,-495.23 640.1,-501.23 634.1,-501.23"/>
+<text xml:space="preserve" text-anchor="middle" x="582.8" y="-484.78" 
font-family="Helvetica,sans-Serif" font-size="11.00">Closed after 30 days</text>
+<text xml:space="preserve" text-anchor="middle" x="582.8" y="-472.78" 
font-family="Helvetica,sans-Serif" font-size="11.00">without fork CI 
activity</text>
+</g>
+<!-- waiting&#45;&gt;closed -->
+<g id="edge16" class="edge">
+<title>waiting&#45;&gt;closed</title>
+<path fill="none" stroke="#5b6b80" d="M570.96,-560.28C577.74,-554.82 
583.78,-548.32 587.8,-540.73 592.27,-532.29 592.64,-522.13 591.36,-512.77"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="594.8,-512.1 589.37,-503.01 
587.94,-513.5 594.8,-512.1"/>
+<text xml:space="preserve" text-anchor="middle" x="623.88" y="-532.18" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">30 days 
without</text>
+<text xml:space="preserve" text-anchor="middle" x="623.88" y="-522.43" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">fork CI 
activity</text>
+</g>
+<!-- failed -->
+<g id="node5" class="node">
+<title>failed</title>
+<path fill="#fde8e8" stroke="#b54848" d="M129.6,-390.22C129.6,-390.22 
12,-390.22 12,-390.22 6,-390.22 0,-384.22 0,-378.22 0,-378.22 0,-366.22 
0,-366.22 0,-360.22 6,-354.22 12,-354.22 12,-354.22 129.6,-354.22 129.6,-354.22 
135.6,-354.22 141.6,-360.22 141.6,-366.22 141.6,-366.22 141.6,-378.22 
141.6,-378.22 141.6,-384.22 135.6,-390.22 129.6,-390.22"/>
+<text xml:space="preserve" text-anchor="middle" x="70.8" y="-373.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">Fork CI failed</text>
+<text xml:space="preserve" text-anchor="middle" x="70.8" y="-361.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">(out of the review 
queue)</text>
+</g>
+<!-- running&#45;&gt;failed -->
+<g id="edge3" class="edge">
+<title>running&#45;&gt;failed</title>
+<path fill="none" stroke="#5b6b80" d="M341.04,-477.89C300.26,-473.14 
241.11,-463.61 192.3,-445.23 160.23,-433.16 126.97,-412.81 103.37,-396.81"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="105.66,-394.14 95.44,-391.34 
101.69,-399.9 105.66,-394.14"/>
+<text xml:space="preserve" text-anchor="middle" x="233.55" y="-436.68" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">a job fails, 
times out,</text>
+<text xml:space="preserve" text-anchor="middle" x="233.55" y="-426.93" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">is 
cancelled, or the</text>
+<text xml:space="preserve" text-anchor="middle" x="233.55" y="-417.18" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">run cannot 
start</text>
+</g>
+<!-- ready -->
+<g id="node6" class="node">
+<title>ready</title>
+<path fill="#e6f4ea" stroke="#3d8b57" d="M533.85,-395.98C533.85,-395.98 
405.75,-395.98 405.75,-395.98 399.75,-395.98 393.75,-389.98 393.75,-383.98 
393.75,-383.98 393.75,-360.46 393.75,-360.46 393.75,-354.46 399.75,-348.46 
405.75,-348.46 405.75,-348.46 533.85,-348.46 533.85,-348.46 539.85,-348.46 
545.85,-354.46 545.85,-360.46 545.85,-360.46 545.85,-383.98 545.85,-383.98 
545.85,-389.98 539.85,-395.98 533.85,-395.98"/>
+<text xml:space="preserve" text-anchor="middle" x="469.8" y="-379.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">Green</text>
+<text xml:space="preserve" text-anchor="middle" x="469.8" y="-367.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">ready for maintainer 
review</text>
+<text xml:space="preserve" text-anchor="middle" x="469.8" y="-355.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">CI passed</text>
+</g>
+<!-- running&#45;&gt;ready -->
+<g id="edge4" class="edge">
+<title>running&#45;&gt;ready</title>
+<path fill="none" stroke="#5b6b80" d="M427.54,-464.91C435.85,-459.6 
443.92,-453.06 449.8,-445.23 457.98,-434.35 462.81,-420.3 465.67,-407.5"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="469.1,-408.18 467.5,-397.71 
462.22,-406.9 469.1,-408.18"/>
+<text xml:space="preserve" text-anchor="middle" x="488.99" y="-431.81" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">passes on 
a</text>
+<text xml:space="preserve" text-anchor="middle" x="488.99" y="-422.06" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">current 
base</text>
+</g>
+<!-- rebase -->
+<g id="node7" class="node">
+<title>rebase</title>
+<path fill="#fff6db" stroke="#b38a1d" d="M762.35,-395.98C762.35,-395.98 
607.25,-395.98 607.25,-395.98 601.25,-395.98 595.25,-389.98 595.25,-383.98 
595.25,-383.98 595.25,-360.46 595.25,-360.46 595.25,-354.46 601.25,-348.46 
607.25,-348.46 607.25,-348.46 762.35,-348.46 762.35,-348.46 768.35,-348.46 
774.35,-354.46 774.35,-360.46 774.35,-360.46 774.35,-383.98 774.35,-383.98 
774.35,-389.98 768.35,-395.98 762.35,-395.98"/>
+<text xml:space="preserve" text-anchor="middle" x="684.8" y="-379.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">Green, base too old</text>
+<text xml:space="preserve" text-anchor="middle" x="684.8" y="-367.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">ready for maintainer 
review</text>
+<text xml:space="preserve" text-anchor="middle" x="684.8" y="-355.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">CI passed pending until a 
rebase</text>
+</g>
+<!-- running&#45;&gt;rebase -->
+<g id="edge5" class="edge">
+<title>running&#45;&gt;rebase</title>
+<path fill="none" stroke="#5b6b80" d="M438.57,-474.7C458.21,-471.7 
481.07,-468.25 501.8,-465.23 533.55,-460.6 618.34,-463.38 644.8,-445.23 
658.24,-436.01 667.64,-420.73 673.93,-406.61"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="677.09,-408.15 677.59,-397.57 
670.6,-405.53 677.09,-408.15"/>
+<text xml:space="preserve" text-anchor="middle" x="725.57" y="-436.68" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">passes, base 
200+ commits</text>
+<text xml:space="preserve" text-anchor="middle" x="725.57" y="-426.93" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">behind or 
changed</text>
+<text xml:space="preserve" text-anchor="middle" x="725.57" y="-417.18" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">the same 
files</text>
+</g>
+<!-- needs -->
+<g id="node8" class="node">
+<title>needs</title>
+<path fill="#fff6db" stroke="#b38a1d" d="M333.23,-390.22C333.23,-390.22 
222.38,-390.22 222.38,-390.22 216.38,-390.22 210.38,-384.22 210.38,-378.22 
210.38,-378.22 210.38,-366.22 210.38,-366.22 210.38,-360.22 216.38,-354.22 
222.38,-354.22 222.38,-354.22 333.23,-354.22 333.23,-354.22 339.23,-354.22 
345.23,-360.22 345.23,-366.22 345.23,-366.22 345.23,-378.22 345.23,-378.22 
345.23,-384.22 339.23,-390.22 333.23,-390.22"/>
+<text xml:space="preserve" text-anchor="middle" x="277.8" y="-373.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">Green, CI files 
changed</text>
+<text xml:space="preserve" text-anchor="middle" x="277.8" y="-361.77" 
font-family="Helvetica,sans-Serif" font-size="11.00">needs project ci</text>
+</g>
+<!-- running&#45;&gt;needs -->
+<g id="edge6" class="edge">
+<title>running&#45;&gt;needs</title>
+<path fill="none" stroke="#5b6b80" d="M341.39,-471.52C325.96,-465.97 
310,-457.64 298.8,-445.23 288.06,-433.33 282.79,-416.22 280.22,-401.67"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="283.75,-401.61 278.89,-392.19 
276.81,-402.58 283.75,-401.61"/>
+<text xml:space="preserve" text-anchor="middle" x="333.3" y="-431.81" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">passes, 
changes</text>
+<text xml:space="preserve" text-anchor="middle" x="333.3" y="-422.06" 
font-family="Helvetica,sans-Serif" font-size="9.00" 
fill="#33404d">CI&#45;defining files</text>
+</g>
+<!-- failed&#45;&gt;running -->
+<g id="edge7" class="edge">
+<title>failed&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" d="M56.66,-390.62C45.49,-406.58 
33.86,-429.98 47.55,-445.23 66.04,-465.83 237.03,-475.99 330.1,-480.03"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="329.67,-483.52 339.81,-480.45 
329.96,-476.53 329.67,-483.52"/>
+<text xml:space="preserve" text-anchor="middle" x="86.18" y="-426.93" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
pushes a fix</text>
+</g>
+<!-- ready&#45;&gt;running -->
+<g id="edge9" class="edge">
+<title>ready&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M401.79,-396.32C394.07,-401.62 387.22,-408.09 382.3,-415.98 375.31,-427.18 
376.04,-441.55 378.99,-454.05"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="375.56,-454.8 381.75,-463.4 
382.28,-452.82 375.56,-454.8"/>
+<text xml:space="preserve" text-anchor="middle" x="411.55" y="-426.93" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
pushes</text>
+</g>
+<!-- project -->
+<g id="node9" class="node">
+<title>project</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M445.85,-288.96C445.85,-288.96 
377.75,-288.96 377.75,-288.96 371.75,-288.96 365.75,-282.96 365.75,-276.96 
365.75,-276.96 365.75,-264.96 365.75,-264.96 365.75,-258.96 371.75,-252.96 
377.75,-252.96 377.75,-252.96 445.85,-252.96 445.85,-252.96 451.85,-252.96 
457.85,-258.96 457.85,-264.96 457.85,-264.96 457.85,-276.96 457.85,-276.96 
457.85,-282.96 451.85,-288.96 445.85,-288.96"/>
+<text xml:space="preserve" text-anchor="middle" x="411.8" y="-272.51" 
font-family="Helvetica,sans-Serif" font-size="11.00">Project CI</text>
+<text xml:space="preserve" text-anchor="middle" x="411.8" y="-260.51" 
font-family="Helvetica,sans-Serif" font-size="11.00">(use project ci)</text>
+</g>
+<!-- ready&#45;&gt;project -->
+<g id="edge12" class="edge">
+<title>ready&#45;&gt;project</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M449.54,-348.07C444.69,-341.93 439.78,-335.14 435.8,-328.46 430.47,-319.51 
425.72,-309.15 421.87,-299.77"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="425.25,-298.8 418.34,-290.76 
418.73,-301.35 425.25,-298.8"/>
+<text xml:space="preserve" text-anchor="middle" x="468.8" y="-319.91" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">maintainer 
adds</text>
+<text xml:space="preserve" text-anchor="middle" x="468.8" y="-310.16" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">use project 
ci</text>
+</g>
+<!-- approved -->
+<g id="node10" class="node">
+<title>approved</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M551.85,-193.46C551.85,-193.46 
507.75,-193.46 507.75,-193.46 501.75,-193.46 495.75,-187.46 495.75,-181.46 
495.75,-181.46 495.75,-169.46 495.75,-169.46 495.75,-163.46 501.75,-157.46 
507.75,-157.46 507.75,-157.46 551.85,-157.46 551.85,-157.46 557.85,-157.46 
563.85,-163.46 563.85,-169.46 563.85,-169.46 563.85,-181.46 563.85,-181.46 
563.85,-187.46 557.85,-193.46 551.85,-193.46"/>
+<text xml:space="preserve" text-anchor="middle" x="529.8" y="-171.01" 
font-family="Helvetica,sans-Serif" font-size="11.00">Approved</text>
+</g>
+<!-- ready&#45;&gt;approved -->
+<g id="edge10" class="edge">
+<title>ready&#45;&gt;approved</title>
+<path fill="none" stroke="#5b6b80" d="M493.94,-348.04C498.91,-342.1 
503.6,-335.41 506.8,-328.46 525.24,-288.44 529.46,-236.83 530.13,-205.21"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="533.63,-205.3 530.23,-195.27 
526.63,-205.24 533.63,-205.3"/>
+<text xml:space="preserve" text-anchor="middle" x="568.42" y="-267.29" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">maintainer 
approves</text>
+</g>
+<!-- rebase&#45;&gt;running -->
+<g id="edge8" class="edge">
+<title>rebase&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" d="M619.81,-396.43C604.68,-402.36 
588.77,-409.04 574.3,-415.98 550.91,-427.19 547.72,-435.2 523.8,-445.23 
500.17,-455.14 473.09,-463.2 449.48,-469.23"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="448.89,-465.77 440.03,-471.58 
450.58,-472.57 448.89,-465.77"/>
+<text xml:space="preserve" text-anchor="middle" x="605.05" y="-431.81" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
rebases</text>
+<text xml:space="preserve" text-anchor="middle" x="605.05" y="-422.06" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">and 
pushes</text>
+</g>
+<!-- rebase&#45;&gt;approved -->
+<g id="edge11" class="edge">
+<title>rebase&#45;&gt;approved</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M675.26,-348.37C664.18,-323.38 644.24,-282.91 619.8,-252.96 603.68,-233.21 
581.99,-214.6 563.94,-200.68"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="566.19,-197.99 556.09,-194.76 
561.97,-203.57 566.19,-197.99"/>
+<text xml:space="preserve" text-anchor="middle" x="698.37" y="-272.16" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">maintainer 
approves;</text>
+<text xml:space="preserve" text-anchor="middle" x="698.37" y="-262.41" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">merge waits 
for the rebase</text>
+</g>
+<!-- needs&#45;&gt;project -->
+<g id="edge13" class="edge">
+<title>needs&#45;&gt;project</title>
+<path fill="none" stroke="#5b6b80" d="M277.87,-353.72C278.86,-339.88 
282.43,-320.94 293.8,-308.96 309.59,-292.32 332.77,-283.12 354.4,-278.06"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="354.96,-281.51 364.05,-276.07 
353.55,-274.66 354.96,-281.51"/>
+<text xml:space="preserve" text-anchor="middle" x="355.3" y="-319.91" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">maintainer 
reviews the</text>
+<text xml:space="preserve" text-anchor="middle" x="355.3" y="-310.16" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">CI change, 
adds use project ci</text>
+</g>
+<!-- project&#45;&gt;approved -->
+<g id="edge14" class="edge">
+<title>project&#45;&gt;approved</title>
+<path fill="none" stroke="#5b6b80" d="M409.06,-252.55C408.13,-240.33 
409.07,-224.29 417.8,-213.46 433.82,-193.6 460.78,-184.31 484.24,-180"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="484.57,-183.49 493.91,-178.49 
483.49,-176.57 484.57,-183.49"/>
+<text xml:space="preserve" text-anchor="middle" x="459.8" y="-224.41" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">project CI 
passes,</text>
+<text xml:space="preserve" text-anchor="middle" x="459.8" y="-214.66" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">maintainer 
approves</text>
+</g>
+<!-- merged -->
+<g id="node11" class="node">
+<title>merged</title>
+<path fill="#e6f4ea" stroke="#3d8b57" d="M546.97,-97.96C546.97,-97.96 
512.62,-97.96 512.62,-97.96 506.62,-97.96 500.62,-91.96 500.62,-85.96 
500.62,-85.96 500.62,-73.96 500.62,-73.96 500.62,-67.96 506.62,-61.96 
512.62,-61.96 512.62,-61.96 546.97,-61.96 546.97,-61.96 552.97,-61.96 
558.97,-67.96 558.97,-73.96 558.97,-73.96 558.97,-85.96 558.97,-85.96 
558.97,-91.96 552.97,-97.96 546.97,-97.96"/>
+<text xml:space="preserve" text-anchor="middle" x="529.8" y="-75.51" 
font-family="Helvetica,sans-Serif" font-size="11.00">Merged</text>
+</g>
+<!-- approved&#45;&gt;merged -->
+<g id="edge15" class="edge">
+<title>approved&#45;&gt;merged</title>
+<path fill="none" stroke="#5b6b80" d="M529.8,-157.31C529.8,-143.95 
529.8,-125.11 529.8,-109.49"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="533.3,-109.83 529.8,-99.83 
526.3,-109.83 533.3,-109.83"/>
+<text xml:space="preserve" text-anchor="middle" x="579.3" y="-128.91" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">merged, or 
auto&#45;merged</text>
+<text xml:space="preserve" text-anchor="middle" x="579.3" y="-119.16" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">once CI 
passed is green</text>
+</g>
+<!-- merged&#45;&gt;done -->
+<g id="edge18" class="edge">
+<title>merged&#45;&gt;done</title>
+<path fill="none" stroke="#5b6b80" d="M529.8,-61.58C529.8,-52.78 529.8,-42.01 
529.8,-32.71"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="533.3,-32.79 529.8,-22.79 
526.3,-32.79 533.3,-32.79"/>
+</g>
+<!-- closed&#45;&gt;waiting -->
+<g id="edge17" class="edge">
+<title>closed&#45;&gt;waiting</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M536.43,-501.63C528.49,-506.72 521.28,-513.16 516.55,-521.23 511.59,-529.68 
511.13,-539.87 512.62,-549.53"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="509.15,-550.05 514.78,-559.02 
515.97,-548.5 509.15,-550.05"/>
+<text xml:space="preserve" text-anchor="middle" x="547.67" y="-527.31" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
reopens</text>
+</g>
+</g>
+</svg>
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_states.md5sum 
b/dev/breeze/doc/ci/images/diagram_fork_ci_states.md5sum
new file mode 100644
index 00000000000..629b8f412d2
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_states.md5sum
@@ -0,0 +1 @@
+1570edf2d31def0a094a6b94b3c457d0
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_states.py 
b/dev/breeze/doc/ci/images/diagram_fork_ci_states.py
new file mode 100644
index 00000000000..a6da4e581ea
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_states.py
@@ -0,0 +1,92 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# /// script
+# requires-python = ">=3.11"
+# dependencies = [
+#    "rich>=13.6.0",
+#    "graphviz>=0.20.1",
+# ]
+# ///
+"""The fork CI reconciler's states for a gated pull request. Used in 
dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md and on the ADIP-12 
wiki page."""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+import graphviz
+from rich.console import Console
+
+MY_DIR = Path(__file__).parent
+MY_FILENAME = Path(__file__).with_suffix("").name
+
+console = Console(width=400, color_system="standard")
+
+DOT = r"""
+digraph states {
+  graph [rankdir=TB, fontname="Helvetica", fontsize=11, nodesep=0.45, 
ranksep=0.5, compound=true, bgcolor="white"];
+  node  [shape=box, style="rounded,filled", fillcolor="#f2f4f7", 
color="#5b6b80", fontname="Helvetica", fontsize=11];
+  edge  [fontname="Helvetica", fontsize=9, color="#5b6b80", 
fontcolor="#33404d"];
+
+  start [shape=circle, label="", width=0.22, fillcolor="#33404d", 
color="#33404d"];
+
+  subgraph cluster_gated {
+    label="gated (label: fork ci required)"; fontname="Helvetica"; 
fontsize=12; style="rounded"; color="#8a96a3";
+    setup_required [label="setup_required\nworkflow not enabled in the 
fork\n(draft)"];
+    awaiting_push  [label="awaiting_push\nworkflow enabled, no run\nfor the 
head commit (draft)"];
+    running        [label="running\nfork run in progress"];
+    failed         [label="failed\nrun failed, cancelled, timed out\nor could 
not start", fillcolor="#fde8e8", color="#b54848"];
+    green          [label="green\nno CI-defining change\nready for maintainer 
review", fillcolor="#e6f4ea", color="#3d8b57"];
+    green_unverified [label="green_unverified\nCI-defining change\nneeds 
project ci", fillcolor="#fff6db", color="#b38a1d"];
+  }
+
+  closed        [label="closed\nsetup_required or awaiting_push\nfor 30 days", 
fillcolor="#eceff3", color="#8a96a3"];
+  released      [label="released\nmaintainer added use project ci"];
+  ungated       [label="ungated\nbase branch without fork CI"];
+  gate_modified [label="gate_modified\nPR changes the gate (closed)", 
fillcolor="#fde8e8", color="#b54848"];
+  gate_bypassed [label="gate_bypassed\nproject CI ran without\nuse project ci 
(closed)", fillcolor="#fde8e8", color="#b54848"];
+
+  start -> setup_required [label="discovery adds\nfork ci required", 
lhead=cluster_gated];
+  setup_required -> awaiting_push [label="author enables\nthe workflow"];
+  awaiting_push -> running [label="author pushes"];
+  running -> failed [label="run fails"];
+  running -> green [label="run passes"];
+  running -> green_unverified [label="run passes,\nCI files changed"];
+  failed -> running [label="author pushes", style=dashed];
+  green -> running [label="author pushes", style=dashed];
+  green_unverified -> running [label="author pushes", style=dashed];
+  running -> setup_required [label="workflow disabled", style=dotted];
+  awaiting_push -> setup_required [label="workflow disabled", style=dotted];
+
+  awaiting_push -> closed [label="30 days", ltail=cluster_gated];
+  closed -> awaiting_push [label="reopened", style=dashed, 
lhead=cluster_gated];
+  green_unverified -> released [label="maintainer adds\nuse project ci", 
ltail=cluster_gated];
+  green -> ungated [label="base without fork CI", ltail=cluster_gated];
+  failed -> gate_modified [label="PR changes the gate", ltail=cluster_gated];
+  running -> gate_bypassed [label="gate tampering", ltail=cluster_gated];
+}
+"""
+
+
+def generate_diagram() -> None:
+    image_file = MY_DIR / f"{MY_FILENAME}.svg"
+    console.print(f"[bright_blue]Generating {image_file}")
+    graphviz.Source(DOT).render(outfile=image_file, format="svg", cleanup=True)
+    console.print(f"[green]Generated {image_file}")
+
+
+if __name__ == "__main__":
+    generate_diagram()
diff --git a/dev/breeze/doc/ci/images/diagram_fork_ci_states.svg 
b/dev/breeze/doc/ci/images/diagram_fork_ci_states.svg
new file mode 100644
index 00000000000..2509036529c
--- /dev/null
+++ b/dev/breeze/doc/ci/images/diagram_fork_ci_states.svg
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<!-- Generated by graphviz version 16.1.0 (20260904.0139)
+ -->
+<!-- Title: states Pages: 1 -->
+<svg width="749pt" height="522pt"
+ viewBox="0.00 0.00 749.00 522.00" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink";>
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 
518.34)">
+<title>states</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-518.34 745.38,-518.34 
745.38,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster_gated</title>
+<path fill="white" stroke="#8a96a3" d="M20,-83.5C20,-83.5 549,-83.5 549,-83.5 
555,-83.5 561,-89.5 561,-95.5 561,-95.5 561,-441 561,-441 561,-447 555,-453 
549,-453 549,-453 20,-453 20,-453 14,-453 8,-447 8,-441 8,-441 8,-95.5 8,-95.5 
8,-89.5 14,-83.5 20,-83.5"/>
+<text xml:space="preserve" text-anchor="middle" x="284.5" y="-437.6" 
font-family="Helvetica,sans-Serif" font-size="12.00">gated (label: fork ci 
required)</text>
+</g>
+<!-- start -->
+<g id="node1" class="node">
+<title>start</title>
+<ellipse fill="#33404d" stroke="#33404d" cx="370" cy="-506.42" rx="7.92" 
ry="7.92"/>
+</g>
+<!-- setup_required -->
+<g id="node2" class="node">
+<title>setup_required</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M442.12,-424.25C442.12,-424.25 
297.88,-424.25 297.88,-424.25 291.88,-424.25 285.88,-418.25 285.88,-412.25 
285.88,-412.25 285.88,-392.25 285.88,-392.25 285.88,-386.25 291.88,-380.25 
297.88,-380.25 297.88,-380.25 442.12,-380.25 442.12,-380.25 448.12,-380.25 
454.12,-386.25 454.12,-392.25 454.12,-392.25 454.12,-412.25 454.12,-412.25 
454.12,-418.25 448.12,-424.25 442.12,-424.25"/>
+<text xml:space="preserve" text-anchor="middle" x="370" y="-409.8" 
font-family="Helvetica,sans-Serif" font-size="11.00">setup_required</text>
+<text xml:space="preserve" text-anchor="middle" x="370" y="-397.8" 
font-family="Helvetica,sans-Serif" font-size="11.00">workflow not enabled in 
the fork</text>
+<text xml:space="preserve" text-anchor="middle" x="370" y="-385.8" 
font-family="Helvetica,sans-Serif" font-size="11.00">(draft)</text>
+</g>
+<!-- start&#45;&gt;setup_required -->
+<g id="edge1" class="edge">
+<title>start&#45;&gt;setup_required</title>
+<path fill="none" stroke="#5b6b80" d="M370,-498.07C370,-490.53 370,-478 
370,-464.49"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="373.5,-464.51 370,-454.51 
366.5,-464.51 373.5,-464.51"/>
+<text xml:space="preserve" text-anchor="middle" x="400.75" y="-471.95" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">discovery 
adds</text>
+<text xml:space="preserve" text-anchor="middle" x="400.75" y="-462.2" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">fork ci 
required</text>
+</g>
+<!-- awaiting_push -->
+<g id="node3" class="node">
+<title>awaiting_push</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M540.75,-324.75C540.75,-324.75 
421.25,-324.75 421.25,-324.75 415.25,-324.75 409.25,-318.75 409.25,-312.75 
409.25,-312.75 409.25,-292.75 409.25,-292.75 409.25,-286.75 415.25,-280.75 
421.25,-280.75 421.25,-280.75 540.75,-280.75 540.75,-280.75 546.75,-280.75 
552.75,-286.75 552.75,-292.75 552.75,-292.75 552.75,-312.75 552.75,-312.75 
552.75,-318.75 546.75,-324.75 540.75,-324.75"/>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-310.3" 
font-family="Helvetica,sans-Serif" font-size="11.00">awaiting_push</text>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-298.3" 
font-family="Helvetica,sans-Serif" font-size="11.00">workflow enabled, no 
run</text>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-286.3" 
font-family="Helvetica,sans-Serif" font-size="11.00">for the head commit 
(draft)</text>
+</g>
+<!-- setup_required&#45;&gt;awaiting_push -->
+<g id="edge2" class="edge">
+<title>setup_required&#45;&gt;awaiting_push</title>
+<path fill="none" stroke="#5b6b80" d="M420.51,-379.82C429.23,-374.81 
437.8,-368.95 445,-362.25 453.21,-354.61 460.24,-344.7 465.86,-335.21"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="468.88,-336.97 470.65,-326.53 
462.75,-333.59 468.88,-336.97"/>
+<text xml:space="preserve" text-anchor="middle" x="491.33" y="-353.7" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
enables</text>
+<text xml:space="preserve" text-anchor="middle" x="491.33" y="-343.95" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">the 
workflow</text>
+</g>
+<!-- awaiting_push&#45;&gt;setup_required -->
+<g id="edge11" class="edge">
+<title>awaiting_push&#45;&gt;setup_required</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="1,5" 
d="M409.02,-320.22C388.33,-326.49 369.66,-334.21 363.5,-342.75 358.12,-350.21 
357.32,-359.65 358.56,-368.76"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="355.12,-369.46 360.68,-378.48 
361.96,-367.96 355.12,-369.46"/>
+<text xml:space="preserve" text-anchor="middle" x="400.25" y="-348.82" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">workflow 
disabled</text>
+</g>
+<!-- running -->
+<g id="node4" class="node">
+<title>running</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M346.5,-231C346.5,-231 261.5,-231 
261.5,-231 255.5,-231 249.5,-225 249.5,-219 249.5,-219 249.5,-207 249.5,-207 
249.5,-201 255.5,-195 261.5,-195 261.5,-195 346.5,-195 346.5,-195 352.5,-195 
358.5,-201 358.5,-207 358.5,-207 358.5,-219 358.5,-219 358.5,-225 352.5,-231 
346.5,-231"/>
+<text xml:space="preserve" text-anchor="middle" x="304" y="-214.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">running</text>
+<text xml:space="preserve" text-anchor="middle" x="304" y="-202.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">fork run in progress</text>
+</g>
+<!-- awaiting_push&#45;&gt;running -->
+<g id="edge3" class="edge">
+<title>awaiting_push&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" d="M437.7,-280.28C410.84,-266.97 
376.38,-249.88 349.18,-236.4"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="351,-233.4 340.49,-232.09 
347.9,-239.67 351,-233.4"/>
+<text xml:space="preserve" text-anchor="middle" x="427.06" y="-254.2" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
pushes</text>
+</g>
+<!-- closed -->
+<g id="node8" class="node">
+<title>closed</title>
+<path fill="#eceff3" stroke="#8a96a3" d="M729.38,-235C729.38,-235 580.62,-235 
580.62,-235 574.62,-235 568.62,-229 568.62,-223 568.62,-223 568.62,-203 
568.62,-203 568.62,-197 574.62,-191 580.62,-191 580.62,-191 729.38,-191 
729.38,-191 735.38,-191 741.38,-197 741.38,-203 741.38,-203 741.38,-223 
741.38,-223 741.38,-229 735.38,-235 729.38,-235"/>
+<text xml:space="preserve" text-anchor="middle" x="655" y="-220.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">closed</text>
+<text xml:space="preserve" text-anchor="middle" x="655" y="-208.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">setup_required or 
awaiting_push</text>
+<text xml:space="preserve" text-anchor="middle" x="655" y="-196.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">for 30 days</text>
+</g>
+<!-- awaiting_push&#45;&gt;closed -->
+<g id="edge12" class="edge">
+<title>awaiting_push&#45;&gt;closed</title>
+<path fill="none" stroke="#5b6b80" d="M561,-250.35C568.46,-246.67 
576.29,-243.13 584.11,-239.81"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="585.26,-243.12 593.16,-236.06 
582.58,-236.65 585.26,-243.12"/>
+<text xml:space="preserve" text-anchor="middle" x="571.88" y="-254.2" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">30 
days</text>
+</g>
+<!-- running&#45;&gt;setup_required -->
+<g id="edge10" class="edge">
+<title>running&#45;&gt;setup_required</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="1,5" 
d="M304.09,-231.47C304.72,-253.8 307.63,-293.33 319.5,-324.75 325.66,-341.04 
335.78,-357.38 345.35,-370.76"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="342.48,-372.77 351.25,-378.71 
348.1,-368.6 342.48,-372.77"/>
+<text xml:space="preserve" text-anchor="middle" x="356.25" y="-299.07" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">workflow 
disabled</text>
+</g>
+<!-- failed -->
+<g id="node5" class="node">
+<title>failed</title>
+<path fill="#fde8e8" stroke="#b54848" d="M170.75,-135.5C170.75,-135.5 
33.25,-135.5 33.25,-135.5 27.25,-135.5 21.25,-129.5 21.25,-123.5 21.25,-123.5 
21.25,-103.5 21.25,-103.5 21.25,-97.5 27.25,-91.5 33.25,-91.5 33.25,-91.5 
170.75,-91.5 170.75,-91.5 176.75,-91.5 182.75,-97.5 182.75,-103.5 182.75,-103.5 
182.75,-123.5 182.75,-123.5 182.75,-129.5 176.75,-135.5 170.75,-135.5"/>
+<text xml:space="preserve" text-anchor="middle" x="102" y="-121.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">failed</text>
+<text xml:space="preserve" text-anchor="middle" x="102" y="-109.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">run failed, cancelled, 
timed out</text>
+<text xml:space="preserve" text-anchor="middle" x="102" y="-97.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">or could not start</text>
+</g>
+<!-- running&#45;&gt;failed -->
+<g id="edge4" class="edge">
+<title>running&#45;&gt;failed</title>
+<path fill="none" stroke="#5b6b80" d="M249.19,-211.8C181.07,-210.24 
70.58,-203.01 44.75,-173 35.96,-162.79 41.54,-152.11 52.04,-142.72"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="53.96,-145.66 59.69,-136.75 
49.65,-140.15 53.96,-145.66"/>
+<text xml:space="preserve" text-anchor="middle" x="60.88" y="-159.57" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">run 
fails</text>
+</g>
+<!-- green -->
+<g id="node6" class="node">
+<title>green</title>
+<path fill="#e6f4ea" stroke="#3d8b57" d="M352.25,-135.5C352.25,-135.5 
229.75,-135.5 229.75,-135.5 223.75,-135.5 217.75,-129.5 217.75,-123.5 
217.75,-123.5 217.75,-103.5 217.75,-103.5 217.75,-97.5 223.75,-91.5 
229.75,-91.5 229.75,-91.5 352.25,-91.5 352.25,-91.5 358.25,-91.5 364.25,-97.5 
364.25,-103.5 364.25,-103.5 364.25,-123.5 364.25,-123.5 364.25,-129.5 
358.25,-135.5 352.25,-135.5"/>
+<text xml:space="preserve" text-anchor="middle" x="291" y="-121.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">green</text>
+<text xml:space="preserve" text-anchor="middle" x="291" y="-109.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">no CI&#45;defining 
change</text>
+<text xml:space="preserve" text-anchor="middle" x="291" y="-97.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">ready for maintainer 
review</text>
+</g>
+<!-- running&#45;&gt;green -->
+<g id="edge5" class="edge">
+<title>running&#45;&gt;green</title>
+<path fill="none" stroke="#5b6b80" d="M249.3,-201.32C234.03,-195.55 
219.12,-186.66 210,-173 201.98,-160.99 207.75,-150.6 219.18,-142.05"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="221.06,-145.01 227.62,-136.69 
217.3,-139.1 221.06,-145.01"/>
+<text xml:space="preserve" text-anchor="middle" x="232.5" y="-159.57" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">run 
passes</text>
+</g>
+<!-- green_unverified -->
+<g id="node7" class="node">
+<title>green_unverified</title>
+<path fill="#fff6db" stroke="#b38a1d" d="M522.38,-135.5C522.38,-135.5 
439.62,-135.5 439.62,-135.5 433.62,-135.5 427.62,-129.5 427.62,-123.5 
427.62,-123.5 427.62,-103.5 427.62,-103.5 427.62,-97.5 433.62,-91.5 
439.62,-91.5 439.62,-91.5 522.38,-91.5 522.38,-91.5 528.38,-91.5 534.38,-97.5 
534.38,-103.5 534.38,-103.5 534.38,-123.5 534.38,-123.5 534.38,-129.5 
528.38,-135.5 522.38,-135.5"/>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-121.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">green_unverified</text>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-109.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">CI&#45;defining 
change</text>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-97.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">needs project ci</text>
+</g>
+<!-- running&#45;&gt;green_unverified -->
+<g id="edge6" class="edge">
+<title>running&#45;&gt;green_unverified</title>
+<path fill="none" stroke="#5b6b80" d="M325.98,-194.76C342.46,-182.33 
365.89,-165.66 388,-153.5 397.22,-148.43 407.24,-143.59 417.18,-139.15"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="418.3,-142.48 426.07,-135.28 
415.51,-136.06 418.3,-142.48"/>
+<text xml:space="preserve" text-anchor="middle" x="421" y="-164.45" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">run 
passes,</text>
+<text xml:space="preserve" text-anchor="middle" x="421" y="-154.7" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">CI files 
changed</text>
+</g>
+<!-- gate_bypassed -->
+<g id="node12" class="node">
+<title>gate_bypassed</title>
+<path fill="#fde8e8" stroke="#b54848" d="M677.12,-135.5C677.12,-135.5 
580.88,-135.5 580.88,-135.5 574.88,-135.5 568.88,-129.5 568.88,-123.5 
568.88,-123.5 568.88,-103.5 568.88,-103.5 568.88,-97.5 574.88,-91.5 
580.88,-91.5 580.88,-91.5 677.12,-91.5 677.12,-91.5 683.12,-91.5 689.12,-97.5 
689.12,-103.5 689.12,-103.5 689.12,-123.5 689.12,-123.5 689.12,-129.5 
683.12,-135.5 677.12,-135.5"/>
+<text xml:space="preserve" text-anchor="middle" x="629" y="-121.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">gate_bypassed</text>
+<text xml:space="preserve" text-anchor="middle" x="629" y="-109.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">project CI ran 
without</text>
+<text xml:space="preserve" text-anchor="middle" x="629" y="-97.05" 
font-family="Helvetica,sans-Serif" font-size="11.00">use project ci 
(closed)</text>
+</g>
+<!-- running&#45;&gt;gate_bypassed -->
+<g id="edge17" class="edge">
+<title>running&#45;&gt;gate_bypassed</title>
+<path fill="none" stroke="#5b6b80" d="M561,-162.29C572.52,-156.1 
583.57,-149.03 593.29,-142.27"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="594.98,-145.36 601.07,-136.69 
590.91,-139.67 594.98,-145.36"/>
+<text xml:space="preserve" text-anchor="middle" x="605.69" y="-159.57" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">gate 
tampering</text>
+</g>
+<!-- failed&#45;&gt;running -->
+<g id="edge7" class="edge">
+<title>failed&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M110.73,-135.9C116.95,-148.52 126.51,-163.79 139.5,-173 168.09,-193.27 
205.81,-203.12 237.91,-207.85"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="237.13,-211.28 247.5,-209.13 
238.05,-204.34 237.13,-211.28"/>
+<text xml:space="preserve" text-anchor="middle" x="168.75" y="-159.57" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
pushes</text>
+</g>
+<!-- gate_modified -->
+<g id="node11" class="node">
+<title>gate_modified</title>
+<path fill="#fde8e8" stroke="#b54848" d="M169.25,-36C169.25,-36 34.75,-36 
34.75,-36 28.75,-36 22.75,-30 22.75,-24 22.75,-24 22.75,-12 22.75,-12 22.75,-6 
28.75,0 34.75,0 34.75,0 169.25,0 169.25,0 175.25,0 181.25,-6 181.25,-12 
181.25,-12 181.25,-24 181.25,-24 181.25,-30 175.25,-36 169.25,-36"/>
+<text xml:space="preserve" text-anchor="middle" x="102" y="-19.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">gate_modified</text>
+<text xml:space="preserve" text-anchor="middle" x="102" y="-7.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">PR changes the gate 
(closed)</text>
+</g>
+<!-- failed&#45;&gt;gate_modified -->
+<g id="edge16" class="edge">
+<title>failed&#45;&gt;gate_modified</title>
+<path fill="none" stroke="#5b6b80" d="M102,-83.5C102,-72.17 102,-59.21 
102,-47.89"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="105.5,-47.89 102,-37.89 
98.5,-47.89 105.5,-47.89"/>
+<text xml:space="preserve" text-anchor="middle" x="144.75" y="-60.08" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">PR changes 
the gate</text>
+</g>
+<!-- green&#45;&gt;running -->
+<g id="edge8" class="edge">
+<title>green&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M290.22,-135.74C290.14,-146.9 290.57,-160.76 292.5,-173 293.05,-176.51 
293.83,-180.15 294.73,-183.73"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="291.35,-184.63 297.45,-193.29 
298.08,-182.72 291.35,-184.63"/>
+<text xml:space="preserve" text-anchor="middle" x="321.75" y="-159.57" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
pushes</text>
+</g>
+<!-- ungated -->
+<g id="node10" class="node">
+<title>ungated</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M353,-36C353,-36 229,-36 229,-36 
223,-36 217,-30 217,-24 217,-24 217,-12 217,-12 217,-6 223,0 229,0 229,0 353,0 
353,0 359,0 365,-6 365,-12 365,-12 365,-24 365,-24 365,-30 359,-36 353,-36"/>
+<text xml:space="preserve" text-anchor="middle" x="291" y="-19.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">ungated</text>
+<text xml:space="preserve" text-anchor="middle" x="291" y="-7.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">base branch without fork 
CI</text>
+</g>
+<!-- green&#45;&gt;ungated -->
+<g id="edge15" class="edge">
+<title>green&#45;&gt;ungated</title>
+<path fill="none" stroke="#5b6b80" d="M291,-83.5C291,-72.17 291,-59.21 
291,-47.89"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="294.5,-47.89 291,-37.89 
287.5,-47.89 294.5,-47.89"/>
+<text xml:space="preserve" text-anchor="middle" x="331.12" y="-60.08" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">base without 
fork CI</text>
+</g>
+<!-- green_unverified&#45;&gt;running -->
+<g id="edge9" class="edge">
+<title>green_unverified&#45;&gt;running</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M477.58,-135.96C474.5,-148.45 468.73,-163.55 458,-173 433.96,-194.18 
400.03,-204.16 370.13,-208.76"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="369.85,-205.26 360.4,-210.06 
370.78,-212.2 369.85,-205.26"/>
+<text xml:space="preserve" text-anchor="middle" x="500.78" y="-159.57" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">author 
pushes</text>
+</g>
+<!-- released -->
+<g id="node9" class="node">
+<title>released</title>
+<path fill="#f2f4f7" stroke="#5b6b80" d="M551.62,-36C551.62,-36 410.38,-36 
410.38,-36 404.38,-36 398.38,-30 398.38,-24 398.38,-24 398.38,-12 398.38,-12 
398.38,-6 404.38,0 410.38,0 410.38,0 551.62,0 551.62,0 557.62,0 563.62,-6 
563.62,-12 563.62,-12 563.62,-24 563.62,-24 563.62,-30 557.62,-36 551.62,-36"/>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-19.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">released</text>
+<text xml:space="preserve" text-anchor="middle" x="481" y="-7.55" 
font-family="Helvetica,sans-Serif" font-size="11.00">maintainer added use 
project ci</text>
+</g>
+<!-- green_unverified&#45;&gt;released -->
+<g id="edge14" class="edge">
+<title>green_unverified&#45;&gt;released</title>
+<path fill="none" stroke="#5b6b80" d="M481,-83.5C481,-72.17 481,-59.21 
481,-47.89"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="484.5,-47.89 481,-37.89 
477.5,-47.89 484.5,-47.89"/>
+<text xml:space="preserve" text-anchor="middle" x="514" y="-64.95" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">maintainer 
adds</text>
+<text xml:space="preserve" text-anchor="middle" x="514" y="-55.2" 
font-family="Helvetica,sans-Serif" font-size="9.00" fill="#33404d">use project 
ci</text>
+</g>
+<!-- closed&#45;&gt;awaiting_push -->
+<g id="edge13" class="edge">
+<title>closed&#45;&gt;awaiting_push</title>
+<path fill="none" stroke="#5b6b80" stroke-dasharray="5,2" 
d="M631.14,-235.4C619.86,-244.72 605.85,-255.22 592,-262.75 583.04,-267.62 
573.35,-272.08 563.56,-276.1"/>
+<polygon fill="#5b6b80" stroke="#5b6b80" points="569.98,-269.27 562.37,-276.65 
572.92,-275.62 569.98,-269.27"/>
+<text xml:space="preserve" text-anchor="middle" x="626.73" y="-254.2" 
font-family="Helvetica,sans-Serif" font-size="9.00" 
fill="#33404d">reopened</text>
+</g>
+</g>
+</svg>

Reply via email to