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

rahulvats pushed a commit to branch execution-api-consolidate-2026-04-06
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 29f7228809e75a787e1b10797c837cde3c369aaf
Author: vatsrahul1001 <[email protected]>
AuthorDate: Mon Mar 30 18:58:16 2026 +0530

    Consolidate unreleased Execution API versions under 2026-04-06
---
 .../api_fastapi/execution_api/versions/__init__.py |  19 ++--
 .../execution_api/versions/v2025_11_07.py          |  54 -----------
 .../execution_api/versions/v2025_12_08.py          |  41 --------
 .../versions/{v2026_03_31.py => v2026_04_06.py}    | 105 ++++++++++++++++-----
 .../execution_api/versions/v2026_04_13.py          |  28 ------
 .../execution_api/versions/head/test_dag_runs.py   |   2 +-
 .../execution_api/versions/v2026_03_31/__init__.py |  16 ----
 .../{v2025_11_07 => v2026_04_06}/__init__.py       |   0
 .../{v2025_11_07 => v2026_04_06}/test_dag_runs.py  |   3 +-
 .../{v2026_03_31 => v2026_04_06}/test_dags.py      |   3 +-
 .../test_task_instances.py                         |   4 +-
 .../src/airflow/sdk/api/datamodels/_generated.py   |   2 +-
 12 files changed, 100 insertions(+), 177 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py 
