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

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 26d53bd1e84 Resolve the constraints a release ships instead of tagging 
whatever exists (#71040)
26d53bd1e84 is described below

commit 26d53bd1e84088bf7cb7ab11dcfde3d8e6b99048
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Aug 4 11:51:57 2026 +0200

    Resolve the constraints a release ships instead of tagging whatever exists 
(#71040)
    
    * Resolve the constraints a release ships instead of tagging whatever exists
    
    The constraints published with a version were whatever the constraints-X-Y
    branch happened to hold when the release ran - a resolution made by the 
last CI
    build, for the sources at that moment, rather than for the version being
    released. The candidate tagged that tip and the final release retagged the
    candidate, so no release ever resolved constraints of its own.
    
    A candidate now resolves them allowing pre-releases: the providers of the 
wave
    being voted on exist on PyPI only as rc versions, so constraints that refuse
    pre-releases cannot describe what a tester is asked to install. They land 
on a
    branch of the candidate's own, leaving the branch every other build reads 
where
    it was. The final release cannot promote those by retagging - a released 
version
    must never pin an rc - so it resolves the same set again, without 
pre-releases,
    and commits onto constraints-X-Y, which is what makes the released 
constraints
    the baseline everything downstream reads.
    
    Which of the two happens is derived from the version, so the stage cannot 
be set
    inconsistently with it. The work runs on CI runners rather than on the 
release
    manager's machine, which would otherwise need a CI image for every supported
    Python before it could cut a release, and is reachable on its own through
    `breeze workflow-run release-constraints` for redoing a candidate's 
constraints
    or producing them for a release cut before this existed.
    
    * Let only the providers answer with a pre-release
    
    `--pre` applies to the whole resolution, so a candidate's constraints could 
pin
    a pre-release of any dependency - a beta of some third-party library would 
end
    up in what a release ships, which is not what allowing rc providers was 
meant to
    permit. uv considers a pre-release for a package only when a requirement 
for it
    mentions one, so naming the providers with a pre-release lower bound 
confines
    the allowance to them and leaves every other package on the default policy.
    
    * State the pre-release scoping rather than leaning on uv's default
    
    The rc lower bounds already confined pre-releases to the providers, but only
    because uv's default strategy happens to permit them for explicitly marked
    packages. Naming `explicit` says that in the command instead of leaving it 
to a
    default that could change, and drops the `if-necessary` half of that 
default -
    the part that would let a package nobody marked resolve to a pre-release 
when no
    final version satisfies it.
    
    * Document what a release manager now sees at the constraints step
    
    The candidate half was undocumented - the release notes described only what 
the
    final release does, leaving a release manager to infer why a candidate 
suddenly
    grows a branch and a tag of its own, and why its constraints pin rc 
providers
    when nothing else in them is a pre-release. Both stages and the scope of the
    pre-release allowance are stated where each is reached.
    
    * Register the new constraints command where the docs checks look for it
    
    A command has to be grouped in its `*_commands_config.py` and embedded in 
one of
    the breeze docs, or the static checks reject it - `--allow-pre-releases` 
had no
    group and `workflow-run release-constraints` had a generated screenshot that
    nothing referenced. The two `setup` screenshots move because the command 
list
    they render is exactly what gained the new entry.
---
 .github/workflows/generate-constraints.yml         |  16 +-
 .github/workflows/release-constraints.yml          | 248 +++++++++++++++++++++
 dev/README_RELEASE_AIRFLOW.md                      |  50 ++++-
 dev/breeze/doc/09_release_management_tasks.rst     |  21 ++
 ...put_release-management_generate-constraints.svg |  54 +++--
 ...put_release-management_generate-constraints.txt |   2 +-
 .../output_setup_check-all-params-in-groups.svg    |  24 +-
 .../output_setup_check-all-params-in-groups.txt    |   2 +-
 .../output_setup_regenerate-command-images.svg     |   2 +-
 .../output_setup_regenerate-command-images.txt     |   2 +-
 dev/breeze/doc/images/output_workflow-run.svg      |  14 +-
 dev/breeze/doc/images/output_workflow-run.txt      |   2 +-
 .../output_workflow-run_release-constraints.svg    | 138 ++++++++++++
 .../output_workflow-run_release-constraints.txt    |   1 +
 .../commands/release_candidate_command.py          |  31 +--
 .../src/airflow_breeze/commands/release_command.py |  48 +---
 .../commands/release_management_commands.py        |   4 +
 .../commands/release_management_commands_config.py |   1 +
 .../airflow_breeze/commands/workflow_commands.py   |  44 ++++
 .../commands/workflow_commands_config.py           |  12 +-
 .../airflow_breeze/utils/release_constraints.py    |  61 +++++
 dev/breeze/tests/test_release_command.py           |  57 ++---
 dev/breeze/tests/test_release_constraints.py       |  91 ++++++++
 scripts/in_container/run_generate_constraints.py   |  48 ++++
 .../in_container/test_run_generate_constraints.py  |  39 ++++
 25 files changed, 851 insertions(+), 161 deletions(-)

diff --git a/.github/workflows/generate-constraints.yml 
b/.github/workflows/generate-constraints.yml
index 28c078a3563..8178cde09e0 100644
--- a/.github/workflows/generate-constraints.yml
+++ b/.github/workflows/generate-constraints.yml
@@ -44,6 +44,15 @@ on:  # yamllint disable-line rule:truthy
         description: "Whether to generate PyPI constraints (true/false)"
         required: true
         type: string
+      allow-pre-releases:
+        description: >
+          Whether the PyPI constraints may pin pre-release providers 
(true/false). Set when
+          constraints are cut for a release candidate, whose providers are on 
PyPI only as rc
+          versions. Scoped to apache-airflow-providers-* - no other package 
can resolve to a
+          pre-release.
+        required: false
+        default: "false"
+        type: string
       debug-resources:
         description: "Whether to run in debug mode (true/false)"
         required: true
@@ -137,9 +146,14 @@ jobs:
       - name: "PyPI constraints"
         shell: bash
         timeout-minutes: 25
+        env:
+          # Deliberately not named ALLOW_PRE_RELEASES: that is the option's 
own envvar, and having
+          # both paths set it would make it unclear which one is in force. 
Empty on a normal run,
+          # so the unquoted expansion below contributes no argument at all.
+          PRE_RELEASE_FLAG: ${{ inputs.allow-pre-releases == 'true' && 
'--allow-pre-releases' || '' }}
         run: |
           breeze release-management generate-constraints 
--airflow-constraints-mode constraints \
-            --answer yes --python "${PYTHON_VERSION}"
+            --answer yes --python "${PYTHON_VERSION}" ${PRE_RELEASE_FLAG}
         if: inputs.generate-pypi-constraints == 'true'
       - name: "Check for provider downgrade alert"
         id: downgrade-alert
diff --git a/.github/workflows/release-constraints.yml 
b/.github/workflows/release-constraints.yml
new file mode 100644
index 00000000000..268abf83341
--- /dev/null
+++ b/.github/workflows/release-constraints.yml
@@ -0,0 +1,248 @@
+# 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.
+#
+# Resolves the constraints that a release ships, rather than tagging whatever 
the
+# `constraints-X-Y` branch happened to hold when the release ran.
+#
+# The stage is derived from the version, so the two cannot be mismatched by 
hand:
+#
+#   * a candidate (`3.1.3rc1`) resolves with pre-releases allowed - the 
providers of the wave
+#     being voted on exist on PyPI only as rc versions - and lands on a branch 
of its own, so a
+#     candidate never moves the branch every other build reads;
+#   * a final (`3.1.3`) resolves without them, against the providers now 
published as finals, and
+#     commits onto `constraints-X-Y` itself, which is what makes the released 
constraints the
+#     baseline everything downstream reads.
+#
+# A final therefore cannot be produced by retagging a candidate: a released 
version must never
+# pin an rc.
+---
+name: Release constraints
+on:  # yamllint disable-line rule:truthy
+  workflow_dispatch:
+    inputs:
+      version:
+        description: "Version the constraints belong to, e.g. 3.1.3rc1 or 
3.1.3"
+        required: true
+        type: string
+      ref:
+        description: "Ref the constraints are resolved from, e.g. v3-1-stable 
or the release tag"
+        required: true
+        type: string
+permissions:
+  contents: read
+concurrency:
+  group: release-constraints-${{ inputs.version }}
+  cancel-in-progress: false
+jobs:
+  build-info:
+    timeout-minutes: 10
+    name: "Build info"
+    runs-on: ["ubuntu-22.04"]
+    if: contains(fromJSON('[
+      "ashb",
+      "eladkal",
+      "ephraimbuddy",
+      "jedcunningham",
+      "kaxil",
+      "pierrejeambrun",
+      "potiuk",
+      "utkarsharma2",
+      "vincbeck",
+      ]'), github.event.sender.login)
+    outputs:
+      python-versions: ${{ steps.selective-checks.outputs.python-versions }}
+      python-versions-list-as-string: ${{ 
steps.selective-checks.outputs.python-versions-list-as-string }}
+      default-branch: ${{ steps.selective-checks.outputs.default-branch }}
+      default-constraints-branch: ${{ 
steps.selective-checks.outputs.default-constraints-branch }}
+      constraints-branch: ${{ steps.stage.outputs.constraints-branch }}
+      target-branch: ${{ steps.stage.outputs.target-branch }}
+      allow-pre-releases: ${{ steps.stage.outputs.allow-pre-releases }}
+    steps:
+      - name: "Cleanup repo"
+        shell: bash
+        run: sudo rm -rf ${GITHUB_WORKSPACE}/*
+      - name: "Checkout ${{ inputs.ref }}"
+        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # 
v7.0.1
+        with:
+          ref: ${{ inputs.ref }}
+          fetch-depth: 2
+          persist-credentials: false
+      - name: "Install Breeze"
+        uses: ./.github/actions/breeze
+        id: breeze
+      - name: "Save github context to file"
+        # See ci-amd.yml for the full rationale: avoids ARG_MAX on big PRs by 
writing the
+        # github context to a file, and the single-quoted heredoc makes the 
zizmor
+        # template-injection finding a false positive (no bash expansion 
happens inside it).
+        shell: bash
+        run: |  # zizmor: ignore[template-injection]
+          cat > "${RUNNER_TEMP}/github_context.json" << 
'__GITHUB_CONTEXT_END__'
+          ${{ toJson(github) }}
+          __GITHUB_CONTEXT_END__
+      - name: Selective checks
+        id: selective-checks
+        env:
+          PR_LABELS: "[]"
+          COMMIT_REF: "${{ inputs.ref }}"
+          VERBOSE: "false"
+          GITHUB_CONTEXT_INPUT: "${{ runner.temp }}/github_context.json"
+        run: breeze ci selective-check 2>> ${GITHUB_OUTPUT}
+      - name: "Derive the release stage from the version"
+        id: stage
+        shell: bash
+        env:
+          VERSION: ${{ inputs.version }}
+        run: |
+          if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$ ]]; then
+            echo "'${VERSION}' is not a release version - expected X.Y.Z or 
X.Y.ZrcN." >&2
+            exit 1
+          fi
+          major="$(echo "${VERSION}" | cut -d. -f1)"
+          minor="$(echo "${VERSION}" | cut -d. -f2)"
+          constraints_branch="constraints-${major}-${minor}"
+          echo "constraints-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
+          if [[ "${VERSION}" == *rc* ]]; then
+            echo "allow-pre-releases=true" >> "${GITHUB_OUTPUT}"
+            echo "target-branch=constraints-${VERSION}" >> "${GITHUB_OUTPUT}"
+          else
+            echo "allow-pre-releases=false" >> "${GITHUB_OUTPUT}"
+            echo "target-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
+          fi
+      - name: "Parameters summary"
+        shell: bash
+        env:
+          VERSION: ${{ inputs.version }}
+          REF: ${{ inputs.ref }}
+          CONSTRAINTS_BRANCH: ${{ steps.stage.outputs.constraints-branch }}
+          TARGET_BRANCH: ${{ steps.stage.outputs.target-branch }}
+          ALLOW_PRE_RELEASES: ${{ steps.stage.outputs.allow-pre-releases }}
+        run: |
+          {
+            echo "## Release constraints"
+            echo ""
+            echo "| Parameter | Value |"
+            echo "|---|---|"
+            echo "| Version | \`${VERSION}\` |"
+            echo "| Resolved from ref | \`${REF}\` |"
+            echo "| Branched off | \`${CONSTRAINTS_BRANCH}\` |"
+            echo "| Committed to | \`${TARGET_BRANCH}\` |"
+            echo "| Pre-releases allowed | \`${ALLOW_PRE_RELEASES}\` |"
+            echo "| Tagged | \`constraints-${VERSION}\` |"
+          } | tee -a "${GITHUB_STEP_SUMMARY}"
+
+  build-ci-images:
+    name: "Build CI images"
+    needs: [build-info]
+    uses: ./.github/workflows/ci-image-build.yml
+    permissions:
+      contents: read
+      packages: write
+    with:
+      runners: '["ubuntu-22.04"]'
+      platform: "linux/amd64"
+      push-image: "false"
+      upload-image-artifact: "true"
+      upload-mount-cache-artifact: "false"
+      python-versions: ${{ needs.build-info.outputs.python-versions }}
+      branch: ${{ needs.build-info.outputs.default-branch }}
+      constraints-branch: ${{ 
needs.build-info.outputs.default-constraints-branch }}
+      checkout-ref: ${{ inputs.ref }}
+      use-uv: "true"
+      upgrade-to-newer-dependencies: "false"
+      docker-cache: "registry"
+      disable-airflow-repo-cache: "false"
+
+  generate-constraints:
+    name: "Generate constraints"
+    needs: [build-info, build-ci-images]
+    uses: ./.github/workflows/generate-constraints.yml
+    with:
+      runners: '["ubuntu-22.04"]'
+      platform: "linux/amd64"
+      python-versions-list-as-string: ${{ 
needs.build-info.outputs.python-versions-list-as-string }}
+      python-versions: ${{ needs.build-info.outputs.python-versions }}
+      generate-pypi-constraints: "true"
+      # Only the PyPI constraints are what a release ships; the other modes 
serve CI, and
+      # regenerating them here would move them for reasons unrelated to the 
release.
+      generate-no-providers-constraints: "false"
+      allow-pre-releases: ${{ needs.build-info.outputs.allow-pre-releases }}
+      debug-resources: "false"
+      checkout-ref: ${{ inputs.ref }}
+      use-uv: "true"
+    secrets:
+      SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
+
+  publish-constraints:
+    runs-on: ["ubuntu-22.04"]
+    timeout-minutes: 20
+    name: "Publish and tag constraints"
+    needs: [build-info, generate-constraints]
+    permissions:
+      contents: write
+      packages: read
+    env:
+      VERSION: ${{ inputs.version }}
+      CONSTRAINTS_BRANCH: ${{ needs.build-info.outputs.constraints-branch }}
+      TARGET_BRANCH: ${{ needs.build-info.outputs.target-branch }}
+    steps:
+      - name: "Cleanup repo"
+        shell: bash
+        run: sudo rm -rf ${GITHUB_WORKSPACE}/*
+      - name: "Checkout ${{ inputs.ref }}"
+        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # 
v7.0.1
+        with:
+          ref: ${{ inputs.ref }}
+          persist-credentials: false
+      # Branched off the shared constraints branch in both cases; what differs 
is where the commit
+      # ends up, which the checkout below decides.
+      - name: "Checkout ${{ needs.build-info.outputs.constraints-branch }}"
+        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # 
v7.0.1
+        with:
+          path: "constraints"
+          ref: ${{ needs.build-info.outputs.constraints-branch }}
+          persist-credentials: true
+          fetch-depth: 0
+      - name: "Download constraints from the generate-constraints job"
+        uses: 
actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c  # v8.0.1
+        with:
+          pattern: constraints-*
+          path: ./files
+      - name: "Switch to ${{ needs.build-info.outputs.target-branch }}"
+        working-directory: "constraints"
+        shell: bash
+        run: git switch -c "${TARGET_BRANCH}" 2>/dev/null || git switch 
"${TARGET_BRANCH}"
+      - name: "Diff in constraints for Python: ${{ 
needs.build-info.outputs.python-versions-list-as-string }}"
+        run: ./scripts/ci/constraints/ci_diff_constraints.sh
+      - name: "Commit changed constraint files"
+        run: ./scripts/ci/constraints/ci_commit_constraints.sh
+      - name: "Push ${{ needs.build-info.outputs.target-branch }}"
+        working-directory: "constraints"
+        shell: bash
+        run: git push origin "${TARGET_BRANCH}"
+      - name: "Tag constraints-${{ inputs.version }}"
+        working-directory: "constraints"
+        shell: bash
+        run: |
+          git tag -a "constraints-${VERSION}" -m "Constraints for Apache 
Airflow ${VERSION}"
+          git push origin "constraints-${VERSION}"
+      - name: "Summary"
+        shell: bash
+        run: |
+          {
+            echo "Constraints for \`${VERSION}\` are on \`${TARGET_BRANCH}\`,"
+            echo "tagged \`constraints-${VERSION}\`."
+          } | tee -a "${GITHUB_STEP_SUMMARY}"
diff --git a/dev/README_RELEASE_AIRFLOW.md b/dev/README_RELEASE_AIRFLOW.md
index 33881515df9..0841475566c 100644
--- a/dev/README_RELEASE_AIRFLOW.md
+++ b/dev/README_RELEASE_AIRFLOW.md
@@ -588,6 +588,18 @@ still works but is no longer recommended.
         --sync-branch ${SYNC_BRANCH}
    ```
 
+   Note: when it reaches the constraints step, `start-rc-process` triggers the 
`Release
+   constraints` workflow and waits. The candidate resolves constraints of its 
own rather than
+   tagging the `constraints-X-Y` branch tip, and it resolves them **allowing 
pre-releases for the
+   providers** — the providers of the wave being voted on exist on PyPI only 
as `rcN` versions, so
+   constraints that refused them could not describe what a tester is asked to 
install. The
+   allowance is scoped to `apache-airflow-providers-*`; no other package can 
resolve to a
+   pre-release, so a beta of some third-party library cannot slip into what 
the candidate ships.
+
+   The result lands on a branch of its own (`constraints-${VERSION_RC}`) and 
is tagged
+   `constraints-${VERSION_RC}`. The shared `constraints-X-Y` branch is left 
where it was — only
+   the final release moves it.
+
    **Testing the start-rc-process command:**
    Before running the actual release command, you can safely test it using:
 
@@ -1372,15 +1384,37 @@ breeze release-management start-release \
 
 Note: The `--task-sdk-version` parameter is optional. If you are releasing 
Airflow without a corresponding Task SDK release, you can omit this parameter.
 
-Note: When it reaches the constraints step, `start-release` asks whether to 
base the final
-`constraints-${VERSION}` tag on the latest `constraints-X-Y` branch tip 
instead of the RC
-constraints tag. The RC constraints are frozen when the RC is cut, so if any 
providers were
-released (or constraints were otherwise refreshed - see
+Note: When it reaches the constraints step, `start-release` resolves the 
constraints again rather
+than promoting the ones the RC was cut with. The RC constraints deliberately 
pin pre-releases -
+that wave's providers exist on PyPI only as `rcN` versions at candidate time - 
so they can never
+become the released constraints by retagging. The regeneration runs without 
pre-releases against
+the same providers now published as finals, commits the result onto the 
`constraints-X-Y` branch,
+pushes it, and tags it `constraints-${VERSION}`. That commit is what makes the 
released
+constraints the new baseline, so refreshing the branch beforehand (see
 
[MANUALLY_GENERATING_IMAGE_CACHE_AND_CONSTRAINTS.md](MANUALLY_GENERATING_IMAGE_CACHE_AND_CONSTRAINTS.md))
-after the last RC and you want the released constraints to reflect that, 
answer **yes** to tag the
-`constraints-X-Y` branch tip. Otherwise (the default) the final tag matches 
the RC exactly. If you
-do refresh, run the `Update constraints` workflow from `main` with `ref` set 
to the ref you are
-releasing (typically `v3-*-stable`) **before** running `start-release`.
+is no longer necessary.
+
+The resolution runs on CI runners through the `Release constraints` workflow, 
so the release
+manager's machine does not need a CI image for every supported Python. 
`start-release` triggers it
+and waits. You can also run it on its own - to redo a candidate's constraints, 
or to produce them
+for a release cut before this existed:
+
+```shell script
+breeze workflow-run release-constraints --version ${VERSION} --ref v3-1-stable
+```
+
+The workflow derives the stage from `--version` alone, so there is no separate 
switch that could
+disagree with the version:
+
+| `--version` | Pre-releases | Lands on | Tagged |
+|---|---|---|---|
+| `3.1.3rc1` | providers only | `constraints-3.1.3rc1`, branched off 
`constraints-3-1` | `constraints-3.1.3rc1` |
+| `3.1.3` | none | `constraints-3-1` (commit) | `constraints-3.1.3` |
+
+"Providers only" is literal: the resolution names every 
`apache-airflow-providers-*` with a
+pre-release lower bound and runs under uv's `explicit` strategy, which permits 
a pre-release solely
+for a package marked that way. Nothing else in the dependency graph can 
resolve to one — which is
+why `--pre` is not used, since it would apply to the whole resolution.
 
 
 4. Make sure to update Airflow version in ``v3-*-test`` branch after 
cherry-picking to X.Y.1 in
diff --git a/dev/breeze/doc/09_release_management_tasks.rst 
b/dev/breeze/doc/09_release_management_tasks.rst
index 70453de37db..40694e437c6 100644
--- a/dev/breeze/doc/09_release_management_tasks.rst
+++ b/dev/breeze/doc/09_release_management_tasks.rst
@@ -1080,6 +1080,27 @@ These are all available flags of ``workflow-run 
publish-docs`` command:
   :width: 100%
   :alt: Breeze workflow-run publish-docs
 
+Resolving the constraints for a release
+"""""""""""""""""""""""""""""""""""""""
+
+To trigger the GitHub Actions workflow that resolves, publishes and tags the 
constraints belonging to a
+release, you can use the ``breeze workflow-run release-constraints`` command. 
The release commands trigger
+it themselves, so this is for redoing a candidate's constraints or producing 
them for a release cut before
+the workflow existed.
+
+The stage is derived from ``--version`` alone, so it cannot be set 
inconsistently with it. A candidate
+(``3.1.3rc1``) resolves allowing pre-releases for 
``apache-airflow-providers-*`` — the providers of the wave
+being voted on are on PyPI only as ``rcN`` versions — and lands on a branch of 
its own, leaving the shared
+``constraints-X-Y`` branch where it was. A final (``3.1.3``) resolves without 
them and commits onto
+``constraints-X-Y``, which is what makes the released constraints the baseline 
everything downstream reads.
+
+These are all available flags of ``workflow-run release-constraints`` command:
+
+.. image:: ./images/output_workflow-run_release-constraints.svg
+  :target: 
https://raw.githubusercontent.com/apache/airflow/main/dev/breeze/doc/images/output_workflow-run_release-constraints.svg
+  :width: 100%
+  :alt: Breeze workflow-run release-constraints
+
 Publishing the schema files to S3
 """""""""""""""""""""""""""""""""
 
diff --git 
a/dev/breeze/doc/images/output_release-management_generate-constraints.svg 
b/dev/breeze/doc/images/output_release-management_generate-constraints.svg
index 9a2a1328ea1..07e3538c1ce 100644
--- a/dev/breeze/doc/images/output_release-management_generate-constraints.svg
+++ b/dev/breeze/doc/images/output_release-management_generate-constraints.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 757.5999999999999" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 806.4" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -43,7 +43,7 @@
 
     <defs>
     <clipPath 
id="breeze-release-management-generate-constraints-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="706.5999999999999" />
+      <rect x="0" y="0" width="1463.0" height="755.4" />
     </clipPath>
     <clipPath id="breeze-release-management-generate-constraints-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -129,9 +129,15 @@
 <clipPath id="breeze-release-management-generate-constraints-line-27">
     <rect x="0" y="660.3" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-release-management-generate-constraints-line-28">
+    <rect x="0" y="684.7" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-release-management-generate-constraints-line-29">
+    <rect x="0" y="709.1" width="1464" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="755.6" rx="8"/><text 
class="breeze-release-management-generate-constraints-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;release-management&#160;generate-constraints</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="804.4" rx="8"/><text 
class="breeze-release-management-generate-constraints-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;release-management&#160;generate-constraints</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -150,26 +156,28 @@
 </text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="166.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-6)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="166.4" 
textLength="317.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-6)">--airflow-constraints-mode</text><text
 class="breeze-release-management-generate-constraints-r1" x="414.8" y="166.4" 
textLength="671" [...]
 </text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="190.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-7)">│</text><text
 class="breeze-release-management-generate-constraints-r5" x="414.8" y="190.8" 
textLength="353.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-7)">constraints-source-providers]</text><text
 class="breeze-release-management-generate-constraints-r6" x="780.8" y="190.8" 
textLength=" [...]
 </text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="215.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-8)">│</text><text
 class="breeze-release-management-generate-constraints-r6" x="414.8" y="215.2" 
textLength="305" 
clip-path="url(#breeze-release-management-generate-constraints-line-8)">constraints-no-providers)</text><text
 class="breeze-release-management-generate-constraints-r5" x="1451.8" y="215.2" 
textLength="12.2" [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="239.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-9)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="239.6" 
textLength="317.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-9)">--github-repository&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r7" x [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="264" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-10)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="264" 
textLength="317.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-10)">--python&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class= [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="288.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-11)">│</text><text
 class="breeze-release-management-generate-constraints-r6" x="414.8" y="288.4" 
textLength="341.6" 
clip-path="url(#breeze-release-management-generate-constraints-line-11)">|&#160;3.11&#160;|&#160;3.12&#160;|&#160;3.13&#160;|&#160;3.14)</text><text
 class="breeze-release-management-generate-constraints- [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-12)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="312.8" 
textLength="97.6" 
clip-path="url(#breeze-release-management-generate-constraints-line-12)">--use-uv</text><text
 class="breeze-release-management-generate-constraints-r1" x="122" y="312.8" 
textLength="12.2" clip-path="url(#b [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="337.2" textLength="1464" 
clip-path="url(#breeze-release-management-generate-constraints-line-13)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-generate-constraints-r1" x="1464" y="337.2" 
textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-13)">
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="361.6" textLength="24.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-14)">╭─</text><text
 class="breeze-release-management-generate-constraints-r5" x="24.4" y="361.6" 
textLength="219.6" 
clip-path="url(#breeze-release-management-generate-constraints-line-14)">&#160;Parallel&#160;running&#160;</text><text
 class="breeze-release-management-generate-constraints-r5" x="244" y="361.6" 
textLeng [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="386" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-15)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="386" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-15)">--debug-resources</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="386" 
textLength="768.6" clip-path= [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="410.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-16)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="410.4" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-16)">--parallelism&#160;&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="410.4" 
tex [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="434.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-17)">│</text><text
 class="breeze-release-management-generate-constraints-r6" x="256.2" y="434.8" 
textLength="170.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-17)">RANGE&#160;1&lt;=x&lt;=8)</text><text
 class="breeze-release-management-generate-constraints-r5" x="1451.8" y="434.8" 
textLength="1 [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="459.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-18)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="459.2" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-18)">--python-versions</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="459.2" 
textLength="963.8" clip [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="483.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-19)">│</text><text
 class="breeze-release-management-generate-constraints-r5" x="256.2" y="483.6" 
textLength="244" 
clip-path="url(#breeze-release-management-generate-constraints-line-19)">3.11&#160;3.12&#160;3.13&#160;3.14]</text><text
 class="breeze-release-management-generate-constraints-r6" x="512.4" y="483.6" 
textLe [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="508" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-20)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="508" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-20)">--run-in-parallel</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="508" 
textLength="854" clip-path="u [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="532.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-21)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="532.4" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-21)">--skip-cleanup&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="532.4" 
textLeng [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="556.8" textLength="1464" 
clip-path="url(#breeze-release-management-generate-constraints-line-22)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-generate-constraints-r1" x="1464" y="556.8" 
textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-22)">
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="581.2" textLength="24.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-23)">╭─</text><text
 class="breeze-release-management-generate-constraints-r5" x="24.4" y="581.2" 
textLength="195.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-23)">&#160;Common&#160;options&#160;</text><text
 class="breeze-release-management-generate-constraints-r5" x="219.6" y="581.2" 
textLeng [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="605.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-24)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="605.6" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-24)">--verbose</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="605.6" 
textLength="24.4" clip-path="ur [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="630" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-25)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="630" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-25)">--dry-run</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="630" 
textLength="24.4" clip-path="url(#bre [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="654.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-26)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="654.4" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-26)">--answer&#160;</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="654.4" 
textLength="24.4" clip-pat [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="678.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-27)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="678.8" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-27)">--help&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="678.8" 
textLength="24.4 [...]
-</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="703.2" textLength="1464" 
clip-path="url(#breeze-release-management-generate-constraints-line-28)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-generate-constraints-r1" x="1464" y="703.2" 
textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-28)">
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="239.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-9)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="239.6" 
textLength="317.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-9)">--allow-pre-releases&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r1" x="414 [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="264" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-10)">│</text><text
 class="breeze-release-management-generate-constraints-r1" x="414.8" y="264" 
textLength="512.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-10)">Set&#160;to&#160;true&#160;automatically&#160;for&#160;pre-release&#160;</text><text
 class="breeze-release-management-generate-constra [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="288.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-11)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="288.4" 
textLength="317.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-11)">--github-repository&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r7" [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-12)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="312.8" 
textLength="317.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-12)">--python&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 cl [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-13)">│</text><text
 class="breeze-release-management-generate-constraints-r6" x="414.8" y="337.2" 
textLength="341.6" 
clip-path="url(#breeze-release-management-generate-constraints-line-13)">|&#160;3.11&#160;|&#160;3.12&#160;|&#160;3.13&#160;|&#160;3.14)</text><text
 class="breeze-release-management-generate-constraints- [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="361.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-14)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="361.6" 
textLength="97.6" 
clip-path="url(#breeze-release-management-generate-constraints-line-14)">--use-uv</text><text
 class="breeze-release-management-generate-constraints-r1" x="122" y="361.6" 
textLength="12.2" clip-path="url(#b [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="386" textLength="1464" 
clip-path="url(#breeze-release-management-generate-constraints-line-15)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-generate-constraints-r1" x="1464" y="386" 
textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-15)">
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="410.4" textLength="24.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-16)">╭─</text><text
 class="breeze-release-management-generate-constraints-r5" x="24.4" y="410.4" 
textLength="219.6" 
clip-path="url(#breeze-release-management-generate-constraints-line-16)">&#160;Parallel&#160;running&#160;</text><text
 class="breeze-release-management-generate-constraints-r5" x="244" y="410.4" 
textLeng [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="434.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-17)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="434.8" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-17)">--debug-resources</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="434.8" 
textLength="768.6" clip [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="459.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-18)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="459.2" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-18)">--parallelism&#160;&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="459.2" 
tex [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="483.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-19)">│</text><text
 class="breeze-release-management-generate-constraints-r6" x="256.2" y="483.6" 
textLength="170.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-19)">RANGE&#160;1&lt;=x&lt;=8)</text><text
 class="breeze-release-management-generate-constraints-r5" x="1451.8" y="483.6" 
textLength="1 [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="508" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-20)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="508" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-20)">--python-versions</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="508" 
textLength="963.8" clip-path= [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="532.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-21)">│</text><text
 class="breeze-release-management-generate-constraints-r5" x="256.2" y="532.4" 
textLength="244" 
clip-path="url(#breeze-release-management-generate-constraints-line-21)">3.11&#160;3.12&#160;3.13&#160;3.14]</text><text
 class="breeze-release-management-generate-constraints-r6" x="512.4" y="532.4" 
textLe [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="556.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-22)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="556.8" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-22)">--run-in-parallel</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="556.8" 
textLength="854" clip-p [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="581.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-23)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="581.2" 
textLength="207.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-23)">--skip-cleanup&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r1" x="256.2" y="581.2" 
textLeng [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="605.6" textLength="1464" 
clip-path="url(#breeze-release-management-generate-constraints-line-24)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-generate-constraints-r1" x="1464" y="605.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-24)">
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="630" textLength="24.4" 
clip-path="url(#breeze-release-management-generate-constraints-line-25)">╭─</text><text
 class="breeze-release-management-generate-constraints-r5" x="24.4" y="630" 
textLength="195.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-25)">&#160;Common&#160;options&#160;</text><text
 class="breeze-release-management-generate-constraints-r5" x="219.6" y="630" 
textLength="12 [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="654.4" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-26)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="654.4" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-26)">--verbose</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="654.4" 
textLength="24.4" clip-path="ur [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="678.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-27)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="678.8" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-27)">--dry-run</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="678.8" 
textLength="24.4" clip-path="ur [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="703.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-28)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="703.2" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-28)">--answer&#160;</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="703.2" 
textLength="24.4" clip-pat [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="727.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-29)">│</text><text
 class="breeze-release-management-generate-constraints-r4" x="24.4" y="727.6" 
textLength="109.8" 
clip-path="url(#breeze-release-management-generate-constraints-line-29)">--help&#160;&#160;&#160;</text><text
 class="breeze-release-management-generate-constraints-r7" x="158.6" y="727.6" 
textLength="24.4 [...]
+</text><text class="breeze-release-management-generate-constraints-r5" x="0" 
y="752" textLength="1464" 
clip-path="url(#breeze-release-management-generate-constraints-line-30)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-generate-constraints-r1" x="1464" y="752" 
textLength="12.2" 
clip-path="url(#breeze-release-management-generate-constraints-line-30)">
 </text>
     </g>
     </g>
diff --git 
a/dev/breeze/doc/images/output_release-management_generate-constraints.txt 
b/dev/breeze/doc/images/output_release-management_generate-constraints.txt
index 6b4ef06da4a..6328985d0ee 100644
--- a/dev/breeze/doc/images/output_release-management_generate-constraints.txt
+++ b/dev/breeze/doc/images/output_release-management_generate-constraints.txt
@@ -1 +1 @@
-8f5434ddc6564b1a2b026a63424eecea
+ab2a920a9c7ec445e7edd70051af73e1
diff --git a/dev/breeze/doc/images/output_setup_check-all-params-in-groups.svg 
b/dev/breeze/doc/images/output_setup_check-all-params-in-groups.svg
index 202eba652be..938ff18d355 100644
--- a/dev/breeze/doc/images/output_setup_check-all-params-in-groups.svg
+++ b/dev/breeze/doc/images/output_setup_check-all-params-in-groups.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 1245.6" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 1270.0" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -43,7 +43,7 @@
 
     <defs>
     <clipPath id="breeze-setup-check-all-params-in-groups-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="1194.6" />
+      <rect x="0" y="0" width="1463.0" height="1219.0" />
     </clipPath>
     <clipPath id="breeze-setup-check-all-params-in-groups-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -189,9 +189,12 @@
 <clipPath id="breeze-setup-check-all-params-in-groups-line-47">
     <rect x="0" y="1148.3" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-setup-check-all-params-in-groups-line-48">
+    <rect x="0" y="1172.7" width="1464" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="1243.6" rx="8"/><text 
class="breeze-setup-check-all-params-in-groups-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;setup&#160;check-all-params-in-groups</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="1268" rx="8"/><text 
class="breeze-setup-check-all-params-in-groups-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;setup&#160;check-all-params-in-groups</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -243,13 +246,14 @@
 </text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="971.6" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-39)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r6" x="158.6" y="971.6" 
textLength="890.6" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-39)">testing:core-tests&#160;|&#160;testing:docker-compose-tests&#160;|&#160;testing:helm-tests&#160;|&#160;</text><text
 class="breeze-setup-check-all-params-in-gr [...]
 </text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" y="996" 
textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-40)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r6" x="158.6" y="996" 
textLength="1195.6" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-40)">testing:providers-integration-tests&#160;|&#160;testing:providers-tests&#160;|&#160;testing:python-api-client-tests&#160;|&#160;</text><text
 class="breeze-setup- [...]
 </text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1020.4" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-41)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r6" x="158.6" y="1020.4" 
textLength="1281" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-41)">testing:system-tests&#160;|&#160;testing:task-sdk-integration-tests&#160;|&#160;testing:task-sdk-tests&#160;|&#160;testing:ui-e2e-tests</text><text
 class="br [...]
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1044.8" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-42)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r6" x="158.6" y="1044.8" 
textLength="1268.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-42)">|&#160;ui&#160;|&#160;ui:check-translation-completeness&#160;|&#160;ui:compile-assets&#160;|&#160;workflow-run&#160;|&#160;workflow-run:publish-docs)</text
 [...]
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1069.2" textLength="1464" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-43)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-setup-check-all-params-in-groups-r1" x="1464" y="1069.2" 
textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-43)">
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1093.6" textLength="24.4" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-44)">╭─</text><text
 class="breeze-setup-check-all-params-in-groups-r5" x="24.4" y="1093.6" 
textLength="195.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-44)">&#160;Common&#160;options&#160;</text><text
 class="breeze-setup-check-all-params-in-groups-r5" x="219.6" y="1093.6" 
textLength="1220" clip-path="url(#breeze [...]
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" y="1118" 
textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-45)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r4" x="24.4" y="1118" 
textLength="109.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-45)">--verbose</text><text
 class="breeze-setup-check-all-params-in-groups-r7" x="158.6" y="1118" 
textLength="24.4" clip-path="url(#breeze-setup-check-all-params-in-gr [...]
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1142.4" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-46)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r4" x="24.4" y="1142.4" 
textLength="109.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-46)">--dry-run</text><text
 class="breeze-setup-check-all-params-in-groups-r7" x="158.6" y="1142.4" 
textLength="24.4" clip-path="url(#breeze-setup-check-all-params [...]
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1166.8" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-47)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r4" x="24.4" y="1166.8" 
textLength="109.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-47)">--help&#160;&#160;&#160;</text><text
 class="breeze-setup-check-all-params-in-groups-r7" x="158.6" y="1166.8" 
textLength="24.4" clip-path="url(#breeze-setup-c [...]
-</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1191.2" textLength="1464" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-48)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-setup-check-all-params-in-groups-r1" x="1464" y="1191.2" 
textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-48)">
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1044.8" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-42)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r6" x="158.6" y="1044.8" 
textLength="1281" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-42)">|&#160;ui&#160;|&#160;ui:check-translation-completeness&#160;|&#160;ui:compile-assets&#160;|&#160;workflow-run&#160;|&#160;workflow-run:publish-docs&#160;|</
 [...]
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1069.2" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-43)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r6" x="158.6" y="1069.2" 
textLength="402.6" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-43)">workflow-run:release-constraints)</text><text
 class="breeze-setup-check-all-params-in-groups-r5" x="1451.8" y="1069.2" 
textLength="12.2" clip-path="url(#bre [...]
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1093.6" textLength="1464" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-44)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-setup-check-all-params-in-groups-r1" x="1464" y="1093.6" 
textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-44)">
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" y="1118" 
textLength="24.4" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-45)">╭─</text><text
 class="breeze-setup-check-all-params-in-groups-r5" x="24.4" y="1118" 
textLength="195.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-45)">&#160;Common&#160;options&#160;</text><text
 class="breeze-setup-check-all-params-in-groups-r5" x="219.6" y="1118" 
textLength="1220" clip-path="url(#breeze-setup [...]
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1142.4" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-46)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r4" x="24.4" y="1142.4" 
textLength="109.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-46)">--verbose</text><text
 class="breeze-setup-check-all-params-in-groups-r7" x="158.6" y="1142.4" 
textLength="24.4" clip-path="url(#breeze-setup-check-all-params [...]
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1166.8" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-47)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r4" x="24.4" y="1166.8" 
textLength="109.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-47)">--dry-run</text><text
 class="breeze-setup-check-all-params-in-groups-r7" x="158.6" y="1166.8" 
textLength="24.4" clip-path="url(#breeze-setup-check-all-params [...]
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1191.2" textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-48)">│</text><text 
class="breeze-setup-check-all-params-in-groups-r4" x="24.4" y="1191.2" 
textLength="109.8" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-48)">--help&#160;&#160;&#160;</text><text
 class="breeze-setup-check-all-params-in-groups-r7" x="158.6" y="1191.2" 
textLength="24.4" clip-path="url(#breeze-setup-c [...]
+</text><text class="breeze-setup-check-all-params-in-groups-r5" x="0" 
y="1215.6" textLength="1464" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-49)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-setup-check-all-params-in-groups-r1" x="1464" y="1215.6" 
textLength="12.2" 
clip-path="url(#breeze-setup-check-all-params-in-groups-line-49)">
 </text>
     </g>
     </g>
diff --git a/dev/breeze/doc/images/output_setup_check-all-params-in-groups.txt 
b/dev/breeze/doc/images/output_setup_check-all-params-in-groups.txt
index 92e75f17fd4..6987a59efd8 100644
--- a/dev/breeze/doc/images/output_setup_check-all-params-in-groups.txt
+++ b/dev/breeze/doc/images/output_setup_check-all-params-in-groups.txt
@@ -1 +1 @@
-fc5f65d76377357873c6b3ea200f2ec8
+b52151d5b1fb9ecfd92edf910750f0ee
diff --git a/dev/breeze/doc/images/output_setup_regenerate-command-images.svg 
b/dev/breeze/doc/images/output_setup_regenerate-command-images.svg
index de56522e818..8b96e56ba30 100644
--- a/dev/breeze/doc/images/output_setup_regenerate-command-images.svg
+++ b/dev/breeze/doc/images/output_setup_regenerate-command-images.svg
@@ -261,7 +261,7 @@
 </text><text class="breeze-setup-regenerate-command-images-r5" x="0" 
y="1044.8" textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-42)">│</text><text 
class="breeze-setup-regenerate-command-images-r6" x="195.2" y="1044.8" 
textLength="1037" 
clip-path="url(#breeze-setup-regenerate-command-images-line-42)">testing:helm-tests&#160;|&#160;testing:providers-integration-tests&#160;|&#160;testing:providers-tests&#160;|&#160;</text><text
 class="breeze-setup-regenerate-co [...]
 </text><text class="breeze-setup-regenerate-command-images-r5" x="0" 
y="1069.2" textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-43)">│</text><text 
class="breeze-setup-regenerate-command-images-r6" x="195.2" y="1069.2" 
textLength="1146.8" 
clip-path="url(#breeze-setup-regenerate-command-images-line-43)">testing:python-api-client-tests&#160;|&#160;testing:system-tests&#160;|&#160;testing:task-sdk-integration-tests&#160;|&#160;</text><text
 class="breeze-setup-re [...]
 </text><text class="breeze-setup-regenerate-command-images-r5" x="0" 
y="1093.6" textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-44)">│</text><text 
class="breeze-setup-regenerate-command-images-r6" x="195.2" y="1093.6" 
textLength="1085.8" 
clip-path="url(#breeze-setup-regenerate-command-images-line-44)">testing:task-sdk-tests&#160;|&#160;testing:ui-e2e-tests&#160;|&#160;ui&#160;|&#160;ui:check-translation-completeness&#160;|&#160;</text><text
 class="breeze-set [...]
-</text><text class="breeze-setup-regenerate-command-images-r5" x="0" y="1118" 
textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-45)">│</text><text 
class="breeze-setup-regenerate-command-images-r6" x="195.2" y="1118" 
textLength="744.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-45)">ui:compile-assets&#160;|&#160;workflow-run&#160;|&#160;workflow-run:publish-docs)</text><text
 class="breeze-setup-regenerate-command-images-r5" x="1451.8" y="1118"  [...]
+</text><text class="breeze-setup-regenerate-command-images-r5" x="0" y="1118" 
textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-45)">│</text><text 
class="breeze-setup-regenerate-command-images-r6" x="195.2" y="1118" 
textLength="1171.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-45)">ui:compile-assets&#160;|&#160;workflow-run&#160;|&#160;workflow-run:publish-docs&#160;|&#160;workflow-run:release-constraints)</text><text
 class="breeze-setup-rege [...]
 </text><text class="breeze-setup-regenerate-command-images-r5" x="0" 
y="1142.4" textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-46)">│</text><text 
class="breeze-setup-regenerate-command-images-r4" x="24.4" y="1142.4" 
textLength="146.4" 
clip-path="url(#breeze-setup-regenerate-command-images-line-46)">--check-only</text><text
 class="breeze-setup-regenerate-command-images-r1" x="195.2" y="1142.4" 
textLength="1244.4" clip-path="url(#breeze-setup-regenerate-comma [...]
 </text><text class="breeze-setup-regenerate-command-images-r5" x="0" 
y="1166.8" textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-47)">│</text><text 
class="breeze-setup-regenerate-command-images-r1" x="195.2" y="1166.8" 
textLength="170.8" 
clip-path="url(#breeze-setup-regenerate-command-images-line-47)">together&#160;with&#160;</text><text
 class="breeze-setup-regenerate-command-images-r4" x="366" y="1166.8" 
textLength="109.8" clip-path="url(#breeze-setup-regene [...]
 </text><text class="breeze-setup-regenerate-command-images-r5" x="0" 
y="1191.2" textLength="1464" 
clip-path="url(#breeze-setup-regenerate-command-images-line-48)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-setup-regenerate-command-images-r1" x="1464" y="1191.2" 
textLength="12.2" 
clip-path="url(#breeze-setup-regenerate-command-images-line-48)">
diff --git a/dev/breeze/doc/images/output_setup_regenerate-command-images.txt 
b/dev/breeze/doc/images/output_setup_regenerate-command-images.txt
index fe2e502097d..ac7e52d52bf 100644
--- a/dev/breeze/doc/images/output_setup_regenerate-command-images.txt
+++ b/dev/breeze/doc/images/output_setup_regenerate-command-images.txt
@@ -1 +1 @@
-141b4bc2fbf13eddd1301de1064bf9fe
+fadc598c6c80132f76537b23c462e6ed
diff --git a/dev/breeze/doc/images/output_workflow-run.svg 
b/dev/breeze/doc/images/output_workflow-run.svg
index af211f19a8e..f8f7cb779f0 100644
--- a/dev/breeze/doc/images/output_workflow-run.svg
+++ b/dev/breeze/doc/images/output_workflow-run.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 318.4" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 342.79999999999995" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -42,7 +42,7 @@
 
     <defs>
     <clipPath id="breeze-workflow-run-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="267.4" />
+      <rect x="0" y="0" width="1463.0" height="291.79999999999995" />
     </clipPath>
     <clipPath id="breeze-workflow-run-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -74,9 +74,12 @@
 <clipPath id="breeze-workflow-run-line-9">
     <rect x="0" y="221.1" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-workflow-run-line-10">
+    <rect x="0" y="245.5" width="1464" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="316.4" rx="8"/><text 
class="breeze-workflow-run-title" fill="#c5c8c6" text-anchor="middle" x="740" 
y="27">Command:&#160;workflow-run</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="340.8" rx="8"/><text 
class="breeze-workflow-run-title" fill="#c5c8c6" text-anchor="middle" x="740" 
y="27">Command:&#160;workflow-run</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -95,8 +98,9 @@
 </text><text class="breeze-workflow-run-r5" x="0" y="166.4" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-6)">│</text><text 
class="breeze-workflow-run-r4" x="24.4" y="166.4" textLength="73.2" 
clip-path="url(#breeze-workflow-run-line-6)">--help</text><text 
class="breeze-workflow-run-r6" x="122" y="166.4" textLength="24.4" 
clip-path="url(#breeze-workflow-run-line-6)">-h</text><text 
class="breeze-workflow-run-r1" x="170.8" y="166.4" textLength="329.4" 
clip-path="url(#breeze-wor [...]
 </text><text class="breeze-workflow-run-r5" x="0" y="190.8" textLength="1464" 
clip-path="url(#breeze-workflow-run-line-7)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-workflow-run-r1" x="1464" y="190.8" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-7)">
 </text><text class="breeze-workflow-run-r5" x="0" y="215.2" textLength="24.4" 
clip-path="url(#breeze-workflow-run-line-8)">╭─</text><text 
class="breeze-workflow-run-r5" x="24.4" y="215.2" textLength="512.4" 
clip-path="url(#breeze-workflow-run-line-8)">&#160;Airflow&#160;github&#160;actions&#160;workflow&#160;commands&#160;</text><text
 class="breeze-workflow-run-r5" x="536.8" y="215.2" textLength="902.8" 
clip-path="url(#breeze-workflow-run-line-8)">────────────────────────────────────────
 [...]
-</text><text class="breeze-workflow-run-r5" x="0" y="239.6" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-9)">│</text><text 
class="breeze-workflow-run-r4" x="24.4" y="239.6" textLength="366" 
clip-path="url(#breeze-workflow-run-line-9)">publish-docs&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-workflow-run-r1" x="414.8" y="239.6" textLength="1024.8" 
clip-path="url(#breeze-workflow-run-lin [...]
-</text><text class="breeze-workflow-run-r5" x="0" y="264" textLength="1464" 
clip-path="url(#breeze-workflow-run-line-10)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-workflow-run-r1" x="1464" y="264" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-10)">
+</text><text class="breeze-workflow-run-r5" x="0" y="239.6" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-9)">│</text><text 
class="breeze-workflow-run-r4" x="24.4" y="239.6" textLength="256.2" 
clip-path="url(#breeze-workflow-run-line-9)">publish-docs&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-workflow-run-r1" x="305" y="239.6" textLength="1134.6" 
clip-path="url(#breeze-workflow-run-line-9)">Trigger&#160;publish&#160;docs&#160;to&#160;S3&#
 [...]
+</text><text class="breeze-workflow-run-r5" x="0" y="264" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-10)">│</text><text 
class="breeze-workflow-run-r4" x="24.4" y="264" textLength="256.2" 
clip-path="url(#breeze-workflow-run-line-10)">release-constraints&#160;&#160;</text><text
 class="breeze-workflow-run-r1" x="305" y="264" textLength="1134.6" 
clip-path="url(#breeze-workflow-run-line-10)">Trigger&#160;the&#160;workflow&#160;that&#160;resolves,&#160;publishes&#160;and&#160;t
 [...]
+</text><text class="breeze-workflow-run-r5" x="0" y="288.4" textLength="1464" 
clip-path="url(#breeze-workflow-run-line-11)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-workflow-run-r1" x="1464" y="288.4" textLength="12.2" 
clip-path="url(#breeze-workflow-run-line-11)">
 </text>
     </g>
     </g>
diff --git a/dev/breeze/doc/images/output_workflow-run.txt 
b/dev/breeze/doc/images/output_workflow-run.txt
index 096f1b4e226..606ed71654d 100644
--- a/dev/breeze/doc/images/output_workflow-run.txt
+++ b/dev/breeze/doc/images/output_workflow-run.txt
@@ -1 +1 @@
-348e7af5d380b4c7dd9452e48dfc4abf
+cfbf0a5234b3a9689a01d164e3e91abf
diff --git a/dev/breeze/doc/images/output_workflow-run_release-constraints.svg 
b/dev/breeze/doc/images/output_workflow-run_release-constraints.svg
new file mode 100644
index 00000000000..e20e2d2696b
--- /dev/null
+++ b/dev/breeze/doc/images/output_workflow-run_release-constraints.svg
@@ -0,0 +1,138 @@
+<svg class="rich-terminal" viewBox="0 0 1482 513.5999999999999" 
xmlns="http://www.w3.org/2000/svg";>
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2";)
 format("woff2"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff";)
 format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2";)
 format("woff2"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff";)
 format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .breeze-workflow-run-release-constraints-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .breeze-workflow-run-release-constraints-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .breeze-workflow-run-release-constraints-r1 { fill: #c5c8c6 }
+.breeze-workflow-run-release-constraints-r2 { fill: #d0b344 }
+.breeze-workflow-run-release-constraints-r3 { fill: #c5c8c6;font-weight: bold }
+.breeze-workflow-run-release-constraints-r4 { fill: #68a0b3;font-weight: bold }
+.breeze-workflow-run-release-constraints-r5 { fill: #868887 }
+.breeze-workflow-run-release-constraints-r6 { fill: #cc555a }
+.breeze-workflow-run-release-constraints-r7 { fill: #8a4346 }
+.breeze-workflow-run-release-constraints-r8 { fill: #8d7b39 }
+.breeze-workflow-run-release-constraints-r9 { fill: #98a84b;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="breeze-workflow-run-release-constraints-clip-terminal">
+      <rect x="0" y="0" width="1463.0" height="462.59999999999997" />
+    </clipPath>
+    <clipPath id="breeze-workflow-run-release-constraints-line-0">
+    <rect x="0" y="1.5" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-1">
+    <rect x="0" y="25.9" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-2">
+    <rect x="0" y="50.3" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-3">
+    <rect x="0" y="74.7" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-4">
+    <rect x="0" y="99.1" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-5">
+    <rect x="0" y="123.5" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-6">
+    <rect x="0" y="147.9" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-7">
+    <rect x="0" y="172.3" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-8">
+    <rect x="0" y="196.7" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-9">
+    <rect x="0" y="221.1" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-10">
+    <rect x="0" y="245.5" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-11">
+    <rect x="0" y="269.9" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-12">
+    <rect x="0" y="294.3" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-13">
+    <rect x="0" y="318.7" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-14">
+    <rect x="0" y="343.1" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-15">
+    <rect x="0" y="367.5" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-16">
+    <rect x="0" y="391.9" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-workflow-run-release-constraints-line-17">
+    <rect x="0" y="416.3" width="1464" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="511.6" rx="8"/><text 
class="breeze-workflow-run-release-constraints-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;workflow-run&#160;release-constraints</text>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" 
clip-path="url(#breeze-workflow-run-release-constraints-clip-terminal)">
+    
+    <g class="breeze-workflow-run-release-constraints-matrix">
+    <text class="breeze-workflow-run-release-constraints-r1" x="1464" y="20" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-0)">
+</text><text class="breeze-workflow-run-release-constraints-r2" x="12.2" 
y="44.4" textLength="73.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-1)">Usage:</text><text
 class="breeze-workflow-run-release-constraints-r3" x="97.6" y="44.4" 
textLength="475.8" 
clip-path="url(#breeze-workflow-run-release-constraints-line-1)">breeze&#160;workflow-run&#160;release-constraints</text><text
 class="breeze-workflow-run-release-constraints-r1" x="585.6" y="44.4" 
textLength="12.2" clip- [...]
+</text><text class="breeze-workflow-run-release-constraints-r1" x="1464" 
y="68.8" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-2)">
+</text><text class="breeze-workflow-run-release-constraints-r1" x="12.2" 
y="93.2" textLength="1037" 
clip-path="url(#breeze-workflow-run-release-constraints-line-3)">Trigger&#160;the&#160;workflow&#160;that&#160;resolves,&#160;publishes&#160;and&#160;tags&#160;the&#160;constraints&#160;for&#160;a&#160;release.</text><text
 class="breeze-workflow-run-release-constraints-r1" x="1464" y="93.2" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-3)">
+</text><text class="breeze-workflow-run-release-constraints-r1" x="1464" 
y="117.6" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-4)">
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" y="142" 
textLength="24.4" 
clip-path="url(#breeze-workflow-run-release-constraints-line-5)">╭─</text><text 
class="breeze-workflow-run-release-constraints-r5" x="24.4" y="142" 
textLength="292.8" 
clip-path="url(#breeze-workflow-run-release-constraints-line-5)">&#160;Constraints&#160;to&#160;resolve&#160;</text><text
 class="breeze-workflow-run-release-constraints-r5" x="317.2" y="142" 
textLength="1122.4" clip-path="url(#br [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="166.4" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-6)">│</text><text 
class="breeze-workflow-run-release-constraints-r6" x="24.4" y="166.4" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-6)">*</text><text 
class="breeze-workflow-run-release-constraints-r4" x="61" y="166.4" 
textLength="207.4" 
clip-path="url(#breeze-workflow-run-release-constraints-line- [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="190.8" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-7)">│</text><text 
class="breeze-workflow-run-release-constraints-r1" x="292.8" y="190.8" 
textLength="1146.8" 
clip-path="url(#breeze-workflow-run-release-constraints-line-7)">and&#160;lands&#160;on&#160;a&#160;branch&#160;of&#160;its&#160;own;&#160;a&#160;final&#160;(3.1.3)&#160;resolves&#160;without&#160;them&#160;and&#160;commits&
 [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="215.2" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-8)">│</text><text 
class="breeze-workflow-run-release-constraints-r1" x="292.8" y="215.2" 
textLength="1146.8" 
clip-path="url(#breeze-workflow-run-release-constraints-line-8)">constraints-X-Y.&#160;The&#160;stage&#160;is&#160;derived&#160;from&#160;this,&#160;so&#160;it&#160;cannot&#160;be&#160;set&#160;inconsistently.&#160;&#160;&#1
 [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="239.6" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-9)">│</text><text 
class="breeze-workflow-run-release-constraints-r7" x="292.8" y="239.6" 
textLength="122" 
clip-path="url(#breeze-workflow-run-release-constraints-line-9)">[required]</text><text
 class="breeze-workflow-run-release-constraints-r8" x="427" y="239.6" 
textLength="73.2" clip-path="url(#breeze-workflow-run-release-constrai [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" y="264" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-10)">│</text><text 
class="breeze-workflow-run-release-constraints-r6" x="24.4" y="264" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-10)">*</text><text 
class="breeze-workflow-run-release-constraints-r4" x="61" y="264" 
textLength="207.4" 
clip-path="url(#breeze-workflow-run-release-constraints-line-10)" [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="288.4" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-11)">│</text><text 
class="breeze-workflow-run-release-constraints-r8" x="292.8" y="288.4" 
textLength="73.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-11)">(TEXT)</text><text
 class="breeze-workflow-run-release-constraints-r5" x="1451.8" y="288.4" 
textLength="12.2" clip-path="url(#breeze-workflow-run-release-constr [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-12)">│</text><text 
class="breeze-workflow-run-release-constraints-r4" x="61" y="312.8" 
textLength="207.4" 
clip-path="url(#breeze-workflow-run-release-constraints-line-12)">--workflow-branch</text><text
 class="breeze-workflow-run-release-constraints-r1" x="292.8" y="312.8" 
textLength="1146.8" clip-path="url(#breeze-workflow-run-rele [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-13)">│</text><text 
class="breeze-workflow-run-release-constraints-r1" x="292.8" y="337.2" 
textLength="1146.8" 
clip-path="url(#breeze-workflow-run-release-constraints-line-13)">want:&#160;unlike&#160;the&#160;docs&#160;build,&#160;the&#160;constraints&#160;do&#160;not&#160;have&#160;to&#160;be&#160;produced&#160;by&#160;the&#160;wor
 [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="361.6" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-14)">│</text><text 
class="breeze-workflow-run-release-constraints-r1" x="292.8" y="361.6" 
textLength="402.6" 
clip-path="url(#breeze-workflow-run-release-constraints-line-14)">stood&#160;at&#160;the&#160;ref&#160;being&#160;released.&#160;</text><text
 class="breeze-workflow-run-release-constraints-r5" x="695.4" y="361.6" 
textLength= [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" y="386" 
textLength="1464" 
clip-path="url(#breeze-workflow-run-release-constraints-line-15)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-workflow-run-release-constraints-r1" x="1464" y="386" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-15)">
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="410.4" textLength="24.4" 
clip-path="url(#breeze-workflow-run-release-constraints-line-16)">╭─</text><text
 class="breeze-workflow-run-release-constraints-r5" x="24.4" y="410.4" 
textLength="195.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-16)">&#160;Common&#160;options&#160;</text><text
 class="breeze-workflow-run-release-constraints-r5" x="219.6" y="410.4" 
textLength="1220" clip-path="url(#breeze-wo [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="434.8" textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-17)">│</text><text 
class="breeze-workflow-run-release-constraints-r4" x="24.4" y="434.8" 
textLength="73.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-17)">--help</text><text
 class="breeze-workflow-run-release-constraints-r9" x="122" y="434.8" 
textLength="24.4" clip-path="url(#breeze-workflow-run-release-constraint [...]
+</text><text class="breeze-workflow-run-release-constraints-r5" x="0" 
y="459.2" textLength="1464" 
clip-path="url(#breeze-workflow-run-release-constraints-line-18)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-workflow-run-release-constraints-r1" x="1464" y="459.2" 
textLength="12.2" 
clip-path="url(#breeze-workflow-run-release-constraints-line-18)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/dev/breeze/doc/images/output_workflow-run_release-constraints.txt 
b/dev/breeze/doc/images/output_workflow-run_release-constraints.txt
new file mode 100644
index 00000000000..871ae3667b4
--- /dev/null
+++ b/dev/breeze/doc/images/output_workflow-run_release-constraints.txt
@@ -0,0 +1 @@
+14273c8c9c667e0004c0a1379d53da58
diff --git 
a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py 
b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
index 5175e6e40ce..f6ac278bb35 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
@@ -49,6 +49,7 @@ from airflow_breeze.utils.path_utils import (
     AIRFLOW_ROOT_PATH,
     OUT_PATH,
 )
+from airflow_breeze.utils.release_constraints import publish_constraints
 from airflow_breeze.utils.reproducible import get_source_date_epoch, 
repack_deterministically
 from airflow_breeze.utils.run_utils import run_command
 from airflow_breeze.utils.shared_options import get_dry_run
@@ -442,28 +443,10 @@ def sign_the_release(repo_root):
         console_print("[success]Release signed")
 
 
-def tag_and_push_constraints(version, version_branch, remote_name):
-    if confirm_action("Do you want to tag and push constraints?"):
-        run_command(
-            ["git", "checkout", f"{remote_name}/constraints-{version_branch}"],
-            check=True,
-        )
-        run_command(
-            [
-                "git",
-                "tag",
-                "-s",
-                f"constraints-{version}",
-                "-m",
-                f"Constraints for Apache Airflow {version}",
-            ],
-            check=True,
-        )
-        run_command(
-            ["git", "push", remote_name, "tag", f"constraints-{version}"],
-            check=True,
-        )
-        console_print("[success]Constraints tagged and pushed")
+def generate_and_push_constraints(version, version_branch):
+    # Resolved from the stable branch the candidate was cut from, so the 
constraints describe the
+    # sources being voted on. The workflow reads "rcN" and allows pre-releases 
accordingly.
+    publish_constraints(version=version, ref=f"v{version_branch}-stable")
 
 
 def clone_asf_repo(version, repo_root):
@@ -845,8 +828,8 @@ def publish_release_candidate(
     test_airflow()
     # Sign the release
     sign_the_release(airflow_repo_root)
-    # Tag and push constraints
-    tag_and_push_constraints(version, version_branch, remote_name)
+    # Generate, publish and tag the constraints for this candidate
+    generate_and_push_constraints(version, version_branch)
     # Clone the asf repo
     clone_asf_repo(version, airflow_repo_root)
     # Move artifacts to SVN
diff --git a/dev/breeze/src/airflow_breeze/commands/release_command.py 
b/dev/breeze/src/airflow_breeze/commands/release_command.py
index 1b93f9c28ac..ffa97a01f63 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_command.py
@@ -29,6 +29,7 @@ from airflow_breeze.utils.confirm import confirm_action
 from airflow_breeze.utils.console import console_print
 from airflow_breeze.utils.environment_check import is_ci_environment
 from airflow_breeze.utils.path_utils import AIRFLOW_ROOT_PATH
+from airflow_breeze.utils.release_constraints import publish_constraints
 from airflow_breeze.utils.run_utils import run_command
 
 # Pattern to match Airflow release versions (e.g., "3.0.5")
@@ -343,45 +344,12 @@ def upload_to_pypi(version, task_sdk_version=None):
         )
 
 
-def get_constraints_branch_for_version(version: str) -> str:
+def regenerate_constraints(version):
+    # The candidate's constraints pinned pre-releases, so they cannot be 
promoted by retagging - a
+    # released version must never point at rc pins. The workflow reads a 
version without "rcN" and
+    # resolves again without them, against the providers now published as 
finals.
     major, minor = version.split(".")[:2]
-    return f"constraints-{major}-{minor}"
-
-
-def retag_constraints(release_candidate, version):
-    # By default the final ``constraints-<version>`` tag is created from the 
RC constraints tag.
-    # If the constraints were refreshed after the last RC (e.g. to pick up 
newly released
-    # providers - see dev/MANUALLY_GENERATING_IMAGE_CACHE_AND_CONSTRAINTS.md) 
the tip of the
-    # ``constraints-X-Y`` branch is newer than the RC tag and should be tagged 
instead.
-    constraints_branch = get_constraints_branch_for_version(version)
-    source_ref = f"constraints-{release_candidate}"
-    if confirm_action(
-        f"Base the final constraints on the latest '{constraints_branch}' 
branch tip instead of the "
-        f"'{source_ref}' tag? Choose yes if you refreshed constraints after 
{release_candidate}."
-    ):
-        run_command(["git", "fetch", "origin", constraints_branch], check=True)
-        source_ref = f"origin/{constraints_branch}"
-    if confirm_action(f"Retag constraints from {source_ref} as {version}?"):
-        run_command(
-            ["git", "checkout", source_ref],
-            check=True,
-        )
-        run_command(
-            [
-                "git",
-                "tag",
-                "-s",
-                f"constraints-{version}",
-                "-m",
-                f"Constraints for Apache Airflow {version}",
-            ],
-            check=True,
-        )
-    if confirm_action(f"Push constraints-{version} tag to GitHub?"):
-        run_command(
-            ["git", "push", "origin", "tag", f"constraints-{version}"],
-            check=True,
-        )
+    publish_constraints(version=version, ref=f"v{major}-{minor}-stable")
 
 
 def tag_and_push_latest_constraint(version):
@@ -567,8 +535,8 @@ def airflow_release(version, task_sdk_version):
     # Change Directory to airflow
     os.chdir(airflow_repo_root)
 
-    # Retag and push the constraint file
-    retag_constraints(release_candidate, version)
+    # Regenerate, publish and tag the constraints for the final version
+    regenerate_constraints(version)
     tag_and_push_latest_constraint(version)
 
     # Push tag for final version
diff --git 
a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py 
b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
index c7efa5a2fd3..52ed5a6001a 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -1548,6 +1548,7 @@ def tag_providers(
 @option_debug_resources
 @option_python_versions
 @option_airflow_constraints_mode_ci
+@option_allow_pre_releases
 @option_github_repository
 @option_use_uv
 @option_verbose
@@ -1555,6 +1556,7 @@ def tag_providers(
 @option_answer
 def generate_constraints(
     airflow_constraints_mode: str,
+    allow_pre_releases: bool,
     debug_resources: bool,
     github_repository: str,
     parallelism: int,
@@ -1599,6 +1601,7 @@ def generate_constraints(
         shell_params_list = [
             ShellParams(
                 airflow_constraints_mode=airflow_constraints_mode,
+                allow_pre_releases=allow_pre_releases,
                 github_repository=github_repository,
                 python=python,
                 use_uv=use_uv,
@@ -1617,6 +1620,7 @@ def generate_constraints(
     else:
         shell_params = ShellParams(
             airflow_constraints_mode=airflow_constraints_mode,
+            allow_pre_releases=allow_pre_releases,
             github_repository=github_repository,
             python=python,
             use_uv=use_uv,
diff --git 
a/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py 
b/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
index 2acad7f44f8..ac59513cdfb 100644
--- 
a/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
+++ 
b/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
@@ -339,6 +339,7 @@ RELEASE_MANAGEMENT_PARAMETERS: dict[str, list[dict[str, str 
| list[str]]]] = {
             "name": "Generate constraints flags",
             "options": [
                 "--airflow-constraints-mode",
+                "--allow-pre-releases",
                 "--github-repository",
                 "--python",
                 "--use-uv",
diff --git a/dev/breeze/src/airflow_breeze/commands/workflow_commands.py 
b/dev/breeze/src/airflow_breeze/commands/workflow_commands.py
index 2330edfcba6..d6c98827b42 100644
--- a/dev/breeze/src/airflow_breeze/commands/workflow_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/workflow_commands.py
@@ -34,8 +34,13 @@ WORKFLOW_NAME_MAPS = {
     "publish-docs": "publish-docs-to-s3.yml",
     "airflow-refresh-site": "build.yml",
     "sync-s3-to-github": "s3-to-github.yml",
+    "release-constraints": "release-constraints.yml",
 }
 
+# X.Y.Z or X.Y.ZrcN - the workflow derives the release stage from which of the 
two it is given,
+# so there is no separate switch that could disagree with the version.
+RELEASE_VERSION_PATTERN = re.compile(r"^\d+\.\d+\.\d+(rc\d+)?$")
+
 APACHE_AIRFLOW_REPO = "apache/airflow"
 APACHE_AIRFLOW_SITE_REPO = "apache/airflow-site"
 APACHE_AIRFLOW_SITE_ARCHIVE_REPO = "apache/airflow-site-archive"
@@ -272,3 +277,42 @@ def workflow_run_publish(
         **workflow_fields,
         monitor=False,
     )
+
+
+@workflow_run_group.command(
+    name="release-constraints",
+    help="Trigger the workflow that resolves, publishes and tags the 
constraints for a release.",
+)
[email protected](
+    "--version",
+    help="Version the constraints belong to. A candidate (3.1.3rc1) resolves 
with pre-releases "
+    "allowed and lands on a branch of its own; a final (3.1.3) resolves 
without them and commits "
+    "onto constraints-X-Y. The stage is derived from this, so it cannot be set 
inconsistently.",
+    required=True,
+)
[email protected](
+    "--ref",
+    help="Git ref the constraints are resolved from, e.g. 'v3-1-stable' or the 
release tag.",
+    required=True,
+)
[email protected](
+    "--workflow-branch",
+    help="Git ref the workflow DEFINITION runs from. Defaults to 'main', which 
is normally what "
+    "you want: unlike the docs build, the constraints do not have to be 
produced by the workflow "
+    "as it stood at the ref being released.",
+    default="main",
+    show_default=True,
+)
+def workflow_run_release_constraints(version: str, ref: str, workflow_branch: 
str):
+    if not RELEASE_VERSION_PATTERN.match(version):
+        console_print(f"[red]Error: '{version}' is not a release version - 
expected X.Y.Z or X.Y.ZrcN.[/red]")
+        sys.exit(1)
+    stage = "candidate" if "rc" in version else "final"
+    console_print(f"[blue]Triggering constraints generation for the {stage} 
{version} from {ref}[/blue]")
+    trigger_workflow_and_monitor(
+        workflow_name=WORKFLOW_NAME_MAPS["release-constraints"],
+        repo=APACHE_AIRFLOW_REPO,
+        branch=workflow_branch,
+        version=version,
+        ref=ref,
+    )
diff --git a/dev/breeze/src/airflow_breeze/commands/workflow_commands_config.py 
b/dev/breeze/src/airflow_breeze/commands/workflow_commands_config.py
index 239a5e46a6a..9c6d0c99277 100644
--- a/dev/breeze/src/airflow_breeze/commands/workflow_commands_config.py
+++ b/dev/breeze/src/airflow_breeze/commands/workflow_commands_config.py
@@ -18,7 +18,7 @@ from __future__ import annotations
 
 WORKFLOW_RUN_COMMANDS: dict[str, str | list[str]] = {
     "name": "Airflow github actions workflow commands",
-    "commands": ["publish-docs"],
+    "commands": ["publish-docs", "release-constraints"],
 }
 
 WORKFLOW_RUN_PARAMETERS: dict[str, list[dict[str, str | list[str]]]] = {
@@ -55,3 +55,13 @@ WORKFLOW_RUN_PARAMETERS: dict[str, list[dict[str, str | 
list[str]]]] = {
         },
     ],
 }
+WORKFLOW_RUN_PARAMETERS["breeze workflow-run release-constraints"] = [
+    {
+        "name": "Constraints to resolve",
+        "options": [
+            "--version",
+            "--ref",
+            "--workflow-branch",
+        ],
+    },
+]
diff --git a/dev/breeze/src/airflow_breeze/utils/release_constraints.py 
b/dev/breeze/src/airflow_breeze/utils/release_constraints.py
new file mode 100644
index 00000000000..466ab689120
--- /dev/null
+++ b/dev/breeze/src/airflow_breeze/utils/release_constraints.py
@@ -0,0 +1,61 @@
+# 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.
+"""Handing the constraints of a release over to the workflow that resolves 
them.
+
+A release used to tag whatever the ``constraints-X-Y`` branch happened to 
hold, so the constraints
+shipped with a version were a resolution made by the last CI build rather than 
one made for that
+version. They are resolved per release now, by the ``release-constraints`` 
workflow - on runners
+rather than on the release manager's machine, which would otherwise need a CI 
image for every
+supported Python before it could cut a release.
+
+The workflow derives the stage from the version it is given, so nothing here 
has to pass a
+separate switch that could disagree with it.
+"""
+
+from __future__ import annotations
+
+from airflow_breeze.utils.confirm import confirm_action
+from airflow_breeze.utils.console import console_print
+from airflow_breeze.utils.gh_workflow_utils import trigger_workflow_and_monitor
+
+RELEASE_CONSTRAINTS_WORKFLOW = "release-constraints.yml"
+APACHE_AIRFLOW_REPO = "apache/airflow"
+
+
+def publish_constraints(*, version: str, ref: str, workflow_branch: str = 
"main") -> None:
+    """Resolve, publish and tag the constraints belonging to ``version``.
+
+    ``version`` alone decides what happens: a candidate (``3.1.3rc1``) 
resolves with pre-releases
+    allowed - the providers of the wave being voted on are on PyPI only as rc 
versions - and lands
+    on a branch of its own, leaving the branch every other build reads where 
it was. A final
+    (``3.1.3``) resolves without them and commits onto ``constraints-X-Y``, 
which is what makes the
+    released constraints the baseline everything downstream reads.
+    """
+    stage = "candidate" if "rc" in version else "final"
+    if not confirm_action(
+        f"Trigger the release-constraints workflow for the {stage} {version} 
(resolved from {ref})?"
+    ):
+        return
+    console_print(f"[info]Resolving constraints for {version} from {ref} - 
this runs on CI runners.")
+    trigger_workflow_and_monitor(
+        workflow_name=RELEASE_CONSTRAINTS_WORKFLOW,
+        repo=APACHE_AIRFLOW_REPO,
+        branch=workflow_branch,
+        version=version,
+        ref=ref,
+    )
+    console_print(f"[success]Constraints for {version} published and tagged 
'constraints-{version}'")
diff --git a/dev/breeze/tests/test_release_command.py 
b/dev/breeze/tests/test_release_command.py
index 1e023e6bf5b..c1d89193e4f 100644
--- a/dev/breeze/tests/test_release_command.py
+++ b/dev/breeze/tests/test_release_command.py
@@ -540,53 +540,22 @@ def 
test_remove_old_release_no_task_sdk_version(monkeypatch, release_cmd):
     assert chdir_calls == [svn_release_repo, "/original/dir"]
 
 
[email protected](
-    ("version", "expected_branch"),
-    [
-        ("3.3.0", "constraints-3-3"),
-        ("3.0.5", "constraints-3-0"),
-        ("3.12.10", "constraints-3-12"),
-    ],
-)
-def test_get_constraints_branch_for_version(release_cmd, version, 
expected_branch):
-    assert release_cmd.get_constraints_branch_for_version(version) == 
expected_branch
-
-
-def test_retag_constraints_from_rc_tag_by_default(monkeypatch, release_cmd):
-    run_command_calls: list[list[str]] = []
-
-    def fake_confirm_action(prompt: str, **_kwargs) -> bool:
-        # Decline basing on the branch tip; accept the retag and the push.
-        return not prompt.startswith("Base the final constraints on the 
latest")
-
-    monkeypatch.setattr(release_cmd, "confirm_action", fake_confirm_action)
-    monkeypatch.setattr(release_cmd, "run_command", lambda cmd, **_kwargs: 
run_command_calls.append(cmd))
-
-    release_cmd.retag_constraints("3.3.0rc2", "3.3.0")
+def test_regenerate_constraints_resolves_from_the_stable_branch(monkeypatch, 
release_cmd):
+    """The final release resolves from the branch it is cut from, not from 
main."""
+    calls: dict[str, object] = {}
+    monkeypatch.setattr(release_cmd, "publish_constraints", lambda **kw: 
calls.update(kw))
 
-    assert ["git", "checkout", "constraints-3.3.0rc2"] in run_command_calls
-    assert ["git", "tag", "-s", "constraints-3.3.0", "-m", "Constraints for 
Apache Airflow 3.3.0"] in (
-        run_command_calls
-    )
-    assert ["git", "push", "origin", "tag", "constraints-3.3.0"] in 
run_command_calls
-    # The branch tip is not fetched when basing on the RC tag.
-    assert not any(cmd[:2] == ["git", "fetch"] for cmd in run_command_calls)
+    release_cmd.regenerate_constraints("3.3.0")
 
+    assert calls == {"version": "3.3.0", "ref": "v3-3-stable"}
 
-def test_retag_constraints_from_branch_tip_when_confirmed(monkeypatch, 
release_cmd):
-    run_command_calls: list[list[str]] = []
 
-    def fake_confirm_action(prompt: str, **_kwargs) -> bool:
-        return True
+def test_regenerate_constraints_passes_a_final_version(monkeypatch, 
release_cmd):
+    """No rc suffix reaches the workflow, which is what makes it refuse 
pre-releases."""
+    calls: dict[str, object] = {}
+    monkeypatch.setattr(release_cmd, "publish_constraints", lambda **kw: 
calls.update(kw))
 
-    monkeypatch.setattr(release_cmd, "confirm_action", fake_confirm_action)
-    monkeypatch.setattr(release_cmd, "run_command", lambda cmd, **_kwargs: 
run_command_calls.append(cmd))
-
-    release_cmd.retag_constraints("3.3.0rc2", "3.3.0")
+    release_cmd.regenerate_constraints("3.0.5")
 
-    assert ["git", "fetch", "origin", "constraints-3-3"] in run_command_calls
-    assert ["git", "checkout", "origin/constraints-3-3"] in run_command_calls
-    assert ["git", "checkout", "constraints-3.3.0rc2"] not in run_command_calls
-    assert ["git", "tag", "-s", "constraints-3.3.0", "-m", "Constraints for 
Apache Airflow 3.3.0"] in (
-        run_command_calls
-    )
+    assert "rc" not in str(calls["version"])
+    assert calls["ref"] == "v3-0-stable"
diff --git a/dev/breeze/tests/test_release_constraints.py 
b/dev/breeze/tests/test_release_constraints.py
new file mode 100644
index 00000000000..db914d97722
--- /dev/null
+++ b/dev/breeze/tests/test_release_constraints.py
@@ -0,0 +1,91 @@
+# 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.
+from __future__ import annotations
+
+import pytest
+
+from airflow_breeze.utils import release_constraints
+
+
[email protected]
+def triggered(monkeypatch):
+    """Capture what would have been dispatched, with the prompt answered 
yes."""
+    calls: list[dict] = []
+    monkeypatch.setattr(release_constraints, "confirm_action", lambda *a, 
**kw: True)
+    monkeypatch.setattr(
+        release_constraints, "trigger_workflow_and_monitor", lambda **kwargs: 
calls.append(kwargs)
+    )
+    return calls
+
+
[email protected](
+    ("version", "ref"),
+    [
+        pytest.param("3.1.3rc1", "v3-1-stable", id="candidate"),
+        pytest.param("3.1.3", "v3-1-stable", id="final"),
+    ],
+)
+def test_version_and_ref_are_passed_through(triggered, version, ref):
+    release_constraints.publish_constraints(version=version, ref=ref)
+
+    assert len(triggered) == 1
+    assert triggered[0]["version"] == version
+    assert triggered[0]["ref"] == ref
+    assert triggered[0]["workflow_name"] == "release-constraints.yml"
+    assert triggered[0]["repo"] == "apache/airflow"
+
+
+def test_workflow_definition_defaults_to_main(triggered):
+    # Unlike the docs build, constraints need not be produced by the workflow 
as it stood at the
+    # ref being released - so this defaults to main rather than to the ref.
+    release_constraints.publish_constraints(version="3.1.3", ref="v3-1-stable")
+
+    assert triggered[0]["branch"] == "main"
+
+
+def test_workflow_definition_can_be_overridden(triggered):
+    release_constraints.publish_constraints(
+        version="3.1.3", ref="v3-1-stable", workflow_branch="my-fix-branch"
+    )
+
+    assert triggered[0]["branch"] == "my-fix-branch"
+
+
+def test_nothing_is_dispatched_when_declined(monkeypatch, triggered):
+    monkeypatch.setattr(release_constraints, "confirm_action", lambda *a, 
**kw: False)
+
+    release_constraints.publish_constraints(version="3.1.3", ref="v3-1-stable")
+
+    assert triggered == []
+
+
[email protected](
+    ("version", "expected_stage"),
+    [
+        pytest.param("3.1.3rc2", "candidate", id="rc-is-a-candidate"),
+        pytest.param("3.1.3", "final", id="plain-version-is-final"),
+    ],
+)
+def test_the_prompt_names_the_stage_the_version_implies(monkeypatch, version, 
expected_stage):
+    """The stage is never passed separately, so the operator sees what the 
version implies."""
+    prompts: list[str] = []
+    monkeypatch.setattr(release_constraints, "confirm_action", lambda prompt, 
**kw: prompts.append(prompt))
+    monkeypatch.setattr(release_constraints, "trigger_workflow_and_monitor", 
lambda **kw: None)
+
+    release_constraints.publish_constraints(version=version, ref="v3-1-stable")
+
+    assert expected_stage in prompts[0]
diff --git a/scripts/in_container/run_generate_constraints.py 
b/scripts/in_container/run_generate_constraints.py
index 6310ec1a052..1089739c4be 100755
--- a/scripts/in_container/run_generate_constraints.py
+++ b/scripts/in_container/run_generate_constraints.py
@@ -130,6 +130,7 @@ PYPI_PROVIDERS_CONSTRAINTS_PREFIX = f"""
 @dataclass
 class ConfigParams:
     airflow_constraints_mode: str
+    allow_pre_releases: bool
     constraints_github_repository: str
     default_constraints_branch: str
     github_actions: bool
@@ -458,6 +459,19 @@ def get_all_active_provider_distributions(python_version: 
str | None = None) ->
     ]
 
 
+def build_provider_pre_release_requirements(python_version: str) -> list[str]:
+    """Requirements that let only the providers resolve to a pre-release.
+
+    uv considers a pre-release for a package only when some requirement for it 
mentions one, so a
+    pre-release lower bound on each provider confines the allowance to them. 
`--pre` would apply to
+    the whole resolution and could put a pre-release of any third-party 
dependency into the
+    constraints a release ships.
+    """
+    return [
+        f"{distribution}>=0.0.0rc0" for distribution in 
get_all_active_provider_distributions(python_version)
+    ]
+
+
 def generate_constraints_source_providers(config_params: ConfigParams) -> None:
     """
     Generates constraints with provider dependencies used from current 
sources. This might be different
@@ -521,6 +535,28 @@ def generate_constraints_pypi_providers(config_params: 
ConfigParams) -> None:
         "gremlinpython>=3.8.0",
     ]
 
+    # Constraints cut for a release candidate have to pin the candidates 
themselves - the providers
+    # for that wave exist on PyPI only as rcN versions, and uv will not 
resolve to a pre-release
+    # unless asked. The final release regenerates these without it, so a 
released constraints file
+    # can never carry an rc pin.
+    #
+    # Scoped to the providers rather than passing `--pre`, which applies to 
the whole resolution
+    # and would let any dependency answer with a pre-release - putting, say, a 
beta of a
+    # third-party library into the constraints a release ships. uv has no 
per-package pre-release
+    # flag, so the scoping is expressed the way it does support: `explicit` 
permits a pre-release
+    # only for a package some requirement marks as such, and the rc lower 
bound below is that mark.
+    # `explicit` rather than the default `if-necessary-or-explicit` also drops 
the fallback that
+    # would otherwise let an unmarked package resolve to a pre-release when no 
final satisfies.
+    pre_release_requirements: list[str] = []
+    pre_release_strategy: list[str] = []
+    if config_params.allow_pre_releases:
+        pre_release_requirements = 
build_provider_pre_release_requirements(config_params.python)
+        pre_release_strategy = ["--prerelease", "explicit"]
+        console.print(
+            f"[bright_blue]Allowing pre-releases for 
{len(pre_release_requirements)} provider "
+            "distributions - no other package can resolve to one."
+        )
+
     result = run_command(
         cmd=[
             "uv",
@@ -534,6 +570,8 @@ def generate_constraints_pypi_providers(config_params: 
ConfigParams) -> None:
             f"apache-airflow-task-sdk=={AIRFLOW_TASK_SDK_VERSION}",
             "./airflow-ctl",
             *additional_constraints_for_highest_resolution,
+            *pre_release_requirements,
+            *pre_release_strategy,
             "--reinstall",  # We need to pull the provider distributions from 
PyPI or dist, not the local ones
             "--resolution",
             "highest",
@@ -632,8 +670,17 @@ ALLOWED_CONSTRAINTS_MODES = ["constraints", 
"constraints-source-providers", "con
     help="Use uv instead of pip as packaging tool.",
     envvar="USE_UV",
 )
[email protected](
+    "--allow-pre-releases",
+    is_flag=True,
+    default=False,
+    help="Allow pre-release versions of Airflow and providers to be pinned. 
Used when constraints "
+    "are generated for a release candidate, whose providers are only on PyPI 
as rc versions.",
+    envvar="ALLOW_PRE_RELEASES",
+)
 def generate_constraints(
     airflow_constraints_mode: str,
+    allow_pre_releases: bool,
     constraints_github_repository: str,
     default_constraints_branch: str,
     github_actions: bool,
@@ -642,6 +689,7 @@ def generate_constraints(
 ) -> None:
     config_params = ConfigParams(
         airflow_constraints_mode=airflow_constraints_mode,
+        allow_pre_releases=allow_pre_releases,
         constraints_github_repository=constraints_github_repository,
         default_constraints_branch=default_constraints_branch,
         github_actions=github_actions,
diff --git a/scripts/tests/in_container/test_run_generate_constraints.py 
b/scripts/tests/in_container/test_run_generate_constraints.py
index b2bd467faa3..88af9e84102 100644
--- a/scripts/tests/in_container/test_run_generate_constraints.py
+++ b/scripts/tests/in_container/test_run_generate_constraints.py
@@ -109,3 +109,42 @@ class TestCheckProvidersNotDowngraded:
         latest.write_text("apache-airflow-providers-amazon==not-a-version\n")
         current.write_text("apache-airflow-providers-amazon==also-bad\n")
         m.check_providers_not_downgraded(_config(latest, current))
+
+
+class TestBuildProviderPreReleaseRequirements:
+    """Pre-releases are allowed for the providers only, never for the whole 
resolution."""
+
+    def 
test_every_requirement_names_a_provider_and_permits_a_pre_release(self, 
monkeypatch):
+        monkeypatch.setattr(
+            m,
+            "get_all_active_provider_distributions",
+            lambda python_version=None: [
+                "apache-airflow-providers-amazon",
+                "apache-airflow-providers-cncf-kubernetes",
+            ],
+        )
+
+        requirements = m.build_provider_pre_release_requirements("3.10")
+
+        assert requirements == [
+            "apache-airflow-providers-amazon>=0.0.0rc0",
+            "apache-airflow-providers-cncf-kubernetes>=0.0.0rc0",
+        ]
+        # The rc lower bound is what marks the package as explicit to uv; 
without it the
+        # requirement would not permit a pre-release at all.
+        assert all(requirement.endswith(">=0.0.0rc0") for requirement in 
requirements)
+        assert all(requirement.startswith("apache-airflow-providers-") for 
requirement in requirements)
+
+    def test_the_python_version_is_passed_through(self, monkeypatch):
+        """Providers excluded on a Python version must not be named for it."""
+        seen: list[str | None] = []
+
+        def fake_distributions(python_version=None):
+            seen.append(python_version)
+            return []
+
+        monkeypatch.setattr(m, "get_all_active_provider_distributions", 
fake_distributions)
+
+        m.build_provider_pre_release_requirements("3.14")
+
+        assert seen == ["3.14"]

Reply via email to