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

vincbeck 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 02dff07c1ff Allow a Job to be associated with several teams (#73561)
02dff07c1ff is described below

commit 02dff07c1ffcae606b08dcf271f29e1cb16bef07
Author: Vincent <[email protected]>
AuthorDate: Thu Sep 24 11:53:12 2026 -0400

    Allow a Job to be associated with several teams (#73561)
    
    A Job's team scope was a single nullable column, which cannot describe a
    Dag processor that parses bundles belonging to more than one team. Scoping
    such a processor to no team at all loses the information that its work is
    relevant to each of those teams, so team coverage in the health endpoint
    and the team column in the UI cannot be derived from it.
---
 .../logging-monitoring/check-health.rst            |   4 +-
 airflow-core/docs/migrations-ref.rst               |   5 +-
 .../src/airflow/api/common/airflow_health.py       |  10 +-
 .../airflow/api_fastapi/core_api/datamodels/job.py |   2 +-
 .../api_fastapi/core_api/datamodels/monitor.py     |   2 +-
 .../api_fastapi/core_api/openapi/_private_ui.yaml  |  10 +-
 .../core_api/openapi/v2-rest-api-generated.yaml    |  22 +-
 .../src/airflow/cli/commands/triggerer_command.py  |   2 +-
 airflow-core/src/airflow/jobs/job.py               |  23 +-
 .../src/airflow/jobs/triggerer_job_runner.py       |   5 +-
 .../0137_3_4_0_add_job_team_association_table.py   |  81 +++
 airflow-core/src/airflow/models/team.py            |  24 +-
 .../airflow/ui/openapi-gen/requests/schemas.gen.ts |  34 +-
 .../airflow/ui/openapi-gen/requests/types.gen.ts   |   4 +-
 .../ui/src/pages/Dashboard/Health/Health.test.tsx  |   9 +-
 .../ui/src/pages/Dashboard/Health/Health.tsx       |   2 +-
 .../src/pages/Dashboard/Health/HealthInstances.tsx |  14 +-
 airflow-core/src/airflow/utils/db.py               |   2 +-
 .../tests/unit/api/common/test_airflow_health.py   |  28 +-
 .../api_fastapi/core_api/routes/public/test_job.py |  26 +-
 .../core_api/routes/public/test_monitor.py         |   6 +-
 .../core_api/routes/public/test_task_instances.py  |   2 +-
 .../unit/cli/commands/test_triggerer_command.py    |  14 +-
 airflow-core/tests/unit/jobs/test_base_job.py      |  67 +++
 airflow-core/tests/unit/jobs/test_triggerer_job.py |   2 +-
 airflow-core/tests/unit/utils/test_db_cleanup.py   |   1 +
 .../src/airflowctl/api/datamodels/generated.py     |   4 +-
 devel-common/src/tests_common/test_utils/db.py     |   7 +
 uv.lock                                            | 632 ++++++++++-----------
 29 files changed, 629 insertions(+), 415 deletions(-)

diff --git 
a/airflow-core/docs/administration-and-deployment/logging-monitoring/check-health.rst
 
b/airflow-core/docs/administration-and-deployment/logging-monitoring/check-health.rst
index 878f9496867..c259a68d3b3 100644
--- 
a/airflow-core/docs/administration-and-deployment/logging-monitoring/check-health.rst
+++ 
b/airflow-core/docs/administration-and-deployment/logging-monitoring/check-health.rst
@@ -64,7 +64,7 @@ including per-instance details when multiple schedulers, 
triggerers, or Dag proc
         {
           "hostname": "triggerer-1.example.com",
           "latest_triggerer_heartbeat": "2018-12-26T17:16:12+00:00",
-          "team_name": "team-a"
+          "team_names": ["team-a"]
         }
       ]
     },
@@ -130,7 +130,7 @@ including per-instance details when multiple schedulers, 
triggerers, or Dag proc
 
     * ``hostname``: host where the component is running
     * the corresponding ``latest_*_heartbeat`` for that instance
-    * ``team_name`` (triggerer only): team the triggerer is scoped to, or 
``null`` when unscoped
+    * ``team_names`` (triggerer only): teams the triggerer is scoped to, empty 
when unscoped
     * ``bundle_names`` (Dag processor only): Dag bundles that processor is 
configured to parse, or ``null`` when unset
 
   * For HA deployments, prefer ``detailed_status`` and ``instances`` when you 
need to see every scheduler,
diff --git a/airflow-core/docs/migrations-ref.rst 
b/airflow-core/docs/migrations-ref.rst
index 009cb049e48..942f3e6ad86 100644
--- a/airflow-core/docs/migrations-ref.rst
+++ b/airflow-core/docs/migrations-ref.rst
@@ -39,7 +39,10 @@ Here's the list of all the Database Migrations that are 
executed via when you ru
 
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
 | Revision ID             | Revises ID       | Airflow Version   | Description 
                                                 |
 
+=========================+==================+===================+==============================================================+
-| ``3b7a91c5df20`` (head) | ``5182d0596ee2`` | ``3.4.0``         | Fold 
task_map into xcom.mapped_length.                       |
+| ``c9f4b3e7a218`` (head) | ``3b7a91c5df20`` | ``3.4.0``         | Replace the 
``job.team_name`` column with a ``job_team``     |
+|                         |                  |                   | association 
table.                                           |
++-------------------------+------------------+-------------------+--------------------------------------------------------------+
+| ``3b7a91c5df20``        | ``5182d0596ee2`` | ``3.4.0``         | Fold 
task_map into xcom.mapped_length.                       |
 
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
 | ``5182d0596ee2``        | ``b6a9c2e7d410`` | ``3.4.0``         | Widen 
revoked_token.jti to store external-issuer token       |
 |                         |                  |                   | 
identifiers.                                                 |
diff --git a/airflow-core/src/airflow/api/common/airflow_health.py 
b/airflow-core/src/airflow/api/common/airflow_health.py
index def85190e44..b073ede5f8a 100644
--- a/airflow-core/src/airflow/api/common/airflow_health.py
+++ b/airflow-core/src/airflow/api/common/airflow_health.py
@@ -95,7 +95,7 @@ def _legacy_status(jobs: list[Job]) -> HealthStatus:
 def _triggerer_instance_health(job: Job) -> dict[str, Any]:
     return {
         **_job_instance_health(job, "latest_triggerer_heartbeat"),
-        "team_name": job.team_name,
+        "team_names": job.team_names,
     }
 
 
@@ -168,7 +168,13 @@ def _triggerer_detailed_status(jobs: list[Job]) -> 
DetailedHealthStatus:
     if not expected:
         return _liveness_status(jobs)
 
-    return _coverage_status(expected, {job.team_name for job in jobs if 
job.is_alive()})
+    covered: set[str | None] = set()
+    for job in jobs:
+        if job.is_alive():
+            # An unscoped triggerer has no ``team_names``, it covers the 
team-less
+            # scope, represented by ``None`` in both ``expected`` and 
``covered``.
+            covered |= set(job.team_names) or {None}
+    return _coverage_status(expected, covered)
 
 
 def get_airflow_health() -> dict[str, Any]:
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/job.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/job.py
index 8e316d7b2e3..9164ad4e03a 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/job.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/job.py
@@ -37,7 +37,7 @@ class JobResponse(BaseModel):
     executor_class: str | None
     hostname: str | None
     unixname: str | None