b/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py
index 2cbe2e3007b..646831501a7 100644
--- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py
+++ b/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py
@@ -28,31 +28,30 @@ from airflow.api_fastapi.execution_api.versions.v2025_08_10 
import (
 from airflow.api_fastapi.execution_api.versions.v2025_09_23 import 
AddDagVersionIdField
 from airflow.api_fastapi.execution_api.versions.v2025_10_27 import 
MakeDagRunConfNullable
 from airflow.api_fastapi.execution_api.versions.v2025_11_05 import 
AddTriggeringUserNameField
-from airflow.api_fastapi.execution_api.versions.v2025_11_07 import 
AddPartitionKeyField
-from airflow.api_fastapi.execution_api.versions.v2025_12_08 import (
+from airflow.api_fastapi.execution_api.versions.v2026_04_06 import (
+    AddDagEndpoint,
     AddDagRunDetailEndpoint,
-    MovePreviousRunEndpoint,
-)
-from airflow.api_fastapi.execution_api.versions.v2026_03_31 import (
     AddNoteField,
+    AddPartitionKeyField,
     MakeDagRunStartDateNullable,
     ModifyDeferredTaskKwargsToJsonValue,
+    MovePreviousRunEndpoint,
     RemoveUpstreamMapIndexesField,
 )
-from airflow.api_fastapi.execution_api.versions.v2026_04_13 import 
AddDagEndpoint
 
 bundle = VersionBundle(
     HeadVersion(),
-    Version("2026-04-13", AddDagEndpoint),
     Version(
-        "2026-03-31",
+        "2026-04-06",
+        AddPartitionKeyField,
+        MovePreviousRunEndpoint,
+        AddDagRunDetailEndpoint,
         MakeDagRunStartDateNullable,
         ModifyDeferredTaskKwargsToJsonValue,
         RemoveUpstreamMapIndexesField,
         AddNoteField,
+        AddDagEndpoint,
     ),
-    Version("2025-12-08", MovePreviousRunEndpoint, AddDagRunDetailEndpoint),
-    Version("2025-11-07", AddPartitionKeyField),
     Version("2025-11-05", AddTriggeringUserNameField),
     Version("2025-10-27", MakeDagRunConfNullable),
     Version("2025-09-23", AddDagVersionIdField),
diff --git 
a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_11_07.py 
b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_11_07.py
deleted file mode 100644
index 117ba492455..00000000000
--- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_11_07.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from cadwyn import ResponseInfo, VersionChange, 
convert_response_to_previous_version_for, schema
-
-from airflow.api_fastapi.execution_api.datamodels.asset_event import (
-    AssetEventResponse,
-    AssetEventsResponse,
-    DagRunAssetReference,
-)
-from airflow.api_fastapi.execution_api.datamodels.dagrun import 
TriggerDAGRunPayload
-from airflow.api_fastapi.execution_api.datamodels.taskinstance import DagRun, 
TIRunContext
-
-
-class AddPartitionKeyField(VersionChange):
-    """Add the `partition_key` field to DagRun model."""
-
-    description = __doc__
-
-    instructions_to_migrate_to_previous_version = (
-        schema(DagRun).field("partition_key").didnt_exist,
-        schema(AssetEventResponse).field("partition_key").didnt_exist,
-        schema(TriggerDAGRunPayload).field("partition_key").didnt_exist,
-        schema(DagRunAssetReference).field("partition_key").didnt_exist,
-    )
-
-    @convert_response_to_previous_version_for(TIRunContext)  # type: 
ignore[arg-type]
-    def remove_partition_key_from_dag_run(response: ResponseInfo) -> None:  # 
type: ignore[misc]
-        """Remove the `partition_key` field from the dag_run object when 
converting to the previous version."""
-        if "dag_run" in response.body and isinstance(response.body["dag_run"], 
dict):
-            response.body["dag_run"].pop("partition_key", None)
-
-    @convert_response_to_previous_version_for(AssetEventsResponse)  # type: 
ignore[arg-type]
-    def remove_partition_key_from_asset_events(response: ResponseInfo) -> 
None:  # type: ignore[misc]
-        """Remove the `partition_key` field from the dag_run object when 
converting to the previous version."""
-        events = response.body["asset_events"]
-        for elem in events:
-            elem.pop("partition_key", None)
diff --git 
a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_12_08.py 
b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_12_08.py
deleted file mode 100644
index 3394036bf15..00000000000
--- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_12_08.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from cadwyn import VersionChange, endpoint
-
-
-class MovePreviousRunEndpoint(VersionChange):
-    """Add new previous-run endpoint and migrate old endpoint."""
-
-    description = __doc__
-
-    instructions_to_migrate_to_previous_version = (
-        endpoint("/dag-runs/previous", ["GET"]).didnt_exist,
-        endpoint("/dag-runs/{dag_id}/previous", ["GET"]).existed,
-    )
-
-
-class AddDagRunDetailEndpoint(VersionChange):
-    """Add dag run detail endpoint."""
-
-    description = __doc__
-
-    instructions_to_migrate_to_previous_version = (
-        endpoint("/dag-runs/{dag_id}/{run_id}", ["GET"]).didnt_exist,
-    )
diff --git 
a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_03_31.py 
b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_06.py
similarity index 62%
rename from 
airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_03_31.py
rename to 
airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_06.py
index 2d14493e81f..85ec1f2a608 100644
--- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_03_31.py
+++ b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_06.py
@@ -19,9 +19,15 @@ from __future__ import annotations
 
 from typing import Any
 
-from cadwyn import ResponseInfo, VersionChange, 
convert_response_to_previous_version_for, schema
+from cadwyn import ResponseInfo, VersionChange, 
convert_response_to_previous_version_for, endpoint, schema
 
 from airflow.api_fastapi.common.types import UtcDateTime
+from airflow.api_fastapi.execution_api.datamodels.asset_event import (
+    AssetEventResponse,
+    AssetEventsResponse,
+    DagRunAssetReference,
+)
+from airflow.api_fastapi.execution_api.datamodels.dagrun import 
TriggerDAGRunPayload
 from airflow.api_fastapi.execution_api.datamodels.taskinstance import (
     DagRun,
     TIDeferredStatePayload,
@@ -29,6 +35,79 @@ from 
airflow.api_fastapi.execution_api.datamodels.taskinstance import (
 )
 
 
+class AddPartitionKeyField(VersionChange):
+    """Add the `partition_key` field to DagRun model."""
+
+    description = __doc__
+
+    instructions_to_migrate_to_previous_version = (
+        schema(DagRun).field("partition_key").didnt_exist,
+        schema(AssetEventResponse).field("partition_key").didnt_exist,
+        schema(TriggerDAGRunPayload).field("partition_key").didnt_exist,
+        schema(DagRunAssetReference).field("partition_key").didnt_exist,
+    )
+
+    @convert_response_to_previous_version_for(TIRunContext)  # type: 
ignore[arg-type]
+    def remove_partition_key_from_dag_run(response: ResponseInfo) -> None:  # 
type: ignore[misc]
+        """Remove the `partition_key` field from the dag_run object when 
converting to the previous version."""
+        if "dag_run" in response.body and isinstance(response.body["dag_run"], 
dict):
+            response.body["dag_run"].pop("partition_key", None)
+
+    @convert_response_to_previous_version_for(AssetEventsResponse)  # type: 
ignore[arg-type]
+    def remove_partition_key_from_asset_events(response: ResponseInfo) -> 
None:  # type: ignore[misc]
+        """Remove the `partition_key` field from the dag_run object when 
converting to the previous version."""
+        events = response.body["asset_events"]
+        for elem in events:
+            elem.pop("partition_key", None)
+
+
+class MovePreviousRunEndpoint(VersionChange):
+    """Add new previous-run endpoint and migrate old endpoint."""
+
+    description = __doc__
+
+    instructions_to_migrate_to_previous_version = (
+        endpoint("/dag-runs/previous", ["GET"]).didnt_exist,
+        endpoint("/dag-runs/{dag_id}/previous", ["GET"]).existed,
+    )
+
+
+class AddDagRunDetailEndpoint(VersionChange):
+    """Add dag run detail endpoint."""
+
+    description = __doc__
+
+    instructions_to_migrate_to_previous_version = (
+        endpoint("/dag-runs/{dag_id}/{run_id}", ["GET"]).didnt_exist,
+    )
+
+
+class MakeDagRunStartDateNullable(VersionChange):
+    """Make DagRun.start_date field nullable for runs that haven't started 
yet."""
+
+    description = __doc__
+
+    instructions_to_migrate_to_previous_version = 
(schema(DagRun).field("start_date").had(type=UtcDateTime),)
+
+    @convert_response_to_previous_version_for(TIRunContext)  # type: 
ignore[arg-type]
+    def ensure_start_date_in_ti_run_context(response: ResponseInfo) -> None:  
# type: ignore[misc]
+        """
+        Ensure start_date is never None in DagRun for previous API versions.
+
+        Older Task SDK clients expect start_date to be non-nullable. When the
+        DagRun hasn't started yet (e.g. queued), fall back to run_after.
+        """
+        dag_run = response.body.get("dag_run")
+        if isinstance(dag_run, dict) and dag_run.get("start_date") is None:
+            dag_run["start_date"] = dag_run.get("run_after")
+
+    @convert_response_to_previous_version_for(DagRun)  # type: ignore[arg-type]
+    def ensure_start_date_in_dag_run(response: ResponseInfo) -> None:  # type: 
ignore[misc]
+        """Ensure start_date is never None in direct DagRun responses for 
previous API versions."""
+        if response.body.get("start_date") is None:
+            response.body["start_date"] = response.body.get("run_after")
+
+
 class ModifyDeferredTaskKwargsToJsonValue(VersionChange):
     """Change the types of `trigger_kwargs` and `next_kwargs` in 
TIDeferredStatePayload to JsonValue."""
 
@@ -71,27 +150,9 @@ class AddNoteField(VersionChange):
             response.body["dag_run"].pop("note", None)
 
 
-class MakeDagRunStartDateNullable(VersionChange):
-    """Make DagRun.start_date field nullable for runs that haven't started 
yet."""
+class AddDagEndpoint(VersionChange):
+    """Add the `/dags/{dag_id}` endpoint."""
 
     description = __doc__
 
-    instructions_to_migrate_to_previous_version = 
(schema(DagRun).field("start_date").had(type=UtcDateTime),)
-
-    @convert_response_to_previous_version_for(TIRunContext)  # type: 
ignore[arg-type]
-    def ensure_start_date_in_ti_run_context(response: ResponseInfo) -> None:  
# type: ignore[misc]
-        """
-        Ensure start_date is never None in DagRun for previous API versions.
-
-        Older Task SDK clients expect start_date to be non-nullable. When the
-        DagRun hasn't started yet (e.g. queued), fall back to run_after.
-        """
-        dag_run = response.body.get("dag_run")
-        if isinstance(dag_run, dict) and dag_run.get("start_date") is None:
-            dag_run["start_date"] = dag_run.get("run_after")
-
-    @convert_response_to_previous_version_for(DagRun)  # type: ignore[arg-type]
-    def ensure_start_date_in_dag_run(response: ResponseInfo) -> None:  # type: 
ignore[misc]
-        """Ensure start_date is never None in direct DagRun responses for 
previous API versions."""
-        if response.body.get("start_date") is None:
-            response.body["start_date"] = response.body.get("run_after")
+    instructions_to_migrate_to_previous_version = (endpoint("/dags/{dag_id}", 
["GET"]).didnt_exist,)
diff --git 
a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_13.py 
b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_13.py
deleted file mode 100644
index 95da513d7bc..00000000000
--- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_13.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from cadwyn import VersionChange, endpoint
-
-
-class AddDagEndpoint(VersionChange):
-    """Add the `/dags/{dag_id}` endpoint."""
-
-    description = __doc__
-
-    instructions_to_migrate_to_previous_version = (endpoint("/dags/{dag_id}", 
["GET"]).didnt_exist,)
diff --git 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py
 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py
index 2aa3be51220..a2910313951 100644
--- 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py
+++ 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py
@@ -264,7 +264,7 @@ class TestDagRunDetail:
     def test_get_state(self, client, session, dag_maker):
         dag_id = "test_dag_id"
         # Named deliberately to check if this routes correctly.
-        # See v2025_11_07.test_dag_runs::test_get_previous_dag_run_redirect
+        # See v2026_04_06.test_dag_runs::test_get_previous_dag_run_redirect
         run_id = "previous"
 
         with dag_maker(dag_id=dag_id, schedule=None, session=session, 
serialized=True):
diff --git 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/__init__.py
 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/__init__.py
deleted file mode 100644
index 13a83393a91..00000000000
--- 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
diff --git 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/__init__.py
 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/__init__.py
similarity index 100%
rename from 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/__init__.py
rename to 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/__init__.py
diff --git 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/test_dag_runs.py
 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dag_runs.py
similarity index 93%
rename from 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/test_dag_runs.py
rename to 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dag_runs.py
index c2821dce55b..a3c7ff77d14 100644
--- 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/test_dag_runs.py
+++ 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dag_runs.py
@@ -27,7 +27,8 @@ pytestmark = pytest.mark.db_test
 
 @pytest.fixture
 def ver_client(client):
-    client.headers["Airflow-API-Version"] = "2025-11-07"
+    """Last released execution API (before 2026-04-06); uses legacy 
previous-run path."""
+    client.headers["Airflow-API-Version"] = "2025-11-05"
     return client
 
 
diff --git 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_dags.py
 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dags.py
similarity index 89%
rename from 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_dags.py
rename to 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dags.py
index 72693443944..5fe7fb26fda 100644
--- 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_dags.py
+++ 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dags.py
@@ -24,7 +24,8 @@ pytestmark = pytest.mark.db_test
 
 @pytest.fixture
 def old_ver_client(client):
-    client.headers["Airflow-API-Version"] = "2026-03-31"
+    """Last released execution API before `/dags/{dag_id}` was added."""
+    client.headers["Airflow-API-Version"] = "2025-11-05"
     return client
 
 
diff --git 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_task_instances.py
 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_task_instances.py
similarity index 96%
rename from 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_task_instances.py
rename to 
airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_task_instances.py
index 7fb44ce7ebe..8117ac6b69c 100644
--- 
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_task_instances.py
+++ 
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_task_instances.py
@@ -40,8 +40,8 @@ RUN_PATCH_BODY = {
 
 @pytest.fixture
 def old_ver_client(client):
-    """Client configured to use API version before start_date nullable 
change."""
-    client.headers["Airflow-API-Version"] = "2025-12-08"
+    """Last released execution API before nullable DagRun.start_date 
(2026-04-06 bundle)."""
+    client.headers["Airflow-API-Version"] = "2025-11-05"
     return client
 
 
diff --git a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py 
b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
index b6c08e9d76c..e08f00562d3 100644
--- a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
+++ b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
@@ -27,7 +27,7 @@ from uuid import UUID
 
 from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, JsonValue, 
RootModel
 
-API_VERSION: Final[str] = "2026-04-13"
+API_VERSION: Final[str] = "2026-04-06"
 
 
 class AssetAliasReferenceAssetEventDagRun(BaseModel):

Reply via email to