-    team_name: str | None = None
+    team_names: list[str] = Field(default_factory=list)
     bundle_names: list[str] | None = None
     dag_display_name: str | None = Field(
         validation_alias=AliasPath("dag_model", "dag_display_name"), 
default=None
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/monitor.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/monitor.py
index 442a5e9f44a..4af62ae387b 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/monitor.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/monitor.py
@@ -40,7 +40,7 @@ class TriggererInstanceInfoResponse(BaseModel):
 
     hostname: str | None
     latest_triggerer_heartbeat: str | None
-    team_name: str | None
+    team_names: list[str]
 
 
 class DagProcessorInstanceInfoResponse(BaseModel):
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
index 7af3e1262f4..24938a8f765 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
@@ -4107,11 +4107,11 @@ components:
           - type: string
           - type: 'null'
           title: Unixname
-        team_name:
-          anyOf:
-          - type: string
-          - type: 'null'
-          title: Team Name
+        team_names:
+          items:
+            type: string
+          type: array
+          title: Team Names
         bundle_names:
           anyOf:
           - items:
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index 732ca53e138..49694cd5d03 100644
--- 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++ 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -15389,11 +15389,11 @@ components:
           - type: string
           - type: 'null'
           title: Unixname
-        team_name:
-          anyOf:
-          - type: string
-          - type: 'null'
-          title: Team Name
+        team_names:
+          items:
+            type: string
+          type: array
+          title: Team Names
         bundle_names:
           anyOf:
           - items:
@@ -17211,16 +17211,16 @@ components:
           - type: string
           - type: 'null'
           title: Latest Triggerer Heartbeat
-        team_name:
-          anyOf:
-          - type: string
-          - type: 'null'
-          title: Team Name
+        team_names:
+          items:
+            type: string
+          type: array
+          title: Team Names
       type: object
       required:
       - hostname
       - latest_triggerer_heartbeat
-      - team_name
+      - team_names
       title: TriggererInstanceInfoResponse
       description: Triggerer instance info serializer for responses.
     UpdateHITLDetailPayload:
diff --git a/airflow-core/src/airflow/cli/commands/triggerer_command.py 
b/airflow-core/src/airflow/cli/commands/triggerer_command.py
index 87200ff57be..3f881e35c51 100644
--- a/airflow-core/src/airflow/cli/commands/triggerer_command.py
+++ b/airflow-core/src/airflow/cli/commands/triggerer_command.py
@@ -62,7 +62,7 @@ def triggerer_run(
     set_component_mp_start_method("triggerer")
     with _serve_logs(skip_serve_logs):
         triggerer_job_runner = TriggererJobRunner(
-            job=Job(heartrate=triggerer_heartrate, team_name=team_name),
+            job=Job(heartrate=triggerer_heartrate, team_names=[team_name] if 
team_name else []),
             capacity=capacity,
             queues=queues,
         )
diff --git a/airflow-core/src/airflow/jobs/job.py 
b/airflow-core/src/airflow/jobs/job.py
index fa59a440abc..d19dff203f4 100644
--- a/airflow-core/src/airflow/jobs/job.py
+++ b/airflow-core/src/airflow/jobs/job.py
@@ -17,14 +17,14 @@
 # under the License.
 from __future__ import annotations
 
-from collections.abc import Callable
+from collections.abc import Callable, Iterable
 from datetime import datetime
 from enum import Enum
 from functools import cached_property, lru_cache
 from time import sleep
 from typing import TYPE_CHECKING, NoReturn
 
-from sqlalchemy import ForeignKey, Index, Integer, String, case, select
+from sqlalchemy import Index, Integer, String, case, select
 from sqlalchemy.exc import OperationalError
 from sqlalchemy.orm import Mapped, backref, foreign, mapped_column, 
relationship
 from sqlalchemy.orm.session import make_transient
@@ -35,6 +35,7 @@ from airflow.configuration import conf
 from airflow.exceptions import AirflowException
 from airflow.listeners.listener import get_listener_manager
 from airflow.models.base import ID_LEN, Base
+from airflow.models.team import JobTeam
 from airflow.utils.helpers import convert_camel_to_snake
 from airflow.utils.log.logging_mixin import LoggingMixin
 from airflow.utils.net import get_hostname
@@ -101,9 +102,6 @@ class Job(Base, LoggingMixin):
     executor_class: Mapped[str | None] = mapped_column(String(500))
     hostname: Mapped[str | None] = mapped_column(String(500))
     unixname: Mapped[str | None] = mapped_column(String(1000))
-    team_name: Mapped[str | None] = mapped_column(
-        String(50), ForeignKey("team.name", ondelete="SET NULL"), nullable=True
-    )
     bundle_names: Mapped[list[str] | None] = mapped_column(ExtendedJSON, 
nullable=True)
 
     __table_args__ = (
@@ -137,6 +135,21 @@ class Job(Base, LoggingMixin):
     Only makes sense for SchedulerJob.
     """
 
+    job_teams = relationship(
+        "JobTeam",
+        cascade="all, delete-orphan",
+        lazy="selectin",
+    )
+
+    @property
+    def team_names(self) -> list[str]:
+        """Names of the teams whose workloads this job serves, empty when it 
is not team-scoped."""
+        return sorted(job_team.team_name for job_team in self.job_teams)
+
+    @team_names.setter
+    def team_names(self, names: Iterable[str]) -> None:
+        self.job_teams = [JobTeam(team_name=name) for name in 
dict.fromkeys(names)]
+
     def __init__(self, heartrate=None, **kwargs):
         # Save init parameters as DB fields
         self.heartbeat_failed = False
diff --git a/airflow-core/src/airflow/jobs/triggerer_job_runner.py 
b/airflow-core/src/airflow/jobs/triggerer_job_runner.py
index 243b8f154d7..ede83fe6c39 100644
--- a/airflow-core/src/airflow/jobs/triggerer_job_runner.py
+++ b/airflow-core/src/airflow/jobs/triggerer_job_runner.py
@@ -276,7 +276,10 @@ class TriggererJobRunner(BaseJobRunner, LoggingMixin):
                 capacity=self.capacity,
                 logger=log,
                 queues=self.queues,
-                team_name=self.job.team_name,
+                # A triggerer is started with at most one ``--team-name`` for 
now, so while a Job can
+                # serve several teams this one never does, and the runner 
stays scoped to a single team.
+                # If we later let a triggerer take several ``--team-name`` 
values, this must fan out.
+                team_name=next(iter(self.job.team_names), None),
             )
             # Run the main DB comms loop in this process
             self.trigger_runner.run()
diff --git 
a/airflow-core/src/airflow/migrations/versions/0137_3_4_0_add_job_team_association_table.py
 
b/airflow-core/src/airflow/migrations/versions/0137_3_4_0_add_job_team_association_table.py
new file mode 100644
index 00000000000..49af0e10b25
--- /dev/null
+++ 
b/airflow-core/src/airflow/migrations/versions/0137_3_4_0_add_job_team_association_table.py
@@ -0,0 +1,81 @@
+#
+# 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.
+
+"""
+Replace the ``job.team_name`` column with a ``job_team`` association table.
+
+A single job can serve more than one team -- a Dag processor parsing bundles 
that belong to
+several teams is relevant to each of them -- so the association is 
many-to-many.
+
+Revision ID: c9f4b3e7a218
+Revises: 3b7a91c5df20
+Create Date: 2026-09-22 10:00:00.000000
+
+"""
+
+from __future__ import annotations
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "c9f4b3e7a218"
+down_revision = "3b7a91c5df20"
+branch_labels = None
+depends_on = None
+airflow_version = "3.4.0"
+
+
+def upgrade():
+    """Replace ``job.team_name`` with the ``job_team`` association table."""
+    # The column goes before the table is created: on SQLite, dropping a 
column rebuilds the
+    # whole ``job`` table, which a ``job_team`` foreign key pointing at it 
would not survive.
+    with op.batch_alter_table("job", schema=None) as batch_op:
+        batch_op.drop_constraint(batch_op.f("job_team_name_fkey"), 
type_="foreignkey")
+        batch_op.drop_column("team_name")
+
+    op.create_table(
+        "job_team",
+        sa.Column("job_id", sa.Integer(), nullable=False),
+        sa.Column("team_name", sa.String(length=50), nullable=False),
+        sa.PrimaryKeyConstraint("job_id", "team_name", 
name=op.f("job_team_pkey")),
+        sa.ForeignKeyConstraint(
+            columns=("job_id",),
+            refcolumns=["job.id"],
+            name="job_team_job_id_fkey",
+            ondelete="CASCADE",
+        ),
+        sa.ForeignKeyConstraint(
+            columns=("team_name",),
+            refcolumns=["team.name"],
+            name="job_team_team_name_fkey",
+            ondelete="CASCADE",
+        ),
+        sa.Index("idx_job_team_team_name", "team_name", unique=False),
+    )
+
+
+def downgrade():
+    """Restore the ``job.team_name`` column."""
+    op.drop_table("job_team")
+
+    with op.batch_alter_table("job", schema=None) as batch_op:
+        batch_op.add_column(sa.Column("team_name", sa.String(length=50), 
nullable=True))
+        batch_op.create_foreign_key(
+            batch_op.f("job_team_name_fkey"), "team", ["team_name"], ["name"], 
ondelete="SET NULL"
+        )
diff --git a/airflow-core/src/airflow/models/team.py 
b/airflow-core/src/airflow/models/team.py
index fab0e272409..9045e97e0da 100644
--- a/airflow-core/src/airflow/models/team.py
+++ b/airflow-core/src/airflow/models/team.py
@@ -20,7 +20,7 @@ from __future__ import annotations
 import re
 from typing import TYPE_CHECKING
 
-from sqlalchemy import Column, ForeignKey, Index, String, Table, select
+from sqlalchemy import Column, ForeignKey, Index, Integer, String, Table, 
select
 from sqlalchemy.orm import Mapped, mapped_column, relationship
 
 from airflow.models.base import Base, StringID
@@ -64,6 +64,28 @@ dag_bundle_team_association_table = Table(
 )
 
 
+class JobTeam(Base):
+    """
+    Association between a Job and a team whose workloads that Job serves.
+
+    Mapped as a class rather than a bare association table so that a Job can 
be given its
+    teams before it is persisted: writing link rows never needs to load, and 
therefore can
+    never accidentally insert, a :class:`Team`.
+    """
+
+    __tablename__ = "job_team"
+
+    job_id: Mapped[int] = mapped_column(Integer, ForeignKey("job.id", 
ondelete="CASCADE"), primary_key=True)
+    team_name: Mapped[str] = mapped_column(
+        String(50), ForeignKey("team.name", ondelete="CASCADE"), 
primary_key=True
+    )
+
+    __table_args__ = (Index("idx_job_team_team_name", "team_name"),)
+
+    def __repr__(self):
+        return f"JobTeam(job_id={self.job_id}, team_name={self.team_name})"
+
+
 class Team(Base):
     """
     Contains the list of teams defined in the environment.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index b9a58b37fd6..71d21c125c5 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -6122,16 +6122,12 @@ export const $JobResponse = {
             ],
             title: 'Unixname'
         },
-        team_name: {
-            anyOf: [
-                {
-                    type: 'string'
-                },
-                {
-                    type: 'null'
-                }
-            ],
-            title: 'Team Name'
+        team_names: {
+            items: {
+                type: 'string'
+            },
+            type: 'array',
+            title: 'Team Names'
         },
         bundle_names: {
             anyOf: [
@@ -8946,20 +8942,16 @@ export const $TriggererInstanceInfoResponse = {
             ],
             title: 'Latest Triggerer Heartbeat'
         },
-        team_name: {
-            anyOf: [
-                {
-                    type: 'string'
-                },
-                {
-                    type: 'null'
-                }
-            ],
-            title: 'Team Name'
+        team_names: {
+            items: {
+                type: 'string'
+            },
+            type: 'array',
+            title: 'Team Names'
         }
     },
     type: 'object',
-    required: ['hostname', 'latest_triggerer_heartbeat', 'team_name'],
+    required: ['hostname', 'latest_triggerer_heartbeat', 'team_names'],
     title: 'TriggererInstanceInfoResponse',
     description: 'Triggerer instance info serializer for responses.'
 } as const;
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index 5972b95c458..776009c9afc 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -1639,7 +1639,7 @@ export type JobResponse = {
     executor_class: string | null;
     hostname: string | null;
     unixname: string | null;
-    team_name?: string | null;
+    team_names?: Array<(string)>;
     bundle_names?: Array<(string)> | null;
     dag_display_name?: string | null;
 };
@@ -2239,7 +2239,7 @@ export type TriggererInfoResponse = {
 export type TriggererInstanceInfoResponse = {
     hostname: string | null;
     latest_triggerer_heartbeat: string | null;
-    team_name: string | null;
+    team_names: Array<(string)>;
 };
 
 /**
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.test.tsx
index c4c8fda2599..961222ae932 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.test.tsx
@@ -240,7 +240,7 @@ describe("Health", () => {
     
expect(screen.getByText("health.instances.unknownHostname")).toBeInTheDocument();
   });
 
-  it("shows the owning team of each triggerer only when multi-team is 
enabled", async () => {
+  it("shows every owning team of each triggerer only when multi-team is 
enabled", async () => {
     mockConfig.multi_team = true;
     renderHealth(
       health({
@@ -250,7 +250,7 @@ describe("Health", () => {
             {
               hostname: "triggerer-1.example.com",
               latest_triggerer_heartbeat: "2026-09-11T10:00:00Z",
-              team_name: "team-a",
+              team_names: ["team-a", "team-b"],
             },
           ],
           latest_triggerer_heartbeat: "2026-09-11T10:00:00Z",
@@ -263,6 +263,7 @@ describe("Health", () => {
 
     expect(screen.getByText("health.instances.team")).toBeInTheDocument();
     expect(screen.getByRole("link", { name: "team-a" 
})).toHaveAttribute("href", "/dags?teams=team-a");
+    expect(screen.getByRole("link", { name: "team-b" 
})).toHaveAttribute("href", "/dags?teams=team-b");
   });
 
   it("hides the team column when multi-team is disabled", async () => {
@@ -274,7 +275,7 @@ describe("Health", () => {
             {
               hostname: "triggerer-1.example.com",
               latest_triggerer_heartbeat: "2026-09-11T10:00:00Z",
-              team_name: "team-a",
+              team_names: ["team-a"],
             },
           ],
           latest_triggerer_heartbeat: "2026-09-11T10:00:00Z",
@@ -299,7 +300,7 @@ describe("Health", () => {
             {
               hostname: "triggerer-1.example.com",
               latest_triggerer_heartbeat: "2026-09-11T10:00:00Z",
-              team_name: null,
+              team_names: [],
             },
           ],
           latest_triggerer_heartbeat: "2026-09-11T10:00:00Z",
diff --git a/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.tsx
index 91b68c949bf..aef443c9f7c 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/Health.tsx
@@ -44,7 +44,7 @@ const triggererInstances = (instances?: 
Array<TriggererInstanceInfoResponse> | n
   instances?.map((instance): HealthInstance => ({
     hostname: instance.hostname,
     latestHeartbeat: instance.latest_triggerer_heartbeat,
-    teamName: instance.team_name,
+    teamNames: instance.team_names,
   }));
 
 const dagProcessorInstances = (instances?: 
Array<DagProcessorInstanceInfoResponse> | null) =>
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/HealthInstances.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/HealthInstances.tsx
index 537b23b799a..afbea263a98 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/HealthInstances.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/Health/HealthInstances.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Table, Text } from "@chakra-ui/react";
+import { HStack, Table, Text } from "@chakra-ui/react";
 import { useTranslation } from "react-i18next";
 
 import { TeamName } from "src/components/TeamName";
@@ -26,7 +26,7 @@ import { useConfig } from "src/queries/useConfig";
 
 /**
  * One live component replica, normalised across the three per-component 
instance payloads so the
- * table does not have to know which ``latest_*_heartbeat`` field the endpoint 
used. ``teamName``
+ * table does not have to know which ``latest_*_heartbeat`` field the endpoint 
used. ``teamNames``
  * and ``bundleNames`` are left undefined for components that do not report 
them at all. There is no
  * per-instance status: the endpoint only lists replicas that are running, and 
how healthy the set of
  * them is together is what the component badge shows.
@@ -35,7 +35,7 @@ export type HealthInstance = {
   readonly bundleNames?: Array<string> | null;
   readonly hostname: string | null;
   readonly latestHeartbeat: string | null;
-  readonly teamName?: string | null;
+  readonly teamNames?: Array<string> | null;
 };
 
 type Props = {
@@ -47,7 +47,7 @@ export const HealthInstances = ({ instances }: Props) => {
   const isMultiTeam = Boolean(useConfig("multi_team"));
   // Both columns only apply to one component each, and even there every 
instance may report
   // nothing — an all-empty column is noise, so it is dropped rather than 
rendered blank.
-  const showTeam = isMultiTeam && instances.some((instance) => 
Boolean(instance.teamName));
+  const showTeam = isMultiTeam && instances.some((instance) => 
Boolean(instance.teamNames?.length));
   const showBundles = instances.some((instance) => 
Boolean(instance.bundleNames?.length));
 
   return (
@@ -77,7 +77,11 @@ export const HealthInstances = ({ instances }: Props) => {
             </Table.Cell>
             {showTeam ? (
               <Table.Cell>
-                <TeamName teamName={instance.teamName} />
+                <HStack gap={2} wrap="wrap">
+                  {instance.teamNames?.map((teamName) => (
+                    <TeamName key={teamName} teamName={teamName} />
+                  ))}
+                </HStack>
               </Table.Cell>
             ) : undefined}
             {showBundles ? <Table.Cell>{instance.bundleNames?.join(", 
")}</Table.Cell> : undefined}
diff --git a/airflow-core/src/airflow/utils/db.py 
b/airflow-core/src/airflow/utils/db.py
index a116050d1ba..a76937b6b1d 100644
--- a/airflow-core/src/airflow/utils/db.py
+++ b/airflow-core/src/airflow/utils/db.py
@@ -117,7 +117,7 @@ _REVISION_HEADS_MAP: dict[str, str] = {
     "3.1.8": "509b94a1042d",
     "3.2.0": "1d6611b6ab7c",
     "3.3.0": "d2f4e1b3c5a7",
-    "3.4.0": "3b7a91c5df20",
+    "3.4.0": "c9f4b3e7a218",
 }
 
 # Prefix used to identify tables holding data moved during migration.
diff --git a/airflow-core/tests/unit/api/common/test_airflow_health.py 
b/airflow-core/tests/unit/api/common/test_airflow_health.py
index cb10bcb79ac..9affe9faab6 100644
--- a/airflow-core/tests/unit/api/common/test_airflow_health.py
+++ b/airflow-core/tests/unit/api/common/test_airflow_health.py
@@ -57,14 +57,14 @@ def _mock_job(
     hostname: str,
     heartbeat: datetime,
     alive: bool,
-    team_name: str | None = None,
+    team_names: list[str] | None = None,
     bundle_names: list[str] | None = None,
 ) -> MagicMock:
     job = MagicMock(spec=Job)
     job.hostname = hostname
     job.latest_heartbeat = heartbeat
     job.is_alive = MagicMock(return_value=alive)
-    job.team_name = team_name
+    job.team_names = team_names or []
     job.bundle_names = bundle_names
     return job
 
@@ -86,7 +86,7 @@ def _create_job(
     heartbeat: datetime,
     state: JobState = JobState.RUNNING,
     end_date: datetime | None = None,
-    team_name: str | None = None,
+    team_names: list[str] | None = None,
     bundle_names: list[str] | None = None,
 ) -> Job:
     job = Job(
@@ -94,7 +94,7 @@ def _create_job(
         latest_heartbeat=heartbeat,
         hostname=hostname,
         end_date=end_date,
-        team_name=team_name,
+        team_names=team_names or [],
         bundle_names=bundle_names,
     )
     if runner_class is TriggererJobRunner:
@@ -118,13 +118,13 @@ ALIVE_TRIGGERER_JOB_MOCK = _mock_job(
     hostname="triggerer-alive",
     heartbeat=datetime(2024, 2, 1),
     alive=True,
-    team_name="team-a",
+    team_names=["team-a"],
 )
 STALE_TRIGGERER_JOB_MOCK = _mock_job(
     hostname="triggerer-stale",
     heartbeat=datetime(2024, 1, 1),
     alive=False,
-    team_name="team-b",
+    team_names=["team-b"],
 )
 
 ALIVE_DAG_PROCESSOR_JOB_MOCK = _mock_job(
@@ -230,7 +230,7 @@ def 
test_get_airflow_health_all_stale_jobs(mock_get_jobs_health):
 
 
 @patch("airflow.api.common.airflow_health.get_jobs_health")
-def 
test_get_airflow_health_mixed_triggerers_include_team_name(mock_get_jobs_health):
+def 
test_get_airflow_health_mixed_triggerers_include_team_names(mock_get_jobs_health):
     mock_get_jobs_health.side_effect = [[], [ALIVE_TRIGGERER_JOB_MOCK, 
STALE_TRIGGERER_JOB_MOCK], []]
     health_status = get_airflow_health()
 
@@ -241,7 +241,7 @@ def 
test_get_airflow_health_mixed_triggerers_include_team_name(mock_get_jobs_hea
         {
             "hostname": ALIVE_TRIGGERER_JOB_MOCK.hostname,
             "latest_triggerer_heartbeat": 
ALIVE_TRIGGERER_JOB_MOCK.latest_heartbeat.isoformat(),
-            "team_name": ALIVE_TRIGGERER_JOB_MOCK.team_name,
+            "team_names": ALIVE_TRIGGERER_JOB_MOCK.team_names,
         },
     ]
     assert (
@@ -267,7 +267,7 @@ def 
test_get_airflow_health_triggerer_and_dag_processor_healthy(mock_get_jobs_he
                 {
                     "hostname": ALIVE_TRIGGERER_JOB_MOCK.hostname,
                     "latest_triggerer_heartbeat": 
ALIVE_TRIGGERER_JOB_MOCK.latest_heartbeat.isoformat(),
-                    "team_name": ALIVE_TRIGGERER_JOB_MOCK.team_name,
+                    "team_names": ALIVE_TRIGGERER_JOB_MOCK.team_names,
                 }
             ],
         },
@@ -300,7 +300,7 @@ def _triggerer(*, alive: bool, team_name: str | None) -> 
MagicMock:
         hostname=f"triggerer-{team_name}-{'alive' if alive else 'stale'}",
         heartbeat=datetime(2024, 2, 1),
         alive=alive,
-        team_name=team_name,
+        team_names=[team_name] if team_name else [],
     )
 
 
@@ -598,7 +598,7 @@ class TestAirflowHealthFromDb:
         assert health_status["scheduler"]["latest_scheduler_heartbeat"] == 
first.isoformat()
 
     @provide_session
-    def test_get_airflow_health_mixed_triggerers_include_team_name(self, 
testing_team, *, session):
+    def test_get_airflow_health_mixed_triggerers_include_team_names(self, 
testing_team, *, session):
         alive_heartbeat = timezone.utcnow()
         stale_heartbeat = timezone.utcnow() - STALE_HEARTBEAT_AGE
         alive = _create_job(
@@ -606,14 +606,14 @@ class TestAirflowHealthFromDb:
             TriggererJobRunner,
             hostname="triggerer-alive",
             heartbeat=alive_heartbeat,
-            team_name=testing_team.name,
+            team_names=[testing_team.name],
         )
         _create_job(
             session,
             TriggererJobRunner,
             hostname="triggerer-stale",
             heartbeat=stale_heartbeat,
-            team_name=testing_team.name,
+            team_names=[testing_team.name],
         )
         session.commit()
 
@@ -625,7 +625,7 @@ class TestAirflowHealthFromDb:
             {
                 "hostname": alive.hostname,
                 "latest_triggerer_heartbeat": alive_heartbeat.isoformat(),
-                "team_name": testing_team.name,
+                "team_names": [testing_team.name],
             },
         ]
         assert health_status["triggerer"]["latest_triggerer_heartbeat"] == 
alive_heartbeat.isoformat()
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_job.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_job.py
index cb4c7254492..f969d48b305 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_job.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_job.py
@@ -19,10 +19,12 @@ from __future__ import annotations
 from typing import TYPE_CHECKING
 
 import pytest
+from sqlalchemy import delete
 from sqlalchemy.orm import Session
 
 from airflow.jobs.job import Job, JobState
 from airflow.jobs.scheduler_job_runner import SchedulerJobRunner
+from airflow.models.team import JobTeam, Team
 from airflow.utils.session import NEW_SESSION, provide_session
 from airflow.utils.state import State
 
@@ -144,7 +146,7 @@ class TestGetJobs(TestJobEndpoint):
     ):
         # setup testcase at runtime based on the `testcase` parameter
         self.setup(testcase)
-        with assert_queries_count(2):
+        with assert_queries_count(3):
             response = test_client.get("/jobs", params=query_params)
         assert response.status_code == expected_status_code
         if expected_status_code != 200:
@@ -167,7 +169,7 @@ class TestGetJobs(TestJobEndpoint):
                 "latest_heartbeat": 
from_datetime_to_zulu(matched[0].latest_heartbeat),
                 "executor_class": None,
                 "hostname": matched[0].hostname,
-                "team_name": None,
+                "team_names": [],
                 "unixname": matched[0].unixname,
             }
             assert resp_job == expected_job
@@ -189,12 +191,24 @@ class TestGetJobs(TestJobEndpoint):
         assert response_json["total_entries"] == 1
         assert response_json["jobs"][0]["dag_id"] == "target_dag"
 
-    def test_get_jobs_includes_team_name_and_bundle_names(self, test_client, 
session: Session, testing_team):
+    @pytest.fixture
+    def extra_team(self, session: Session):
+        team = Team(name="other-team")
+        session.add(team)
+        session.commit()
+        yield team
+        session.execute(delete(JobTeam).where(JobTeam.team_name == team.name))
+        session.delete(team)
+        session.commit()
+
+    def test_get_jobs_includes_team_names_and_bundle_names(
+        self, test_client, session: Session, testing_team, extra_team
+    ):
         clear_db_jobs()
         job = Job(
             state=JobState.RUNNING,
-            job_type="TriggererJob",
-            team_name=testing_team.name,
+            job_type="DagProcessorJob",
+            team_names=[extra_team.name, testing_team.name],
             bundle_names=["bundle-a", "bundle-b"],
         )
         session.add(job)
@@ -205,7 +219,7 @@ class TestGetJobs(TestJobEndpoint):
         assert response.status_code == 200
         response_json = response.json()
         assert response_json["total_entries"] == 1
-        assert response_json["jobs"][0]["team_name"] == testing_team.name
+        assert response_json["jobs"][0]["team_names"] == 
sorted([testing_team.name, extra_team.name])
         assert response_json["jobs"][0]["bundle_names"] == ["bundle-a", 
"bundle-b"]
 
     def test_should_raises_401_unauthenticated(self, 
unauthenticated_test_client):
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_monitor.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_monitor.py
index e696ec43811..e1325de1d9c 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_monitor.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_monitor.py
@@ -137,7 +137,7 @@ class TestGetHealth(TestMonitorEndpoint):
         assert body["triggerer"]["status"] == HEALTHY
 
     
@mock.patch("airflow.api_fastapi.core_api.routes.public.monitor.get_airflow_health")
-    def test_health_includes_instance_team_name_and_bundle_names(self, 
mock_get_airflow_health, test_client):
+    def test_health_includes_instance_team_names_and_bundle_names(self, 
mock_get_airflow_health, test_client):
         mock_get_airflow_health.return_value = {
             "metadatabase": {"status": HEALTHY},
             "scheduler": {
@@ -161,7 +161,7 @@ class TestGetHealth(TestMonitorEndpoint):
                         "status": HEALTHY,
                         "hostname": "triggerer-1",
                         "latest_triggerer_heartbeat": 
"2024-11-23T11:09:15.815483+00:00",
-                        "team_name": "team-a",
+                        "team_names": ["team-a"],
                     }
                 ],
             },
@@ -184,7 +184,7 @@ class TestGetHealth(TestMonitorEndpoint):
 
         assert response.status_code == 200
         body = response.json()
-        assert body["triggerer"]["instances"][0]["team_name"] == "team-a"
+        assert body["triggerer"]["instances"][0]["team_names"] == ["team-a"]
         assert body["dag_processor"]["instances"][0]["bundle_names"] == 
["bundle-a"]
         assert body["scheduler"]["instances"][0]["hostname"] == "scheduler-1"
 
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py
 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py
index 53f5bc85aa3..7d14b4a7c05 100644
--- 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py
+++ 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py
@@ -421,7 +421,7 @@ class TestGetTaskInstance(TestTaskInstanceEndpoint):
                 "end_date": None,
                 "job_type": "TriggererJob",
                 "state": "running",
-                "team_name": None,
+                "team_names": [],
                 "unixname": getuser(),
             },
             "team_name": None,
diff --git a/airflow-core/tests/unit/cli/commands/test_triggerer_command.py 
b/airflow-core/tests/unit/cli/commands/test_triggerer_command.py
index f88dc90ea5c..449ed61626c 100644
--- a/airflow-core/tests/unit/cli/commands/test_triggerer_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_triggerer_command.py
@@ -52,7 +52,7 @@ class TestTriggererCommand:
         mock_serve.return_value.__enter__.assert_called_once()
         mock_serve.return_value.__exit__.assert_called_once()
         mock_triggerer_job_runner.assert_called_once_with(job=mock.ANY, 
capacity=42, queues=None)
-        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_name is 
None
+        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_names == 
[]
 
     @conf_vars({("triggerer", "queues_enabled"): "True"})
     @mock.patch("airflow.cli.commands.triggerer_command.TriggererJobRunner")
@@ -67,7 +67,7 @@ class TestTriggererCommand:
         mock_triggerer_job_runner.assert_called_once_with(
             job=mock.ANY, capacity=4, queues=set(["my_queue", "other_queue"])
         )
-        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_name is 
None
+        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_names == 
[]
 
     @mock.patch("airflow.cli.commands.triggerer_command.TriggererJobRunner")
     @mock.patch("airflow.cli.commands.triggerer_command.run_job")
@@ -120,20 +120,20 @@ class TestTriggererCommand:
     @mock.patch("airflow.cli.commands.triggerer_command.TriggererJobRunner")
     @mock.patch("airflow.cli.commands.triggerer_command._serve_logs")
     def test_team_name_passed_through(self, mock_serve, 
mock_triggerer_job_runner, mock_get_team):
-        """--team-name should be set on the Job when valid"""
+        """--team-name should be the Job's only team when valid"""
         mock_triggerer_job_runner.return_value.job_type = "TriggererJob"
         args = self.parser.parse_args(["triggerer", "--team-name", "team_a"])
         triggerer_command.triggerer(args)
         mock_triggerer_job_runner.assert_called_once_with(job=mock.ANY, 
capacity=mock.ANY, queues=None)
-        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_name == 
"team_a"
+        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_names == 
["team_a"]
 
     @conf_vars({("core", "multi_team"): "False"})
     @mock.patch("airflow.cli.commands.triggerer_command.TriggererJobRunner")
     @mock.patch("airflow.cli.commands.triggerer_command._serve_logs")
-    def test_no_team_name_passes_none(self, mock_serve, 
mock_triggerer_job_runner):
-        """Without --team-name, Job.team_name is None"""
+    def test_no_team_name_leaves_job_unscoped(self, mock_serve, 
mock_triggerer_job_runner):
+        """Without --team-name, Job.team_names is empty"""
         mock_triggerer_job_runner.return_value.job_type = "TriggererJob"
         args = self.parser.parse_args(["triggerer"])
         triggerer_command.triggerer(args)
         mock_triggerer_job_runner.assert_called_once_with(job=mock.ANY, 
capacity=mock.ANY, queues=None)
-        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_name is 
None
+        assert mock_triggerer_job_runner.call_args.kwargs["job"].team_names == 
[]
diff --git a/airflow-core/tests/unit/jobs/test_base_job.py 
b/airflow-core/tests/unit/jobs/test_base_job.py
index 211e4128d01..eda85fa270b 100644
--- a/airflow-core/tests/unit/jobs/test_base_job.py
+++ b/airflow-core/tests/unit/jobs/test_base_job.py
@@ -23,10 +23,12 @@ import sys
 from unittest.mock import ANY, Mock, patch
 
 import pytest
+from sqlalchemy import delete, func, select
 from sqlalchemy.exc import OperationalError
 
 from airflow._shared.timezones import timezone
 from airflow.jobs.job import Job, health_check_threshold, most_recent_job, 
perform_heartbeat, run_job
+from airflow.models.team import JobTeam, Team
 from airflow.utils.session import create_session
 from airflow.utils.state import State
 
@@ -278,3 +280,68 @@ class TestJob:
 
     def test_health_check_threshold_unknown_job_uses_heartrate_fallback(self):
         assert health_check_threshold("UnknownJob", 30) == 30 * 2.1
+
+
+class TestJobTeams:
+    @pytest.fixture
+    def teams(self, session):
+        names = ["team-b", "team-a"]
+        session.add_all(Team(name=name) for name in names)
+        session.commit()
+        yield names
+        session.execute(delete(JobTeam))
+        session.execute(delete(Team).where(Team.name.in_(names)))
+        session.commit()
+
+    def test_no_teams_by_default(self, session):
+        job = Job()
+        session.add(job)
+        session.commit()
+
+        assert job.team_names == []
+
+    def test_multiple_teams_round_trip_sorted(self, session, teams):
+        job = Job(team_names=teams)
+        session.add(job)
+        session.commit()
+        session.expunge(job)
+
+        assert session.get(Job, job.id).team_names == ["team-a", "team-b"]
+
+    def test_repeated_team_name_is_stored_once(self, session, teams):
+        job = Job(team_names=["team-a", "team-a"])
+        session.add(job)
+        session.commit()
+
+        assert job.team_names == ["team-a"]
+        assert session.scalar(select(func.count()).select_from(JobTeam)) == 1
+
+    def test_reassigning_teams_replaces_the_previous_ones(self, session, 
teams):
+        job = Job(team_names=["team-a"])
+        session.add(job)
+        session.commit()
+
+        job.team_names = ["team-b"]
+        session.commit()
+
+        assert job.team_names == ["team-b"]
+        assert session.scalars(select(JobTeam.team_name)).all() == ["team-b"]
+
+    def test_teams_survive_the_heartbeat_merge(self, session, teams):
+        """``prepare_for_execution`` detaches the Job, so the heartbeat merge 
must not duplicate links."""
+        job = Job(team_names=teams)
+        job.prepare_for_execution()
+        job.heartbeat(heartbeat_callback=Mock())
+
+        assert session.scalar(select(func.count()).select_from(JobTeam)) == 2
+        assert session.get(Job, job.id).team_names == ["team-a", "team-b"]
+
+    def test_deleting_a_job_removes_its_team_links(self, session, teams):
+        job = Job(team_names=teams)
+        session.add(job)
+        session.commit()
+
+        session.delete(job)
+        session.commit()
+
+        assert session.scalar(select(func.count()).select_from(JobTeam)) == 0
diff --git a/airflow-core/tests/unit/jobs/test_triggerer_job.py 
b/airflow-core/tests/unit/jobs/test_triggerer_job.py
index 79b1755cd4f..7b4a45e9ea7 100644
--- a/airflow-core/tests/unit/jobs/test_triggerer_job.py
+++ b/airflow-core/tests/unit/jobs/test_triggerer_job.py
@@ -272,7 +272,7 @@ def 
test_execute_passes_job_team_name_to_supervisor(mock_supervisor_start, team_
     mock_supervisor._exit_code = 0
     mock_supervisor_start.return_value = mock_supervisor
 
-    job = Job(team_name=team_name)
+    job = Job(team_names=[team_name] if team_name else [])
     job_runner = TriggererJobRunner(job)
     with (
         patch.object(job_runner, "register_signals"),
diff --git a/airflow-core/tests/unit/utils/test_db_cleanup.py 
b/airflow-core/tests/unit/utils/test_db_cleanup.py
index c7104d954d4..375461b6848 100644
--- a/airflow-core/tests/unit/utils/test_db_cleanup.py
+++ b/airflow-core/tests/unit/utils/test_db_cleanup.py
@@ -1097,6 +1097,7 @@ class TestDBCleanup:
             "deadline_alert",  # cascade from serialized_dag, which cascades 
from dag_version
             "hitl_detail",  # cascade from task_instance
             "hitl_detail_history",  # cascade from task_instance_history
+            "job_team",  # cascade from job
             "task_inlet_asset_reference",  # cascade from dag
         }
 
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py 
b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index 72cecf05509..8938ad8d053 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -951,7 +951,7 @@ class JobResponse(BaseModel):
     executor_class: Annotated[str | None, Field(title="Executor Class")]
     hostname: Annotated[str | None, Field(title="Hostname")]
     unixname: Annotated[str | None, Field(title="Unixname")]
-    team_name: Annotated[str | None, Field(title="Team Name")] = None
+    team_names: Annotated[list[str] | None, Field(title="Team Names")] = None
     bundle_names: Annotated[list[str] | None, Field(title="Bundle Names")] = 
None
     dag_display_name: Annotated[str | None, Field(title="Dag Display Name")] = 
None
 
@@ -1363,7 +1363,7 @@ class TriggererInstanceInfoResponse(BaseModel):
 
     hostname: Annotated[str | None, Field(title="Hostname")]
     latest_triggerer_heartbeat: Annotated[str | None, Field(title="Latest 
Triggerer Heartbeat")]
-    team_name: Annotated[str | None, Field(title="Team Name")]
+    team_names: Annotated[list[str], Field(title="Team Names")]
 
 
 class UpdateHITLDetailPayload(BaseModel):
diff --git a/devel-common/src/tests_common/test_utils/db.py 
b/devel-common/src/tests_common/test_utils/db.py
index 15256253757..bfa3b067fc8 100644
--- a/devel-common/src/tests_common/test_utils/db.py
+++ b/devel-common/src/tests_common/test_utils/db.py
@@ -69,6 +69,7 @@ from tests_common.test_utils.version_compat import (
     AIRFLOW_V_3_1_PLUS,
     AIRFLOW_V_3_2_PLUS,
     AIRFLOW_V_3_3_PLUS,
+    AIRFLOW_V_3_4_PLUS,
 )
 
 log = logging.getLogger(__name__)
@@ -470,6 +471,12 @@ def clear_db_logs():
 @_retry_db
 def clear_db_jobs():
     with create_session() as session:
+        if AIRFLOW_V_3_4_PLUS:
+            from airflow.models.team import JobTeam
+
+            # SQLite does not enforce the ON DELETE CASCADE, and it reuses job 
ids, so leftover
+            # job_team rows would silently re-attach to the next job created.
+            session.execute(delete(JobTeam))
         session.execute(delete(Job))
 
 
diff --git a/uv.lock b/uv.lock
index 1e0292121f2..51fc8c66ab9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -29,148 +29,148 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no 
effect and is included for
 exclude-newer-span = "P4D"
 
 [options.exclude-newer-package]
-apache-airflow = false
-apache-airflow-breeze = false
-apache-airflow-core = false
-apache-airflow-ctl = false
-apache-airflow-ctl-tests = false
-apache-airflow-dev = false
-apache-airflow-devel-common = false
-apache-airflow-docker-stack = false
+apache-airflow-providers-informatica = false
+apache-airflow-providers-clickhousedb = false
+apache-airflow-providers-amazon = false
+apache-airflow-providers-elasticsearch = false
+apache-airflow-providers-duckdb = false
 apache-airflow-docker-tests = false
-apache-airflow-e2e-tests = false
-apache-airflow-helm-chart = false
-apache-airflow-kubernetes-tests = false
-apache-airflow-mypy = false
+apache-airflow-providers-microsoft-winrm = false
+typesafe-sdk = "2026-09-19T00:00:00Z"
 apache-airflow-providers = false
-apache-airflow-providers-airbyte = false
-apache-airflow-providers-akeyless = false
-apache-airflow-providers-alibaba = false
-apache-airflow-providers-amazon = false
-apache-airflow-providers-anthropic = false
-apache-airflow-providers-apache-arrow = false
-apache-airflow-providers-apache-beam = false
-apache-airflow-providers-apache-cassandra = false
+apache-airflow-providers-fab = false
+apache-airflow-providers-openlineage = false
+apache-airflow-providers-sftp = false
+apache-airflow-e2e-tests = false
+apache-airflow-shared-logging = false
+apache-airflow-providers-common-dataquality = false
 apache-airflow-providers-apache-drill = false
-apache-airflow-providers-apache-druid = false
-apache-airflow-providers-apache-flink = false
-apache-airflow-providers-apache-hdfs = false
-apache-airflow-providers-apache-hive = false
-apache-airflow-providers-apache-iceberg = false
-apache-airflow-providers-apache-impala = false
-apache-airflow-providers-apache-kafka = false
-apache-airflow-providers-apache-kylin = false
-apache-airflow-providers-apache-livy = false
-apache-airflow-providers-apache-pig = false
-apache-airflow-providers-apache-pinot = false
-apache-airflow-providers-apache-spark = false
-apache-airflow-providers-apache-tinkerpop = false
-apache-airflow-providers-apprise = false
+apache-airflow-providers-pgvector = false
+apache-airflow-providers-imap = false
+apache-airflow-providers-qdrant = false
+apache-airflow-providers-edge3 = false
+apache-airflow-providers-neo4j = false
+apache-airflow-providers-discord = false
+apache-airflow-providers-opensearch = false
+apache-airflow-providers-samba = false
 apache-airflow-providers-arangodb = false
-apache-airflow-providers-asana = false
-apache-airflow-providers-atlassian-jira = false
+apache-airflow-providers-apache-spark = false
+apache-airflow-providers-ftp = false
+apache-airflow-providers-jenkins = false
+apache-airflow-shared-listeners = false
+apache-airflow-shared-providers-discovery = false
+apache-airflow-providers-telegram = false
 apache-airflow-providers-celery = false
-apache-airflow-providers-clickhousedb = false
-apache-airflow-providers-cloudant = false
-apache-airflow-providers-cncf-kubernetes = false
-apache-airflow-providers-cohere = false
+apache-airflow-providers-docker = false
+apache-airflow-providers-sendgrid = false
 apache-airflow-providers-common-ai = false
-apache-airflow-providers-common-compat = false
-apache-airflow-providers-common-dataquality = false
-apache-airflow-providers-common-io = false
-apache-airflow-providers-common-messaging = false
-apache-airflow-providers-common-sql = false
-apache-airflow-providers-databricks = false
-apache-airflow-providers-datadog = false
+apache-airflow = false
+apache-airflow-shared-observability = false
+apache-airflow-dev = false
 apache-airflow-providers-dbt-cloud = false
-apache-airflow-providers-dingding = false
-apache-airflow-providers-discord = false
-apache-airflow-providers-docker = false
-apache-airflow-providers-duckdb = false
-apache-airflow-providers-edge3 = false
-apache-airflow-providers-elasticsearch = false
-apache-airflow-providers-exasol = false
-apache-airflow-providers-fab = false
-apache-airflow-providers-facebook = false
-apache-airflow-providers-ftp = false
-apache-airflow-providers-git = false
-apache-airflow-providers-github = false
-apache-airflow-providers-google = false
-apache-airflow-providers-grpc = false
-apache-airflow-providers-hashicorp = false
-apache-airflow-providers-http = false
-apache-airflow-providers-ibm-db2 = false
-apache-airflow-providers-ibm-mq = false
-apache-airflow-providers-imap = false
-apache-airflow-providers-influxdb = false
-apache-airflow-providers-informatica = false
-apache-airflow-providers-jdbc = false
-apache-airflow-providers-jenkins = false
-apache-airflow-providers-keycloak = false
-apache-airflow-providers-microsoft-azure = false
-apache-airflow-providers-microsoft-mssql = false
-apache-airflow-providers-microsoft-psrp = false
-apache-airflow-providers-microsoft-winrm = false
-apache-airflow-providers-mongo = false
-apache-airflow-providers-mysql = false
-apache-airflow-providers-neo4j = false
-apache-airflow-providers-odbc = false
-apache-airflow-providers-openai = false
 apache-airflow-providers-openfaas = false
-apache-airflow-providers-openlineage = false
-apache-airflow-providers-opensearch = false
-apache-airflow-providers-opsgenie = false
+apache-airflow-devel-common = false
+apache-airflow-providers-apache-cassandra = false
+apache-airflow-providers-asana = false
 apache-airflow-providers-oracle = false
-apache-airflow-providers-pagerduty = false
-apache-airflow-providers-papermill = false
-apache-airflow-providers-pgvector = false
+apache-airflow-providers-mysql = false
+apache-airflow-providers-alibaba = false
+apache-airflow-providers-microsoft-mssql = false
+apache-airflow-providers-teradata = false
+apache-airflow-providers-jdbc = false
+apache-airflow-helm-chart = false
+apache-airflow-providers-anthropic = false
+apache-airflow-providers-common-io = false
+apache-airflow-providers-cohere = false
 apache-airflow-providers-pinecone = false
-apache-airflow-providers-postgres = false
-apache-airflow-providers-presto = false
-apache-airflow-providers-qdrant = false
+apache-airflow-providers-segment = false
 apache-airflow-providers-redis = false
+apache-airflow-shared-dagnode = false
+apache-airflow-providers-apache-pinot = false
+apache-airflow-providers-weaviate = false
+google-cloud-aiplatform = "2026-08-13T00:00:00Z"
+apache-airflow-providers-akeyless = false
 apache-airflow-providers-salesforce = false
-apache-airflow-providers-samba = false
-apache-airflow-providers-segment = false
-apache-airflow-providers-sendgrid = false
-apache-airflow-providers-sftp = false
-apache-airflow-providers-singularity = false
-apache-airflow-providers-slack = false
-apache-airflow-providers-smtp = false
-apache-airflow-providers-snowflake = false
-apache-airflow-providers-sqlite = false
 apache-airflow-providers-ssh = false
-apache-airflow-providers-standard = false
-apache-airflow-providers-tableau = false
-apache-airflow-providers-telegram = false
-apache-airflow-providers-teradata = false
-apache-airflow-providers-trino = false
+apache-airflow-providers-papermill = false
+apache-airflow-providers-google = false
+pydantic-ai-skills = "2026-07-16T00:00:00Z"
+apache-airflow-providers-apache-beam = false
+apache-airflow-providers-microsoft-psrp = false
 apache-airflow-providers-vertica = false
+apache-airflow-providers-apache-hdfs = false
+apache-airflow-shared-template-rendering = false
+apache-airflow-mypy = false
+apache-airflow-providers-http = false
+apache-airflow-providers-slack = false
 apache-airflow-providers-vespa = false
-apache-airflow-providers-weaviate = false
+apache-airflow-providers-databricks = false
+apache-airflow-shared-state = false
+apache-airflow-docker-stack = false
+apache-airflow-providers-sqlite = false
+apache-airflow-providers-ibm-mq = false
+apache-airflow-shared-module-loading = false
 apache-airflow-providers-yandex = false
-apache-airflow-providers-ydb = false
-apache-airflow-providers-zendesk = false
+apache-airflow-shared-serialization = false
 apache-airflow-scripts = false
+apache-airflow-providers-exasol = false
+apache-airflow-providers-mongo = false
+apache-airflow-providers-apache-arrow = false
+apache-airflow-providers-apprise = false
+apache-airflow-providers-apache-impala = false
+apache-airflow-ctl = false
+apache-airflow-providers-github = false
+apache-airflow-providers-snowflake = false
+apache-airflow-providers-zendesk = false
+apache-airflow-providers-presto = false
+apache-airflow-providers-airbyte = false
+apache-airflow-providers-apache-hive = false
+apache-airflow-kubernetes-tests = false
+apache-airflow-providers-grpc = false
+apache-airflow-providers-apache-druid = false
+apache-airflow-providers-apache-flink = false
+apache-airflow-providers-cncf-kubernetes = false
+apache-airflow-providers-apache-pig = false
+apache-airflow-providers-apache-tinkerpop = false
+apache-airflow-shared-timezones = false
+apache-airflow-providers-apache-iceberg = false
+apache-airflow-breeze = false
+apache-airflow-providers-opsgenie = false
+apache-airflow-providers-apache-livy = false
+apache-airflow-core = false
+apache-airflow-providers-hashicorp = false
+apache-airflow-providers-pagerduty = false
+apache-airflow-providers-datadog = false
+apache-airflow-providers-apache-kafka = false
+apache-airflow-providers-influxdb = false
+apache-airflow-providers-keycloak = false
+apache-airflow-providers-trino = false
+apache-airflow-providers-common-messaging = false
+apache-airflow-providers-standard = false
+apache-airflow-providers-singularity = false
+apache-airflow-providers-common-compat = false
+apache-airflow-ctl-tests = false
+apache-airflow-providers-tableau = false
+apache-airflow-providers-ibm-db2 = false
+apache-airflow-providers-common-sql = false
 apache-airflow-shared-configuration = false
-apache-airflow-shared-dagnode = false
-apache-airflow-shared-listeners = false
-apache-airflow-shared-logging = false
-apache-airflow-shared-module-loading = false
-apache-airflow-shared-observability = false
+apache-airflow-providers-facebook = false
+apache-airflow-providers-ydb = false
+apache-airflow-providers-microsoft-azure = false
 apache-airflow-shared-plugins-manager = false
-apache-airflow-shared-providers-discovery = false
 apache-airflow-shared-secrets-backend = false
 apache-airflow-shared-secrets-masker = false
-apache-airflow-shared-serialization = false
-apache-airflow-shared-state = false
-apache-airflow-shared-template-rendering = false
-apache-airflow-shared-timezones = false
+apache-airflow-providers-git = false
 apache-airflow-task-sdk = false
+apache-airflow-providers-atlassian-jira = false
+apache-airflow-providers-odbc = false
+apache-airflow-providers-postgres = false
+apache-airflow-providers-openai = false
 apache-airflow-task-sdk-integration-tests = false
-google-cloud-aiplatform = "2026-08-13T00:00:00Z"
-pydantic-ai-skills = "2026-07-16T00:00:00Z"
-typesafe-sdk = "2026-09-19T00:00:00Z"
+apache-airflow-providers-smtp = false
+apache-airflow-providers-dingding = false
+apache-airflow-providers-cloudant = false
+apache-airflow-providers-apache-kylin = false
 
 [manifest]
 members = [
@@ -523,7 +523,7 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "caio", version = "0.9.25", source = { registry = 
"https://pypi.org/simple"; } },
+    { name = "caio", version = "0.9.25", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/67/e2/d7cb819de8df6b5c1968a2756c3cb4122d4fa2b8fc768b53b7c9e5edb646/aiofile-3.9.0.tar.gz";,
 hash = 
"sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b", size 
= 17943, upload-time = "2024-10-08T10:39:35.846Z" }
 wheels = [
@@ -555,7 +555,7 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "caio", version = "0.12.4", source = { registry = 
"https://pypi.org/simple"; } },
+    { name = "caio", version = "0.12.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/14/31/edb06aabd8f8f0b56d659f30800795f40b93cba96be946ce179f6931e3a5/aiofile-3.12.3.tar.gz";,
 hash = 
"sha256:caa6aa746b5e47e2165f7abd741b6415e49cf4d44fddc0f61844612cc3924d41", size 
= 21600, upload-time = "2026-08-04T22:59:27.171Z" }
 wheels = [
@@ -722,7 +722,7 @@ name = "aiohttp-cors"
 version = "0.8.1"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "aiohttp" },
+    { name = "aiohttp", marker = "python_full_version < '3.15'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/6f/6d/d89e846a5444b3d5eb8985a6ddb0daef3774928e1bfbce8e84ec97b0ffa7/aiohttp_cors-0.8.1.tar.gz";,
 hash = 
"sha256:ccacf9cb84b64939ea15f859a146af1f662a6b1d68175754a07315e305fb1403", size 
= 38626, upload-time = "2025-03-31T14:16:20.048Z" }
 wheels = [
@@ -11014,8 +11014,8 @@ name = "cassandra-driver"
 version = "3.30.1"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "deprecated" },
-    { name = "geomet" },
+    { name = "deprecated", marker = "python_full_version < '3.14'" },
+    { name = "geomet", marker = "python_full_version < '3.14'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/bb/ed/4e16210e194660f107929ee494f1cf18557252655067d9b39029c241be2d/cassandra_driver-3.30.1.tar.gz";,
 hash = 
"sha256:0c6a3e1428f7c6a9aa6c944b9c47a37cd2cdbeb5b5a82d42c33afdd56f14e398", size 
= 289233, upload-time = "2026-07-06T20:14:31.37Z" }
 wheels = [
@@ -11737,7 +11737,7 @@ name = "colorful"
 version = "0.5.8"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "colorama", marker = "sys_platform == 'win32'" },
+    { name = "colorama", marker = "python_full_version < '3.15' and 
sys_platform == 'win32'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/82/31/109ef4bedeb32b4202e02ddb133162457adc4eb890a9ed9c05c9dd126ed0/colorful-0.5.8.tar.gz";,
 hash = 
"sha256:bb16502b198be2f1c42ba3c52c703d5f651d826076817185f0294c1a549a7445", size 
= 209361, upload-time = "2025-10-29T11:53:21.663Z" }
 wheels = [
@@ -13302,7 +13302,7 @@ name = "geomet"
 version = "1.1.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "click" },
+    { name = "click", marker = "python_full_version < '3.14'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/2a/8c/dde022aa6747b114f6b14a7392871275dea8867e2bd26cddb80cc6d66620/geomet-1.1.0.tar.gz";,
 hash = 
"sha256:51e92231a0ef6aaa63ac20c443377ba78a303fd2ecd179dc3567de79f3c11605", size 
= 28732, upload-time = "2023-11-14T15:43:36.764Z" }
 wheels = [
@@ -13485,10 +13485,10 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "fasteners" },
-    { name = "httplib2" },
-    { name = "oauth2client" },
-    { name = "six" },
+    { name = "fasteners", marker = "python_full_version < '3.13'" },
+    { name = "httplib2", marker = "python_full_version < '3.13'" },
+    { name = "oauth2client", marker = "python_full_version < '3.13'" },
+    { name = "six", marker = "python_full_version < '3.13'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/19/da/aefc4cf4c168b5d875344cd9dddc77e3a2d11986b630251af5ce47dd2843/google-apitools-0.5.31.tar.gz";,
 hash = 
"sha256:4af0dd6dd4582810690251f0b57a97c1873dadfda54c5bc195844c8907624170", size 
= 173463, upload-time = "2020-05-14T17:25:19.126Z" }
 
@@ -13509,10 +13509,10 @@ resolution-markers = [
     "(python_full_version == '3.13.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.13.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "fasteners" },
-    { name = "httplib2" },
-    { name = "oauth2client" },
-    { name = "six" },
+    { name = "fasteners", marker = "python_full_version >= '3.13'" },
+    { name = "httplib2", marker = "python_full_version >= '3.13'" },
+    { name = "oauth2client", marker = "python_full_version >= '3.13'" },
+    { name = "six", marker = "python_full_version >= '3.13'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/f5/e4/15fbddfffa968e10c602a30b7ca4703f3e41d2dbdef011e98eee85575133/google-apitools-0.5.35.tar.gz";,
 hash = 
"sha256:911bc3698686c74187414c610ae30bd6e3c0a7404178fc6479ead6c420d2dd94", size 
= 531707, upload-time = "2025-09-24T20:22:51.11Z" }
 wheels = [
@@ -14838,7 +14838,7 @@ name = "gssapi"
 version = "1.12.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "decorator" },
+    { name = "decorator", marker = "python_full_version < '3.11' or 
sys_platform != 'win32'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/6b/fb/9492eb37b5786369b7e96fff2cf4040713acb33b96124813e058cbbd4356/gssapi-1.12.0.tar.gz";,
 hash = 
"sha256:0b48f17296f869db89221c3bde463fdff35152a4cb99ae4aaa42b0b64333fc6c", size 
= 95321, upload-time = "2026-08-26T03:00:50.067Z" }
 wheels = [
@@ -15018,8 +15018,8 @@ name = "httpcore2"
 version = "2.12.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "h11" },
-    { name = "truststore" },
+    { name = "h11", marker = "python_full_version < '3.11' or sys_platform != 
'emscripten'" },
+    { name = "truststore", marker = "python_full_version < '3.11' or 
sys_platform != 'emscripten'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz";,
 hash = 
"sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size 
= 67548, upload-time = "2026-08-18T13:22:08.211Z" }
 wheels = [
@@ -15666,17 +15666,17 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "colorama", marker = "sys_platform == 'win32'" },
-    { name = "decorator" },
-    { name = "exceptiongroup" },
-    { name = "jedi" },
-    { name = "matplotlib-inline" },
-    { name = "pexpect", marker = "sys_platform != 'emscripten' and 
sys_platform != 'win32'" },
-    { name = "prompt-toolkit" },
-    { name = "pygments" },
-    { name = "stack-data" },
-    { name = "traitlets" },
-    { name = "typing-extensions" },
+    { name = "colorama", marker = "python_full_version < '3.11' and 
sys_platform == 'win32'" },
+    { name = "decorator", marker = "python_full_version < '3.11'" },
+    { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+    { name = "jedi", marker = "python_full_version < '3.11'" },
+    { name = "matplotlib-inline", marker = "python_full_version < '3.11'" },
+    { name = "pexpect", marker = "python_full_version < '3.11' and 
sys_platform != 'emscripten' and sys_platform != 'win32'" },
+    { name = "prompt-toolkit", marker = "python_full_version < '3.11'" },
+    { name = "pygments", marker = "python_full_version < '3.11'" },
+    { name = "stack-data", marker = "python_full_version < '3.11'" },
+    { name = "traitlets", marker = "python_full_version < '3.11'" },
+    { name = "typing-extensions", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz";,
 hash = 
"sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size 
= 5513971, upload-time = "2026-03-27T10:02:13.94Z" }
 wheels = [
@@ -15708,17 +15708,17 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "colorama", marker = "sys_platform == 'win32'" },
-    { name = "ipython-pygments-lexers" },
-    { name = "jedi" },
-    { name = "matplotlib-inline" },
-    { name = "pexpect", marker = "sys_platform != 'emscripten' and 
sys_platform != 'win32'" },
-    { name = "prompt-toolkit" },
-    { name = "psutil", marker = "sys_platform != 'cygwin' and sys_platform != 
'emscripten'" },
-    { name = "pygments" },
-    { name = "stack-data" },
-    { name = "traitlets" },
-    { name = "typing-extensions", marker = "python_full_version < '3.12'" },
+    { name = "colorama", marker = "python_full_version >= '3.11' and 
sys_platform == 'win32'" },
+    { name = "ipython-pygments-lexers", marker = "python_full_version >= 
'3.11'" },
+    { name = "jedi", marker = "python_full_version >= '3.11'" },
+    { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" },
+    { name = "pexpect", marker = "python_full_version >= '3.11' and 
sys_platform != 'emscripten' and sys_platform != 'win32'" },
+    { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" },
+    { name = "psutil", marker = "python_full_version >= '3.11' and 
sys_platform != 'cygwin' and sys_platform != 'emscripten'" },
+    { name = "pygments", marker = "python_full_version >= '3.11'" },
+    { name = "stack-data", marker = "python_full_version >= '3.11'" },
+    { name = "traitlets", marker = "python_full_version >= '3.11'" },
+    { name = "typing-extensions", marker = "python_full_version == '3.11.*'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/b9/32/99451b1283ec5d92ad77073f12e1c667dc10775384d8f15c2914207149dd/ipython-9.17.1.tar.gz";,
 hash = 
"sha256:8919be8c27f20a6f4423145028063f6637b42a03ce57665bb12015ee1f073529", size 
= 4539289, upload-time = "2026-09-01T08:29:32.6Z" }
 wheels = [
@@ -15730,7 +15730,7 @@ name = "ipython-pygments-lexers"
 version = "1.1.1"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "pygments" },
+    { name = "pygments", marker = "python_full_version >= '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz";,
 hash = 
"sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size 
= 8393, upload-time = "2025-01-17T11:24:34.505Z" }
 wheels = [
@@ -16729,18 +16729,18 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "aiohttp" },
-    { name = "click" },
-    { name = "fastuuid" },
-    { name = "httpx" },
-    { name = "importlib-metadata" },
-    { name = "jinja2" },
-    { name = "jsonschema" },
-    { name = "openai" },
-    { name = "pydantic" },
-    { name = "python-dotenv" },
-    { name = "tiktoken" },
-    { name = "tokenizers" },
+    { name = "aiohttp", marker = "python_full_version < '3.14'" },
+    { name = "click", marker = "python_full_version < '3.14'" },
+    { name = "fastuuid", marker = "python_full_version < '3.14'" },
+    { name = "httpx", marker = "python_full_version < '3.14'" },
+    { name = "importlib-metadata", marker = "python_full_version < '3.14'" },
+    { name = "jinja2", marker = "python_full_version < '3.14'" },
+    { name = "jsonschema", marker = "python_full_version < '3.14'" },
+    { name = "openai", marker = "python_full_version < '3.14'" },
+    { name = "pydantic", marker = "python_full_version < '3.14'" },
+    { name = "python-dotenv", marker = "python_full_version < '3.14'" },
+    { name = "tiktoken", marker = "python_full_version < '3.14'" },
+    { name = "tokenizers", marker = "python_full_version < '3.14'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/0d/59/364fa090acef716effd88e6432d3442982b1f965b2d291f1528db0e6aabb/litellm-1.85.7.tar.gz";,
 hash = 
"sha256:461cc5d5a6c7d5086b67f06f1d5a00c40d6fb0240099764cb040dc4a28452938", size 
= 15363830, upload-time = "2026-06-24T04:54:55.115Z" }
 wheels = [
@@ -16760,19 +16760,19 @@ resolution-markers = [
     "python_full_version == '3.14.*' and sys_platform != 'emscripten' and 
sys_platform != 'win32'",
 ]
 dependencies = [
-    { name = "aiohttp" },
-    { name = "click" },
-    { name = "fastuuid" },
-    { name = "httpx" },
-    { name = "importlib-metadata" },
-    { name = "jinja2" },
-    { name = "jsonschema" },
-    { name = "openai" },
-    { name = "pydantic" },
-    { name = "pydantic-settings" },
-    { name = "python-dotenv" },
-    { name = "tiktoken" },
-    { name = "tokenizers" },
+    { name = "aiohttp", marker = "python_full_version >= '3.14'" },
+    { name = "click", marker = "python_full_version >= '3.14'" },
+    { name = "fastuuid", marker = "python_full_version >= '3.14'" },
+    { name = "httpx", marker = "python_full_version >= '3.14'" },
+    { name = "importlib-metadata", marker = "python_full_version >= '3.14'" },
+    { name = "jinja2", marker = "python_full_version >= '3.14'" },
+    { name = "jsonschema", marker = "python_full_version >= '3.14'" },
+    { name = "openai", marker = "python_full_version >= '3.14'" },
+    { name = "pydantic", marker = "python_full_version >= '3.14'" },
+    { name = "pydantic-settings", marker = "python_full_version >= '3.14'" },
+    { name = "python-dotenv", marker = "python_full_version >= '3.14'" },
+    { name = "tiktoken", marker = "python_full_version >= '3.14'" },
+    { name = "tokenizers", marker = "python_full_version >= '3.14'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/79/fe/d03be8a6be6914fabacef29457a2b3d02128c52c448cc6f3019b8e63488b/litellm-1.96.2.tar.gz";,
 hash = 
"sha256:80d477ae092b05ce023b5084542cb3cb75999b52b1dd2e4d834fb348effc9399", size 
= 17682558, upload-time = "2026-08-11T21:12:24.117Z" }
 wheels = [
@@ -18883,9 +18883,9 @@ name = "opencensus"
 version = "0.11.4"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "google-api-core" },
-    { name = "opencensus-context" },
-    { name = "six" },
+    { name = "google-api-core", marker = "python_full_version < '3.15'" },
+    { name = "opencensus-context", marker = "python_full_version < '3.15'" },
+    { name = "six", marker = "python_full_version < '3.15'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/15/a7/a46dcffa1b63084f9f17fe3c8cb20724c4c8f91009fd0b2cfdb27d5d2b35/opencensus-0.11.4.tar.gz";,
 hash = 
"sha256:cbef87d8b8773064ab60e5c2a1ced58bbaa38a6d052c41aec224958ce544eff2", size 
= 64966, upload-time = "2024-01-03T18:04:07.085Z" }
 wheels = [
@@ -19400,10 +19400,10 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "numpy", version = "2.2.6", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "python-dateutil" },
-    { name = "pytz" },
-    { name = "tzdata" },
+    { name = "numpy", version = "2.2.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
+    { name = "python-dateutil", marker = "python_full_version < '3.11'" },
+    { name = "pytz", marker = "python_full_version < '3.11'" },
+    { name = "tzdata", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz";,
 hash = 
"sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size 
= 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
 wheels = [
@@ -19458,9 +19458,9 @@ wheels = [
 
 [package.optional-dependencies]
 sql-other = [
-    { name = "adbc-driver-postgresql" },
-    { name = "adbc-driver-sqlite" },
-    { name = "sqlalchemy" },
+    { name = "adbc-driver-postgresql", marker = "python_full_version < '3.11'" 
},
+    { name = "adbc-driver-sqlite", marker = "python_full_version < '3.11'" },
+    { name = "sqlalchemy", marker = "python_full_version < '3.11'" },
 ]
 
 [[package]]
@@ -19488,9 +19488,9 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "python-dateutil" },
-    { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform 
== 'win32'" },
+    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.11'" },
+    { name = "python-dateutil", marker = "python_full_version >= '3.11'" },
+    { name = "tzdata", marker = "(python_full_version >= '3.11' and 
sys_platform == 'emscripten') or (python_full_version >= '3.11' and 
sys_platform == 'win32')" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/be/4f/5f3422a2afec5ffc46308b79e53291365a93748b498ac2e58bead0197916/pandas-3.0.5.tar.gz";,
 hash = 
"sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712", size 
= 4658219, upload-time = "2026-07-22T22:19:28.819Z" }
 wheels = [
@@ -19539,9 +19539,9 @@ wheels = [
 
 [package.optional-dependencies]
 sql-other = [
-    { name = "adbc-driver-postgresql" },
-    { name = "adbc-driver-sqlite" },
-    { name = "sqlalchemy" },
+    { name = "adbc-driver-postgresql", marker = "python_full_version >= '3.11' 
and python_full_version < '3.13'" },
+    { name = "adbc-driver-sqlite", marker = "python_full_version >= '3.11' and 
python_full_version < '3.13'" },
+    { name = "sqlalchemy", marker = "python_full_version >= '3.11' and 
python_full_version < '3.13'" },
 ]
 
 [[package]]
@@ -22141,9 +22141,9 @@ name = "python3-saml"
 version = "1.16.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "isodate" },
-    { name = "lxml" },
-    { name = "xmlsec" },
+    { name = "isodate", marker = "python_full_version < '3.13'" },
+    { name = "lxml", marker = "python_full_version < '3.13'" },
+    { name = "xmlsec", marker = "python_full_version < '3.13'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/5d/98/6e0268c3a9893af3d4c5cf670183e0314cd6b5cb034a612d6a7cc5060df8/python3-saml-1.16.0.tar.gz";,
 hash = 
"sha256:97c9669aecabc283c6e5fb4eb264f446b6e006f5267d01c9734f9d8bffdac133", size 
= 83468, upload-time = "2023-10-09T10:37:43.128Z" }
 wheels = [
@@ -22462,14 +22462,14 @@ name = "ray"
 version = "2.58.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "click" },
-    { name = "filelock" },
-    { name = "jsonschema" },
-    { name = "msgpack" },
-    { name = "packaging" },
-    { name = "protobuf" },
-    { name = "pyyaml" },
-    { name = "requests" },
+    { name = "click", marker = "python_full_version < '3.15'" },
+    { name = "filelock", marker = "python_full_version < '3.15'" },
+    { name = "jsonschema", marker = "python_full_version < '3.15'" },
+    { name = "msgpack", marker = "python_full_version < '3.15'" },
+    { name = "packaging", marker = "python_full_version < '3.15'" },
+    { name = "protobuf", marker = "python_full_version < '3.15'" },
+    { name = "pyyaml", marker = "python_full_version < '3.15'" },
+    { name = "requests", marker = "python_full_version < '3.15'" },
 ]
 wheels = [
     { url = 
"https://files.pythonhosted.org/packages/2f/41/760f9a076085d043d9fbb8f840436e1d21e47682c976d3620efbf02a3b9e/ray-2.58.0-cp310-cp310-macosx_12_0_arm64.whl";,
 hash = 
"sha256:a2235a5a17e865be053e8aff6d9e9e36d1edb45ad8162f57f7cdfd0202a5a397", size 
= 67217855, upload-time = "2026-08-23T04:45:46.075Z" },
@@ -22496,20 +22496,20 @@ wheels = [
 
 [package.optional-dependencies]
 default = [
-    { name = "aiohttp" },
-    { name = "aiohttp-cors" },
-    { name = "colorful" },
-    { name = "grpcio" },
-    { name = "opencensus" },
-    { name = "opentelemetry-exporter-prometheus" },
-    { name = "opentelemetry-proto" },
-    { name = "opentelemetry-sdk" },
-    { name = "prometheus-client" },
-    { name = "py-spy" },
-    { name = "pydantic" },
-    { name = "requests" },
-    { name = "smart-open" },
-    { name = "virtualenv" },
+    { name = "aiohttp", marker = "python_full_version < '3.15'" },
+    { name = "aiohttp-cors", marker = "python_full_version < '3.15'" },
+    { name = "colorful", marker = "python_full_version < '3.15'" },
+    { name = "grpcio", marker = "python_full_version < '3.15'" },
+    { name = "opencensus", marker = "python_full_version < '3.15'" },
+    { name = "opentelemetry-exporter-prometheus", marker = 
"python_full_version < '3.15'" },
+    { name = "opentelemetry-proto", marker = "python_full_version < '3.15'" },
+    { name = "opentelemetry-sdk", marker = "python_full_version < '3.15'" },
+    { name = "prometheus-client", marker = "python_full_version < '3.15'" },
+    { name = "py-spy", marker = "python_full_version < '3.15'" },
+    { name = "pydantic", marker = "python_full_version < '3.15'" },
+    { name = "requests", marker = "python_full_version < '3.15'" },
+    { name = "smart-open", marker = "python_full_version < '3.15'" },
+    { name = "virtualenv", marker = "python_full_version < '3.15'" },
 ]
 
 [[package]]
@@ -23307,10 +23307,10 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "joblib" },
-    { name = "numpy", version = "2.2.6", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "scipy", version = "1.15.3", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "threadpoolctl" },
+    { name = "joblib", marker = "python_full_version < '3.11'" },
+    { name = "numpy", version = "2.2.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
+    { name = "scipy", version = "1.15.3", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
+    { name = "threadpoolctl", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/37/59/44985a2bdc95c74e34fef3d10cb5d93ce13b0e2a7baefffe1b53853b502d/scikit_learn-1.5.2.tar.gz";,
 hash = 
"sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", size 
= 7001680, upload-time = "2024-09-11T15:50:10.957Z" }
 wheels = [
@@ -23361,12 +23361,12 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "joblib" },
-    { name = "narwhals" },
-    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "scipy", version = "1.17.1", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.12'" },
+    { name = "joblib", marker = "python_full_version >= '3.11'" },
+    { name = "narwhals", marker = "python_full_version >= '3.11'" },
+    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.11'" },
+    { name = "scipy", version = "1.17.1", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version == '3.11.*'" },
     { name = "scipy", version = "1.18.1", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.12'" },
-    { name = "threadpoolctl" },
+    { name = "threadpoolctl", marker = "python_full_version >= '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/d2/eb/eaf5e07fcc0da7149b0e084f24e54edd7441b9a89ce7e034032ae97fe3a0/scikit_learn-1.9.1.tar.gz";,
 hash = 
"sha256:629cada3e33e2b9bf376cdc7614a47a4140b8aedc1d836579e359736fbd82977", size 
= 7786908, upload-time = "2026-09-10T18:34:04.679Z" }
 wheels = [
@@ -23423,7 +23423,7 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "numpy", version = "2.2.6", source = { registry = 
"https://pypi.org/simple"; } },
+    { name = "numpy", version = "2.2.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz";,
 hash = 
"sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size 
= 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
 wheels = [
@@ -23485,7 +23485,7 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; } },
+    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version == '3.11.*'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz";,
 hash = 
"sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size 
= 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
 wheels = [
@@ -23572,7 +23572,7 @@ resolution-markers = [
     "(python_full_version == '3.12.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.12.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; } },
+    { name = "numpy", version = "2.4.6", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.12'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz";,
 hash = 
"sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size 
= 30781235, upload-time = "2026-08-21T23:28:50.599Z" }
 wheels = [
@@ -23678,8 +23678,8 @@ name = "secretstorage"
 version = "3.5.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "cryptography" },
-    { name = "jeepney" },
+    { name = "cryptography", marker = "(python_full_version >= '3.14' and 
sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 
'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or 
(platform_machine != 'arm64' and sys_platform == 'darwin') or (sys_platform != 
'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" },
+    { name = "jeepney", marker = "(python_full_version >= '3.14' and 
sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 
'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or 
(platform_machine != 'arm64' and sys_platform == 'darwin') or (sys_platform != 
'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz";,
 hash = 
"sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size 
= 19884, upload-time = "2025-11-23T19:02:53.191Z" }
 wheels = [
@@ -23878,7 +23878,7 @@ name = "smart-open"
 version = "8.0.1"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "wrapt" },
+    { name = "wrapt", marker = "python_full_version < '3.15'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/76/53/9c513747547fd595d5c143259129ea8b9c3ea2f6b7bb9dcea2b1966ded3c/smart_open-8.0.1.tar.gz";,
 hash = 
"sha256:18b1c4496003c6902be17c15f032b5c319f307c89c6ae9e6b028b508bed8b2cf", size 
= 61882, upload-time = "2026-07-15T13:56:10.492Z" }
 wheels = [
@@ -23992,15 +23992,15 @@ name = "snowflake-snowpark-python"
 version = "1.55.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "cloudpickle" },
-    { name = "protobuf" },
-    { name = "python-dateutil" },
-    { name = "pyyaml" },
-    { name = "setuptools" },
-    { name = "snowflake-connector-python" },
-    { name = "typing-extensions" },
-    { name = "tzlocal" },
-    { name = "wheel" },
+    { name = "cloudpickle", marker = "python_full_version < '3.14'" },
+    { name = "protobuf", marker = "python_full_version < '3.14'" },
+    { name = "python-dateutil", marker = "python_full_version < '3.14'" },
+    { name = "pyyaml", marker = "python_full_version < '3.14'" },
+    { name = "setuptools", marker = "python_full_version < '3.14'" },
+    { name = "snowflake-connector-python", marker = "python_full_version < 
'3.14'" },
+    { name = "typing-extensions", marker = "python_full_version < '3.14'" },
+    { name = "tzlocal", marker = "python_full_version < '3.14'" },
+    { name = "wheel", marker = "python_full_version < '3.14'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/c6/35/84d01dd46679fd2324fca363e59599f52af32e5ee9b9fa68da17926f6690/snowflake_snowpark_python-1.55.0.tar.gz";,
 hash = 
"sha256:2accc4e50f44635b8c9d5ed52f26a4d9885d00f17fcc16eee532e8d9d274c627", size 
= 1799269, upload-time = "2026-09-10T18:57:10.805Z" }
 wheels = [
@@ -24047,23 +24047,23 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "alabaster" },
-    { name = "babel" },
-    { name = "colorama", marker = "sys_platform == 'win32'" },
-    { name = "docutils", version = "0.21.2", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "imagesize" },
-    { name = "jinja2" },
-    { name = "packaging" },
-    { name = "pygments" },
-    { name = "requests" },
-    { name = "snowballstemmer" },
-    { name = "sphinxcontrib-applehelp" },
-    { name = "sphinxcontrib-devhelp" },
-    { name = "sphinxcontrib-htmlhelp" },
-    { name = "sphinxcontrib-jsmath" },
-    { name = "sphinxcontrib-qthelp" },
-    { name = "sphinxcontrib-serializinghtml" },
-    { name = "tomli" },
+    { name = "alabaster", marker = "python_full_version < '3.11'" },
+    { name = "babel", marker = "python_full_version < '3.11'" },
+    { name = "colorama", marker = "python_full_version < '3.11' and 
sys_platform == 'win32'" },
+    { name = "docutils", version = "0.21.2", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
+    { name = "imagesize", marker = "python_full_version < '3.11'" },
+    { name = "jinja2", marker = "python_full_version < '3.11'" },
+    { name = "packaging", marker = "python_full_version < '3.11'" },
+    { name = "pygments", marker = "python_full_version < '3.11'" },
+    { name = "requests", marker = "python_full_version < '3.11'" },
+    { name = "snowballstemmer", marker = "python_full_version < '3.11'" },
+    { name = "sphinxcontrib-applehelp", marker = "python_full_version < 
'3.11'" },
+    { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" 
},
+    { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" 
},
+    { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" },
+    { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" },
+    { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < 
'3.11'" },
+    { name = "tomli", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz";,
 hash = 
"sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size 
= 8184611, upload-time = "2024-10-13T20:27:13.93Z" }
 wheels = [
@@ -24081,23 +24081,23 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "alabaster" },
-    { name = "babel" },
-    { name = "colorama", marker = "sys_platform == 'win32'" },
-    { name = "docutils", version = "0.22.4", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "imagesize" },
-    { name = "jinja2" },
-    { name = "packaging" },
-    { name = "pygments" },
-    { name = "requests" },
-    { name = "roman-numerals" },
-    { name = "snowballstemmer" },
-    { name = "sphinxcontrib-applehelp" },
-    { name = "sphinxcontrib-devhelp" },
-    { name = "sphinxcontrib-htmlhelp" },
-    { name = "sphinxcontrib-jsmath" },
-    { name = "sphinxcontrib-qthelp" },
-    { name = "sphinxcontrib-serializinghtml" },
+    { name = "alabaster", marker = "python_full_version == '3.11.*'" },
+    { name = "babel", marker = "python_full_version == '3.11.*'" },
+    { name = "colorama", marker = "python_full_version == '3.11.*' and 
sys_platform == 'win32'" },
+    { name = "docutils", version = "0.22.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version == '3.11.*'" },
+    { name = "imagesize", marker = "python_full_version == '3.11.*'" },
+    { name = "jinja2", marker = "python_full_version == '3.11.*'" },
+    { name = "packaging", marker = "python_full_version == '3.11.*'" },
+    { name = "pygments", marker = "python_full_version == '3.11.*'" },
+    { name = "requests", marker = "python_full_version == '3.11.*'" },
+    { name = "roman-numerals", marker = "python_full_version == '3.11.*'" },
+    { name = "snowballstemmer", marker = "python_full_version == '3.11.*'" },
+    { name = "sphinxcontrib-applehelp", marker = "python_full_version == 
'3.11.*'" },
+    { name = "sphinxcontrib-devhelp", marker = "python_full_version == 
'3.11.*'" },
+    { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == 
'3.11.*'" },
+    { name = "sphinxcontrib-jsmath", marker = "python_full_version == 
'3.11.*'" },
+    { name = "sphinxcontrib-qthelp", marker = "python_full_version == 
'3.11.*'" },
+    { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == 
'3.11.*'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz";,
 hash = 
"sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size 
= 8710502, upload-time = "2025-12-04T07:45:27.343Z" }
 wheels = [
@@ -24125,23 +24125,23 @@ resolution-markers = [
     "(python_full_version == '3.12.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.12.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "alabaster" },
-    { name = "babel" },
-    { name = "colorama", marker = "sys_platform == 'win32'" },
-    { name = "docutils", version = "0.22.4", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "imagesize" },
-    { name = "jinja2" },
-    { name = "packaging" },
-    { name = "pygments" },
-    { name = "requests" },
-    { name = "roman-numerals" },
-    { name = "snowballstemmer" },
-    { name = "sphinxcontrib-applehelp" },
-    { name = "sphinxcontrib-devhelp" },
-    { name = "sphinxcontrib-htmlhelp" },
-    { name = "sphinxcontrib-jsmath" },
-    { name = "sphinxcontrib-qthelp" },
-    { name = "sphinxcontrib-serializinghtml" },
+    { name = "alabaster", marker = "python_full_version >= '3.12'" },
+    { name = "babel", marker = "python_full_version >= '3.12'" },
+    { name = "colorama", marker = "python_full_version >= '3.12' and 
sys_platform == 'win32'" },
+    { name = "docutils", version = "0.22.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.12'" },
+    { name = "imagesize", marker = "python_full_version >= '3.12'" },
+    { name = "jinja2", marker = "python_full_version >= '3.12'" },
+    { name = "packaging", marker = "python_full_version >= '3.12'" },
+    { name = "pygments", marker = "python_full_version >= '3.12'" },
+    { name = "requests", marker = "python_full_version >= '3.12'" },
+    { name = "roman-numerals", marker = "python_full_version >= '3.12'" },
+    { name = "snowballstemmer", marker = "python_full_version >= '3.12'" },
+    { name = "sphinxcontrib-applehelp", marker = "python_full_version >= 
'3.12'" },
+    { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" 
},
+    { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= 
'3.12'" },
+    { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" 
},
+    { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" 
},
+    { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= 
'3.12'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz";,
 hash = 
"sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size 
= 8718324, upload-time = "2025-12-31T15:09:27.646Z" }
 wheels = [
@@ -24207,12 +24207,12 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "colorama" },
-    { name = "sphinx", version = "8.1.3", source = { registry = 
"https://pypi.org/simple"; } },
-    { name = "starlette" },
-    { name = "uvicorn" },
-    { name = "watchfiles" },
-    { name = "websockets" },
+    { name = "colorama", marker = "python_full_version < '3.11'" },
+    { name = "sphinx", version = "8.1.3", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
+    { name = "starlette", marker = "python_full_version < '3.11'" },
+    { name = "uvicorn", marker = "python_full_version < '3.11'" },
+    { name = "watchfiles", marker = "python_full_version < '3.11'" },
+    { name = "websockets", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz";,
 hash = 
"sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size 
= 14023, upload-time = "2024-10-02T23:15:30.172Z" }
 wheels = [
@@ -24244,13 +24244,13 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "colorama" },
-    { name = "sphinx", version = "9.0.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.12'" },
+    { name = "colorama", marker = "python_full_version >= '3.11'" },
+    { name = "sphinx", version = "9.0.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version == '3.11.*'" },
     { name = "sphinx", version = "9.1.0", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.12'" },
-    { name = "starlette" },
-    { name = "uvicorn" },
-    { name = "watchfiles" },
-    { name = "websockets" },
+    { name = "starlette", marker = "python_full_version >= '3.11'" },
+    { name = "uvicorn", marker = "python_full_version >= '3.11'" },
+    { name = "watchfiles", marker = "python_full_version >= '3.11'" },
+    { name = "websockets", marker = "python_full_version >= '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz";,
 hash = 
"sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size 
= 15200, upload-time = "2025-08-25T18:44:55.436Z" }
 wheels = [
@@ -24280,7 +24280,7 @@ resolution-markers = [
     "(python_full_version < '3.11' and platform_machine != 'arm64') or 
(python_full_version < '3.11' and sys_platform != 'darwin')",
 ]
 dependencies = [
-    { name = "sphinx", version = "8.1.3", source = { registry = 
"https://pypi.org/simple"; } },
+    { name = "sphinx", version = "8.1.3", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz";,
 hash = 
"sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size 
= 2193689, upload-time = "2024-08-02T13:48:44.277Z" }
 wheels = [
@@ -24312,7 +24312,7 @@ resolution-markers = [
     "(python_full_version == '3.11.*' and platform_machine != 'arm64' and 
sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform 
!= 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 dependencies = [
-    { name = "sphinx", version = "9.0.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version < '3.12'" },
+    { name = "sphinx", version = "9.0.4", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version == '3.11.*'" },
     { name = "sphinx", version = "9.1.0", source = { registry = 
"https://pypi.org/simple"; }, marker = "python_full_version >= '3.12'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz";,
 hash = 
"sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size 
= 2203582, upload-time = "2026-01-19T13:12:53.297Z" }
@@ -26390,7 +26390,7 @@ name = "xmlsec"
 version = "1.3.17"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
-    { name = "lxml" },
+    { name = "lxml", marker = "python_full_version < '3.13'" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/49/14/538b75379e6ab8f688f14d8663e2ab138d9c778bac4999d155b5f33c71c1/xmlsec-1.3.17.tar.gz";,
 hash = 
"sha256:f3fac9ae679f66585925cc00c5f6839ae36c1d03157619571dee18acc05b9c01", size 
= 115637, upload-time = "2025-11-11T16:20:46.019Z" }
 wheels = [

Reply via email to