Repository: TobikoData/sqlmesh
Pull request #2079 (2024-02-02 22:29:30)

chore: upgrade black and mypy

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/TobikoData/sqlmesh?pullRequest=2079) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/TobikoData/sqlmesh?pullRequest=2079) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/TobikoData/sqlmesh?pullRequest=2079) it.</sub>

Patch:
diff --git a/examples/airflow/docker_compose_decorator.py b/examples/airflow/docker_compose_decorator.py
index babf3727..21d7fda8 100644
--- a/examples/airflow/docker_compose_decorator.py
+++ b/examples/airflow/docker_compose_decorator.py
@@ -108,13 +108,13 @@ for airflow_component in ["airflow-scheduler", "airflow-worker"]:
             raise RuntimeError(
                 "Tried to use Databricks Airflow Engine operator but did not define `DATABRICKS_SERVER_HOSTNAME`, `DATABRICKS_TOKEN`, `DATABRICKS_HTTP_PATH`"
             )
-        environment_variables[
-            "AIRFLOW_CONN_DATABRICKS_DEFAULT"
-        ] = "databricks://${DATABRICKS_SERVER_HOSTNAME}?token=${DATABRICKS_TOKEN}&http_path=${DATABRICKS_HTTP_PATH}"
+        environment_variables["AIRFLOW_CONN_DATABRICKS_DEFAULT"] = (
+            "databricks://${DATABRICKS_SERVER_HOSTNAME}?token=${DATABRICKS_TOKEN}&http_path=${DATABRICKS_HTTP_PATH}"
+        )
     if os.getenv("DEMO_GITHUB_PAT"):
-        environment_variables[
-            "AIRFLOW_CONN_GITHUB_DEFAULT"
-        ] = '{"conn_type": "github", "password": "${DEMO_GITHUB_PAT}"}'
+        environment_variables["AIRFLOW_CONN_GITHUB_DEFAULT"] = (
+            '{"conn_type": "github", "password": "${DEMO_GITHUB_PAT}"}'
+        )
     docker_compose["services"][airflow_component]["environment"].update(environment_variables)
 
 
diff --git a/setup.py b/setup.py
index 48c1d371..7dbf8da8 100644
--- a/setup.py
+++ b/setup.py
@@ -63,7 +63,7 @@ setup(
             "autoflake==1.7.7",
             "agate==1.6.3",
             "beautifulsoup4",
-            "black==22.6.0",
+            "black==24.1.1",
             "dbt-bigquery",
             "dbt-core",
             "dbt-duckdb>=1.4.2",
@@ -74,7 +74,7 @@ setup(
             "google-cloud-bigquery",
             "google-cloud-bigquery-storage",
             "isort==5.10.1",
-            "mypy~=1.4.0",
+            "mypy~=1.8.0",
             # Pendulum 3.0.0 contains a breaking change for Airflow.
             # To test if this is fixed with future versions, check if this line works:
             # https://github.com/apache/airflow/blob/main/airflow/settings.py#L59
diff --git a/sqlmesh/__init__.py b/sqlmesh/__init__.py
index ab545b70..8006475a 100644
--- a/sqlmesh/__init__.py
+++ b/sqlmesh/__init__.py
@@ -1,6 +1,7 @@
 """
 .. include:: ../README.md
 """
+
 from __future__ import annotations
 
 import glob
diff --git a/sqlmesh/core/context.py b/sqlmesh/core/context.py
index 7960559a..7f5c4b3e 100644
--- a/sqlmesh/core/context.py
+++ b/sqlmesh/core/context.py
@@ -30,6 +30,7 @@ context = Context(path="example")
 context.test()
 ```
 """
+
 from __future__ import annotations
 
 import abc
@@ -518,16 +519,14 @@ class Context(BaseContext):
     @t.overload
     def get_model(
         self, model_or_snapshot: ModelOrSnapshot, raise_if_missing: Literal[True] = True
-    ) -> Model:
-        ...
+    ) -> Model: ...
 
     @t.overload
     def get_model(
         self,
         model_or_snapshot: ModelOrSnapshot,
         raise_if_missing: Literal[False] = False,
-    ) -> t.Optional[Model]:
-        ...
+    ) -> t.Optional[Model]: ...
 
     def get_model(
         self, model_or_snapshot: ModelOrSnapshot, raise_if_missing: bool = False
@@ -557,20 +556,17 @@ class Context(BaseContext):
         return model
 
     @t.overload
-    def get_snapshot(self, node_or_snapshot: NodeOrSnapshot) -> t.Optional[Snapshot]:
-        ...
+    def get_snapshot(self, node_or_snapshot: NodeOrSnapshot) -> t.Optional[Snapshot]: ...
 
     @t.overload
     def get_snapshot(
         self, node_or_snapshot: NodeOrSnapshot, raise_if_missing: Literal[True]
-    ) -> Snapshot:
-        ...
+    ) -> Snapshot: ...
 
     @t.overload
     def get_snapshot(
         self, node_or_snapshot: NodeOrSnapshot, raise_if_missing: Literal[False]
-    ) -> t.Optional[Snapshot]:
-        ...
+    ) -> t.Optional[Snapshot]: ...
 
     def get_snapshot(
         self, node_or_snapshot: NodeOrSnapshot, raise_if_missing: bool = False
@@ -1562,13 +1558,15 @@ class Context(BaseContext):
         If a snapshot has not been versioned yet, its view name will be returned.
         """
         return {
-            fqn: snapshot.table_name()
-            if snapshot.version
-            else snapshot.qualified_view_name.for_environment(
-                EnvironmentNamingInfo.from_environment_catalog_mapping(
-                    self.config.environment_catalog_mapping,
-                    name=c.PROD,
-                    suffix_target=self.config.environment_suffix_target,
+            fqn: (
+                snapshot.table_name()
+                if snapshot.version
+                else snapshot.qualified_view_name.for_environment(
+                    EnvironmentNamingInfo.from_environment_catalog_mapping(
+                        self.config.environment_catalog_mapping,
+                        name=c.PROD,
+                        suffix_target=self.config.environment_suffix_target,
+                    )
                 )
             )
             for fqn, snapshot in self.snapshots.items()
diff --git a/sqlmesh/core/context_diff.py b/sqlmesh/core/context_diff.py
index f19015da..7643d3dd 100644
--- a/sqlmesh/core/context_diff.py
+++ b/sqlmesh/core/context_diff.py
@@ -9,6 +9,7 @@ The SQLMesh CLI diff command uses ContextDiff to determine what to visualize.
 When creating a ContextDiff object, SQLMesh will compare the snapshots from one environment with those of
 another remote environment and determine if nodes have been added, removed, or modified.
 """
+
 from __future__ import annotations
 
 import typing as t
diff --git a/sqlmesh/core/engine_adapter/base.py b/sqlmesh/core/engine_adapter/base.py
index d33bedcc..222fe03e 100644
--- a/sqlmesh/core/engine_adapter/base.py
+++ b/sqlmesh/core/engine_adapter/base.py
@@ -6,6 +6,7 @@ generalize its functionality to different engines that have Python Database API
 connections. Rather than executing queries directly against your data stores, SQLMesh components such as
 the SnapshotEvaluator delegate them to engine adapters so these components can be engine-agnostic.
 """
+
 from __future__ import annotations
 
 import contextlib
@@ -247,14 +248,12 @@ class EngineAdapter:
     @t.overload
     def _columns_to_types(
         self, query_or_df: DF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
-    ) -> t.Dict[str, exp.DataType]:
-        ...
+    ) -> t.Dict[str, exp.DataType]: ...
 
     @t.overload
     def _columns_to_types(
         self, query_or_df: Query, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
-    ) -> t.Optional[t.Dict[str, exp.DataType]]:
-        ...
+    ) -> t.Optional[t.Dict[str, exp.DataType]]: ...
 
     def _columns_to_types(
         self, query_or_df: QueryOrDF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
@@ -343,10 +342,12 @@ class EngineAdapter:
                 ) as temp_table:
                     for source_query in source_queries:
                         source_query.add_transform(
-                            lambda node: temp_table  # type: ignore
-                            if isinstance(node, exp.Table)
-                            and quote_identifiers(node) == quote_identifiers(target_table)
-                            else node
+                            lambda node: (  # type: ignore
+                                temp_table  # type: ignore
+                                if isinstance(node, exp.Table)
+                                and quote_identifiers(node) == quote_identifiers(target_table)
+                                else node
+                            )
                         )
                     return self._insert_overwrite_by_condition(
                         target_table,
@@ -565,11 +566,13 @@ class EngineAdapter:
                 exp.ColumnDef(
                     this=exp.to_identifier(column),
                     kind=None if is_view else kind,  # don't include column data type for views
-                    constraints=self._build_col_comment_exp(column, column_descriptions)
-                    if column_descriptions
-                    and engine_supports_schema_comments
-                    and self.comments_enabled
-                    else None,
+                    constraints=(
+                        self._build_col_comment_exp(column, column_descriptions)
+                        if column_descriptions
+                        and engine_supports_schema_comments
+                        and self.comments_enabled
+                        else None
+                    ),
                 )
                 for column, kind in columns_to_types.items()
             ]
@@ -671,9 +674,11 @@ class EngineAdapter:
                 exists=exists,
                 replace=replace,
                 columns_to_types=columns_to_types,
-                table_description=table_description
-                if self.COMMENT_CREATION_TABLE.supports_schema_def and self.comments_enabled
-                else None,
+                table_description=(
+                    table_description
+                    if self.COMMENT_CREATION_TABLE.supports_schema_def and self.comments_enabled
+                    else None
+                ),
                 **kwargs,
             )
         )
@@ -845,9 +850,11 @@ class EngineAdapter:
 
         create_view_properties = self._build_view_properties_exp(
             create_kwargs.pop("table_properties", None),
-            table_description
-            if self.COMMENT_CREATION_VIEW.supports_schema_def and self.comments_enabled
-            else None,
+            (
+                table_description
+                if self.COMMENT_CREATION_VIEW.supports_schema_def and self.comments_enabled
+                else None
+            ),
         )
         if create_view_properties:
             for view_property in create_view_properties.expressions:
@@ -986,13 +993,11 @@ class EngineAdapter:
 
     @t.overload
     @classmethod
-    def _escape_json(cls, value: Query) -> Query:
-        ...
+    def _escape_json(cls, value: Query) -> Query: ...
 
     @t.overload
     @classmethod
-    def _escape_json(cls, value: str) -> str:
-        ...
+    def _escape_json(cls, value: str) -> str: ...
 
     @classmethod
     def _escape_json(cls, value: Query | str) -> Query | str:
@@ -1004,9 +1009,11 @@ class EngineAdapter:
             if isinstance(value, str):
                 return double_escape(value)
             return value.transform(
-                lambda e: exp.Literal.string(double_escape(e.name))
-                if isinstance(e, exp.Literal) and e.args["is_string"]
-                else e
+                lambda e: (
+                    exp.Literal.string(double_escape(e.name))
+                    if isinstance(e, exp.Literal) and e.args["is_string"]
+                    else e
+                )
             )
         return value
 
@@ -1129,9 +1136,11 @@ class EngineAdapter:
                             query,
                             table,
                             # Change once Databricks supports REPLACE WHERE with columns
-                            columns=list(columns_to_types)
-                            if not insert_overwrite_strategy.is_replace_where
-                            else None,
+                            columns=(
+                                list(columns_to_types)
+                                if not insert_overwrite_strategy.is_replace_where
+                                else None
+                            ),
                             overwrite=insert_overwrite_strategy.is_insert_overwrite,
                         )
                         if insert_overwrite_strategy.is_replace_where:
@@ -1147,9 +1156,11 @@ class EngineAdapter:
     ) -> None:
         if contains_json and properties:
             properties = {
-                k: self._escape_json(v)
-                if isinstance(v, (str, exp.Subqueryable, exp.DerivedTable))
-                else v
+                k: (
+                    self._escape_json(v)
+                    if isinstance(v, (str, exp.Subqueryable, exp.DerivedTable))
+                    else v
+                )
                 for k, v in properties.items()
             }
         self.execute(exp.update(table_name, properties, where=where))
@@ -1631,9 +1642,11 @@ class EngineAdapter:
             for e in ensure_list(expressions):
                 sql = t.cast(
                     str,
-                    self._to_sql(e, quote=quote_identifiers, **to_sql_kwargs)
-                    if isinstance(e, exp.Expression)
-                    else e,
+                    (
+                        self._to_sql(e, quote=quote_identifiers, **to_sql_kwargs)
+                        if isinstance(e, exp.Expression)
+                        else e
+                    ),
                 )
                 self._log_sql(sql)
                 self._execute(sql, **kwargs)
diff --git a/sqlmesh/core/engine_adapter/mssql.py b/sqlmesh/core/engine_adapter/mssql.py
index 93dad48f..875b8fa9 100644
--- a/sqlmesh/core/engine_adapter/mssql.py
+++ b/sqlmesh/core/engine_adapter/mssql.py
@@ -1,6 +1,5 @@
 """Contains MSSQLEngineAdapter."""
 
-
 from __future__ import annotations
 
 import typing as t
diff --git a/sqlmesh/core/engine_adapter/shared.py b/sqlmesh/core/engine_adapter/shared.py
index edeeac3a..1c98d40f 100644
--- a/sqlmesh/core/engine_adapter/shared.py
+++ b/sqlmesh/core/engine_adapter/shared.py
@@ -266,9 +266,11 @@ def set_catalog(override_mapping: t.Optional[t.Dict[str, CatalogSupport]] = None
 
             obj, container, key = t.cast(
                 t.Tuple[t.Union[str, exp.Table], t.Union[t.Dict, t.List], t.Union[int, str]],
-                (kwargs.get(target_name), kwargs, target_name)
-                if kwargs.get(target_name)
-                else (list_args[target_pos], list_args, target_pos),
+                (
+                    (kwargs.get(target_name), kwargs, target_name)
+                    if kwargs.get(target_name)
+                    else (list_args[target_pos], list_args, target_pos)
+                ),
             )
             to_expression_func = t.cast(
                 t.Callable[[t.Union[str, exp.Table]], exp.Table],
diff --git a/sqlmesh/core/engine_adapter/spark.py b/sqlmesh/core/engine_adapter/spark.py
index 24e2fac1..5e4232ec 100644
--- a/sqlmesh/core/engine_adapter/spark.py
+++ b/sqlmesh/core/engine_adapter/spark.py
@@ -224,14 +224,12 @@ class SparkEngineAdapter(GetCurrentCatalogFromFunctionMixin, HiveMetastoreTableP
     @t.overload
     def _columns_to_types(
         self, query_or_df: DF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
-    ) -> t.Dict[str, exp.DataType]:
-        ...
+    ) -> t.Dict[str, exp.DataType]: ...
 
     @t.overload
     def _columns_to_types(
         self, query_or_df: Query, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
-    ) -> t.Optional[t.Dict[str, exp.DataType]]:
-        ...
+    ) -> t.Optional[t.Dict[str, exp.DataType]]: ...
 
     def _columns_to_types(
         self, query_or_df: QueryOrDF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
@@ -328,9 +326,11 @@ class SparkEngineAdapter(GetCurrentCatalogFromFunctionMixin, HiveMetastoreTableP
                 catalog=self.get_current_catalog(),
                 schema=schema_name,
                 name=row["tableName"],
-                type=DataObjectType.VIEW
-                if "Type: VIEW" in row["information"]
-                else DataObjectType.TABLE,
+                type=(
+                    DataObjectType.VIEW
+                    if "Type: VIEW" in row["information"]
+                    else DataObjectType.TABLE
+                ),
             )
             for row in results  # type: ignore
         ]
diff --git a/sqlmesh/core/environment.py b/sqlmesh/core/environment.py
index 719e9bc3..1bdade63 100644
--- a/sqlmesh/core/environment.py
+++ b/sqlmesh/core/environment.py
@@ -38,13 +38,11 @@ class EnvironmentNamingInfo(PydanticModel):
 
     @t.overload
     @classmethod
-    def normalize_name(cls, v: str) -> str:
-        ...
+    def normalize_name(cls, v: str) -> str: ...
 
     @t.overload
     @classmethod
-    def normalize_name(cls, v: Environment) -> Environment:
-        ...
+    def normalize_name(cls, v: Environment) -> Environment: ...
 
     @classmethod
     def normalize_name(cls, v: str | Environment) -> str | Environment:
diff --git a/sqlmesh/core/metric/definition.py b/sqlmesh/core/metric/definition.py
index c48d621f..0c968672 100644
--- a/sqlmesh/core/metric/definition.py
+++ b/sqlmesh/core/metric/definition.py
@@ -31,9 +31,11 @@ def load_metric_ddl(
     metric = MetricMeta(
         **{
             "dialect": dialect,
-            "description": "\n".join(comment.strip() for comment in expression.comments)
-            if expression.comments
-            else None,
+            "description": (
+                "\n".join(comment.strip() for comment in expression.comments)
+                if expression.comments
+                else None
+            ),
             **{prop.name.lower(): prop.args.get("value") for prop in expression.expressions},
             **kwargs,
         }
@@ -156,9 +158,11 @@ class Metric(MetricMeta, frozen=True):
         """
         return {
             t.cast(exp.Expression, agg.parent).transform(
-                lambda node: exp.column(node.this, table=remove_namespace(node))
-                if isinstance(node, exp.Column) and node.table
-                else node
+                lambda node: (
+                    exp.column(node.this, table=remove_namespace(node))
+                    if isinstance(node, exp.Column) and node.table
+                    else node
+                )
             ): _get_measure_and_dim_tables(agg)
             for agg in self.expanded.find_all(exp.AggFunc)
         }
diff --git a/sqlmesh/core/model/decorator.py b/sqlmesh/core/model/decorator.py
index 7bf84d9c..f6ed5679 100644
--- a/sqlmesh/core/model/decorator.py
+++ b/sqlmesh/core/model/decorator.py
@@ -32,16 +32,18 @@ class model(registry_decorator):
         # Make sure that argument values are expressions in order to pass validation in ModelMeta.
         calls = self.kwargs.pop("audits", [])
         self.kwargs["audits"] = [
-            (call, {})
-            if isinstance(call, str)
-            else (
-                call[0],
-                {
-                    arg_key: exp.convert(
-                        tuple(arg_value) if isinstance(arg_value, list) else arg_value
-                    )
-                    for arg_key, arg_value in call[1].items()
-                },
+            (
+                (call, {})
+                if isinstance(call, str)
+                else (
+                    call[0],
+                    {
+                        arg_key: exp.convert(
+                            tuple(arg_value) if isinstance(arg_value, list) else arg_value
+                        )
+                        for arg_key, arg_value in call[1].items()
+                    },
+                )
             )
             for call in calls
         ]
@@ -50,9 +52,11 @@ class model(registry_decorator):
             raise ConfigError("`default_catalog` cannot be set on a per-model basis.")
 
         self.columns = {
-            column_name: column_type
-            if isinstance(column_type, exp.DataType)
-            else exp.DataType.build(str(column_type))
+            column_name: (
+                column_type
+                if isinstance(column_type, exp.DataType)
+                else exp.DataType.build(str(column_type))
+            )
             for column_name, column_type in self.kwargs.pop("columns", {}).items()
         }
 
diff --git a/sqlmesh/core/model/definition.py b/sqlmesh/core/model/definition.py
index d9fa255f..ee0d9ae8 100644
--- a/sqlmesh/core/model/definition.py
+++ b/sqlmesh/core/model/definition.py
@@ -1421,9 +1421,9 @@ def load_sql_based_model(
 
     meta_fields: t.Dict[str, t.Any] = {
         "dialect": dialect,
-        "description": "\n".join(comment.strip() for comment in meta.comments)
-        if meta.comments
-        else None,
+        "description": (
+            "\n".join(comment.strip() for comment in meta.comments) if meta.comments else None
+        ),
         **{prop.name.lower(): prop.args.get("value") for prop in meta.expressions},
         **kwargs,
     }
diff --git a/sqlmesh/core/plan/evaluator.py b/sqlmesh/core/plan/evaluator.py
index 5e185fc9..ac2a0e90 100644
--- a/sqlmesh/core/plan/evaluator.py
+++ b/sqlmesh/core/plan/evaluator.py
@@ -13,6 +13,7 @@ At a high level, when a plan is evaluated, SQLMesh will:
 
 Refer to `sqlmesh.core.plan`.
 """
+
 import abc
 import logging
 import typing as t
diff --git a/sqlmesh/core/scheduler.py b/sqlmesh/core/scheduler.py
index 36420fcf..a327fdd9 100644
--- a/sqlmesh/core/scheduler.py
+++ b/sqlmesh/core/scheduler.py
@@ -345,7 +345,7 @@ class Scheduler:
                     if len(p_intervals) > 1:
                         upstream_dependencies.append((p_sid.name, terminal_node))
                     else:
-                        for (i, interval) in enumerate(p_intervals):
+                        for i, interval in enumerate(p_intervals):
                             upstream_dependencies.append((p_sid.name, (interval, i)))
 
             for i, interval in enumerate(intervals):
diff --git a/sqlmesh/core/snapshot/definition.py b/sqlmesh/core/snapshot/definition.py
index 83c00254..dac7290b 100644
--- a/sqlmesh/core/snapshot/definition.py
+++ b/sqlmesh/core/snapshot/definition.py
@@ -1072,9 +1072,11 @@ class DeployabilityIndex(PydanticModel, frozen=True):
         # Transforming into strings because the serialization of sets of objects / lists is broken in Pydantic.
         return frozenset(
             {
-                cls._snapshot_id_key(snapshot_id)
-                if isinstance(snapshot_id, SnapshotId)
-                else snapshot_id
+                (
+                    cls._snapshot_id_key(snapshot_id)
+                    if isinstance(snapshot_id, SnapshotId)
+                    else snapshot_id
+                )
                 for snapshot_id in v
             }
         )
@@ -1241,9 +1243,11 @@ def display_name(
         return snapshot_info_like.name
     view_name = exp.to_table(snapshot_info_like.name)
     qvn = QualifiedViewName(
-        catalog=view_name.catalog
-        if view_name.catalog and view_name.catalog != default_catalog
-        else None,
+        catalog=(
+            view_name.catalog
+            if view_name.catalog and view_name.catalog != default_catalog
+            else None
+        ),
         schema_name=view_name.db or None,
         table=view_name.name,
     )
diff --git a/sqlmesh/core/snapshot/evaluator.py b/sqlmesh/core/snapshot/evaluator.py
index 47dd87bb..3fa00c8a 100644
--- a/sqlmesh/core/snapshot/evaluator.py
+++ b/sqlmesh/core/snapshot/evaluator.py
@@ -19,6 +19,7 @@ has been evaluated to check for data quality issues.
 
 For more information about audits, see `sqlmesh.core.audit`.
 """
+
 from __future__ import annotations
 
 import abc
@@ -545,9 +546,11 @@ class SnapshotEvaluator:
                 and snapshot.is_incremental_by_time_range
             ):
                 query_or_df = reduce(
-                    lambda a, b: pd.concat([a, b], ignore_index=True)  # type: ignore
-                    if self.adapter.is_pandas_df(a)
-                    else a.union_all(b),  # type: ignore
+                    lambda a, b: (
+                        pd.concat([a, b], ignore_index=True)  # type: ignore
+                        if self.adapter.is_pandas_df(a)
+                        else a.union_all(b)  # type: ignore
+                    ),  # type: ignore
                     queries_or_dfs,
                 )
                 apply(query_or_df, index=0)
diff --git a/sqlmesh/core/state_sync/__init__.py b/sqlmesh/core/state_sync/__init__.py
index 5c64f4f5..8662a1dd 100644
--- a/sqlmesh/core/state_sync/__init__.py
+++ b/sqlmesh/core/state_sync/__init__.py
@@ -13,6 +13,7 @@ implies, it only allows for read-only operations on snapshots and environment st
 The provided `sqlmesh.core.state_sync.EngineAdapterStateSync` leverages an existing engine
 adapter to read and write state to the underlying data store.
 """
+
 from sqlmesh.core.state_sync.base import StateReader, StateSync, Versions
 from sqlmesh.core.state_sync.cache import CachingStateSync
 from sqlmesh.core.state_sync.common import CommonStateSyncMixin, cleanup_expired_views
diff --git a/sqlmesh/core/state_sync/common.py b/sqlmesh/core/state_sync/common.py
index d2419e03..a440561b 100644
--- a/sqlmesh/core/state_sync/common.py
+++ b/sqlmesh/core/state_sync/common.py
@@ -182,9 +182,9 @@ class CommonStateSyncMixin(StateSync):
         return PromotionResult(
             added=sorted(added_table_infos),
             removed=list(removed),
-            removed_environment_naming_info=existing_environment.naming_info
-            if removed and existing_environment
-            else None,
+            removed_environment_naming_info=(
+                existing_environment.naming_info if removed and existing_environment else None
+            ),
         )
 
     @transactional()
diff --git a/sqlmesh/core/state_sync/engine_adapter.py b/sqlmesh/core/state_sync/engine_adapter.py
index 0f6a36b0..d83f74e8 100644
--- a/sqlmesh/core/state_sync/engine_adapter.py
+++ b/sqlmesh/core/state_sync/engine_adapter.py
@@ -13,6 +13,7 @@ implies, it only allows for read-only operations on snapshots and environment st
 The provided `sqlmesh.core.state_sync.EngineAdapterStateSync` leverages an existing engine
 adapter to read and write state to the underlying data store.
 """
+
 from __future__ import annotations
 
 import contextlib
@@ -1080,9 +1081,11 @@ class EngineAdapterStateSync(CommonStateSyncMixin, StateSync):
         updated_environments = []
         for environment in environments:
             snapshots = [
-                new_snapshots[snapshot_id_mapping[info.snapshot_id]].table_info
-                if info.snapshot_id in snapshot_id_mapping
-                else info
+                (
+                    new_snapshots[snapshot_id_mapping[info.snapshot_id]].table_info
+                    if info.snapshot_id in snapshot_id_mapping
+                    else info
+                )
                 for info in environment.snapshots
             ]
 
@@ -1244,11 +1247,11 @@ def _environment_to_df(environment: Environment) -> pd.DataFrame:
                 "previous_plan_id": environment.previous_plan_id,
                 "expiration_ts": environment.expiration_ts,
                 "finalized_ts": environment.finalized_ts,
-                "promoted_snapshot_ids": json.dumps(
-                    [s.dict() for s in environment.promoted_snapshot_ids]
-                )
-                if environment.promoted_snapshot_ids is not None
-                else None,
+                "promoted_snapshot_ids": (
+                    json.dumps([s.dict() for s in environment.promoted_snapshot_ids])
+                    if environment.promoted_snapshot_ids is not None
+                    else None
+                ),
                 "suffix_target": environment.suffix_target.value,
                 "catalog_name_override": environment.catalog_name_override,
             }
diff --git a/sqlmesh/core/table_diff.py b/sqlmesh/core/table_diff.py
index bda05056..092b1ebf 100644
--- a/sqlmesh/core/table_diff.py
+++ b/sqlmesh/core/table_diff.py
@@ -299,19 +299,23 @@ class TableDiff:
                 }
                 if self.source != self.source_alias and self.target != self.target_alias:
                     joined_renamed_cols = {
-                        c: n.replace(
-                            "s__", f"{self.source_alias.upper() if self.source_alias else ''}__"
+                        c: (
+                            n.replace(
+                                "s__", f"{self.source_alias.upper() if self.source_alias else ''}__"
+                            )
+                            if n.startswith("s__")
+                            else n
                         )
-                        if n.startswith("s__")
-                        else n
                         for c, n in joined_renamed_cols.items()
                     }
                     joined_renamed_cols = {
-                        c: n.replace(
-                            "t__", f"{self.target_alias.upper() if self.target_alias else ''}__"
+                        c: (
+                            n.replace(
+                                "t__", f"{self.target_alias.upper() if self.target_alias else ''}__"
+                            )
+                            if n.startswith("t__")
+                            else n
                         )
-                        if n.startswith("t__")
-                        else n
                         for c, n in joined_renamed_cols.items()
                     }
                 joined_sample = sample[sample["rows_joined"] == 1][joined_sample_cols]
diff --git a/sqlmesh/dbt/adapter.py b/sqlmesh/dbt/adapter.py
index 916a1d0a..1e070388 100644
--- a/sqlmesh/dbt/adapter.py
+++ b/sqlmesh/dbt/adapter.py
@@ -233,9 +233,11 @@ class RuntimeAdapter(BaseAdapter):
                 identifier=do.name,
                 quote_policy=self.quote_policy,
                 # DBT relation types aren't snake case and instead just one word without spaces so we remove underscores
-                type=RelationType.External
-                if do.type.is_unknown
-                else RelationType(do.type.lower().replace("_", "")),
+                type=(
+                    RelationType.External
+                    if do.type.is_unknown
+                    else RelationType(do.type.lower().replace("_", ""))
+                ),
             )
             for do in self.engine_adapter.get_data_objects(schema)
         ]
diff --git a/sqlmesh/dbt/model.py b/sqlmesh/dbt/model.py
index 1e1bec2a..4b95e857 100644
--- a/sqlmesh/dbt/model.py
+++ b/sqlmesh/dbt/model.py
@@ -232,9 +232,11 @@ class ModelConfig(BaseModelConfig):
                 valid_to_name="dbt_valid_to",
                 updated_at_name=self.updated_at,
                 updated_at_as_valid_from=True,
-                time_data_type=exp.DataType.build("TIMESTAMPTZ")
-                if target.type == "bigquery"
-                else exp.DataType.build("TIMESTAMP"),
+                time_data_type=(
+                    exp.DataType.build("TIMESTAMPTZ")
+                    if target.type == "bigquery"
+                    else exp.DataType.build("TIMESTAMP")
+                ),
             )
         raise ConfigError(f"{materialization.value} materialization not supported.")
 
diff --git a/sqlmesh/migrations/v0001_init.py b/sqlmesh/migrations/v0001_init.py
index f25a4384..778c36bc 100644
--- a/sqlmesh/migrations/v0001_init.py
+++ b/sqlmesh/migrations/v0001_init.py
@@ -3,6 +3,7 @@
 If a migration alters the payload of any pydantic models, you should not actually use them because
 the running model may not be able to load them. Make sure that these migration files are standalone.
 """
+
 from sqlglot import exp
 
 from sqlmesh.utils.migration import index_text_type
diff --git a/sqlmesh/migrations/v0003_move_batch_size.py b/sqlmesh/migrations/v0003_move_batch_size.py
index 81fdc9aa..9d6d7ce4 100644
--- a/sqlmesh/migrations/v0003_move_batch_size.py
+++ b/sqlmesh/migrations/v0003_move_batch_size.py
@@ -1,4 +1,5 @@
 """Move batch_size from the model and into the kind."""
+
 import json
 
 from sqlglot import exp
diff --git a/sqlmesh/migrations/v0004_environmnent_add_finalized_at.py b/sqlmesh/migrations/v0004_environmnent_add_finalized_at.py
index 0b74c65c..ad228abb 100644
--- a/sqlmesh/migrations/v0004_environmnent_add_finalized_at.py
+++ b/sqlmesh/migrations/v0004_environmnent_add_finalized_at.py
@@ -1,4 +1,5 @@
 """Add support for environment finalization."""
+
 from sqlglot import exp
 
 
diff --git a/sqlmesh/migrations/v0005_create_seed_table.py b/sqlmesh/migrations/v0005_create_seed_table.py
index a57b1678..1e1e7dc3 100644
--- a/sqlmesh/migrations/v0005_create_seed_table.py
+++ b/sqlmesh/migrations/v0005_create_seed_table.py
@@ -1,4 +1,5 @@
 """Create a dedicated table to store the content of seeds."""
+
 from sqlglot import exp
 
 from sqlmesh.utils.migration import index_text_type
diff --git a/sqlmesh/migrations/v0007_env_table_info_to_kind.py b/sqlmesh/migrations/v0007_env_table_info_to_kind.py
index 107a61aa..6e0ac94a 100644
--- a/sqlmesh/migrations/v0007_env_table_info_to_kind.py
+++ b/sqlmesh/migrations/v0007_env_table_info_to_kind.py
@@ -1,4 +1,5 @@
 """Change environments because snapshot table info now stores model kind name."""
+
 import json
 import zlib
 
diff --git a/sqlmesh/migrations/v0008_create_intervals_table.py b/sqlmesh/migrations/v0008_create_intervals_table.py
index 44150242..0746febc 100644
--- a/sqlmesh/migrations/v0008_create_intervals_table.py
+++ b/sqlmesh/migrations/v0008_create_intervals_table.py
@@ -1,4 +1,5 @@
 """Create a dedicated table to store snapshot intervals."""
+
 from sqlglot import exp
 
 from sqlmesh.utils.migration import index_text_type
diff --git a/sqlmesh/migrations/v0009_remove_pre_post_hooks.py b/sqlmesh/migrations/v0009_remove_pre_post_hooks.py
index 5ea4c4bc..79aab446 100644
--- a/sqlmesh/migrations/v0009_remove_pre_post_hooks.py
+++ b/sqlmesh/migrations/v0009_remove_pre_post_hooks.py
@@ -1,4 +1,5 @@
 """Remove pre- / post- hooks from existing snapshots."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0011_add_model_kind_name.py b/sqlmesh/migrations/v0011_add_model_kind_name.py
index 64b33aef..d7fa976e 100644
--- a/sqlmesh/migrations/v0011_add_model_kind_name.py
+++ b/sqlmesh/migrations/v0011_add_model_kind_name.py
@@ -1,4 +1,5 @@
 """Add the kind_name column to the snapshots table."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0012_update_jinja_expressions.py b/sqlmesh/migrations/v0012_update_jinja_expressions.py
index 56babdd8..14e04d6a 100644
--- a/sqlmesh/migrations/v0012_update_jinja_expressions.py
+++ b/sqlmesh/migrations/v0012_update_jinja_expressions.py
@@ -1,4 +1,5 @@
 """Fix expressions that contain jinja."""
+
 import json
 import typing as t
 
diff --git a/sqlmesh/migrations/v0013_serde_using_model_dialects.py b/sqlmesh/migrations/v0013_serde_using_model_dialects.py
index 42f4dbf9..26f9c1e7 100644
--- a/sqlmesh/migrations/v0013_serde_using_model_dialects.py
+++ b/sqlmesh/migrations/v0013_serde_using_model_dialects.py
@@ -1,4 +1,5 @@
 """Serialize SQL using the dialect of each model."""
+
 import json
 import typing as t
 
diff --git a/sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py b/sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py
index a41e8a90..d8d963cb 100644
--- a/sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py
+++ b/sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py
@@ -1,4 +1,5 @@
 """Include a set of snapshot IDs filtered for promotion."""
+
 from sqlglot import exp
 
 
diff --git a/sqlmesh/migrations/v0016_fix_windows_path.py b/sqlmesh/migrations/v0016_fix_windows_path.py
index 50a93520..01e06e1f 100644
--- a/sqlmesh/migrations/v0016_fix_windows_path.py
+++ b/sqlmesh/migrations/v0016_fix_windows_path.py
@@ -1,4 +1,5 @@
 """Fix paths that have a Windows forward slash in them."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0017_fix_windows_seed_path.py b/sqlmesh/migrations/v0017_fix_windows_seed_path.py
index 011390e1..44675aa6 100644
--- a/sqlmesh/migrations/v0017_fix_windows_seed_path.py
+++ b/sqlmesh/migrations/v0017_fix_windows_seed_path.py
@@ -1,4 +1,5 @@
 """Fix seed paths that have a Windows forward slash in them."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py b/sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py
index b5ec32e9..c3119841 100644
--- a/sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py
+++ b/sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py
@@ -1,4 +1,5 @@
 """Replace snapshot model field with node."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0019_add_env_suffix_target.py b/sqlmesh/migrations/v0019_add_env_suffix_target.py
index d3008a8d..b3007b45 100644
--- a/sqlmesh/migrations/v0019_add_env_suffix_target.py
+++ b/sqlmesh/migrations/v0019_add_env_suffix_target.py
@@ -1,4 +1,5 @@
 """Add support for environment suffix target."""
+
 from sqlglot import exp
 
 
diff --git a/sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py b/sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py
index b99eaa26..938e7d42 100644
--- a/sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py
+++ b/sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py
@@ -1,4 +1,5 @@
 """Remove redundant attributes from dbt models."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0021_fix_table_properties.py b/sqlmesh/migrations/v0021_fix_table_properties.py
index 068d06cb..a7d6b8b2 100644
--- a/sqlmesh/migrations/v0021_fix_table_properties.py
+++ b/sqlmesh/migrations/v0021_fix_table_properties.py
@@ -1,4 +1,5 @@
 """Fix table properties that have extra quoting due to a bug."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0022_move_project_to_model.py b/sqlmesh/migrations/v0022_move_project_to_model.py
index 59f5c510..b50d870a 100644
--- a/sqlmesh/migrations/v0022_move_project_to_model.py
+++ b/sqlmesh/migrations/v0022_move_project_to_model.py
@@ -1,4 +1,5 @@
 """Move project attr from snapshot to model."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py b/sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py
index 03dbe4f1..6ae64955 100644
--- a/sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py
+++ b/sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py
@@ -1,4 +1,5 @@
 """Fix snapshots of added models with forward only parents."""
+
 import json
 import typing as t
 
diff --git a/sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py b/sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py
index 00d7f880..81864bbf 100644
--- a/sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py
+++ b/sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py
@@ -1,4 +1,5 @@
 """Replace snapshot model_kind_name enum with value."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py b/sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py
index 9adc5fbf..694cbd10 100644
--- a/sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py
+++ b/sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py
@@ -1,4 +1,5 @@
 """Normalize intervals and fix missing change category."""
+
 import json
 import zlib
 
diff --git a/sqlmesh/migrations/v0026_remove_dialect_from_seed.py b/sqlmesh/migrations/v0026_remove_dialect_from_seed.py
index aba687ca..957cde9a 100644
--- a/sqlmesh/migrations/v0026_remove_dialect_from_seed.py
+++ b/sqlmesh/migrations/v0026_remove_dialect_from_seed.py
@@ -1,4 +1,5 @@
 """Remove dialect from seeds."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0027_minute_interval_to_five.py b/sqlmesh/migrations/v0027_minute_interval_to_five.py
index 36f5383f..ce59066a 100644
--- a/sqlmesh/migrations/v0027_minute_interval_to_five.py
+++ b/sqlmesh/migrations/v0027_minute_interval_to_five.py
@@ -1,4 +1,5 @@
 """Change any interval unit of minute to five_minute."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0028_add_plan_dags_table.py b/sqlmesh/migrations/v0028_add_plan_dags_table.py
index 46372429..d8e67f60 100644
--- a/sqlmesh/migrations/v0028_add_plan_dags_table.py
+++ b/sqlmesh/migrations/v0028_add_plan_dags_table.py
@@ -1,4 +1,5 @@
 """Creates the '_plan_dags' table if Airflow is used."""
+
 from sqlglot import exp
 
 from sqlmesh.utils.migration import index_text_type
diff --git a/sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py b/sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py
index 7be9eac0..39fd527f 100644
--- a/sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py
+++ b/sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py
@@ -1,4 +1,5 @@
 """Generate mapping schema data types using the corresponding model's dialect."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0030_update_unrestorable_snapshots.py b/sqlmesh/migrations/v0030_update_unrestorable_snapshots.py
index 066b0091..ca5ca247 100644
--- a/sqlmesh/migrations/v0030_update_unrestorable_snapshots.py
+++ b/sqlmesh/migrations/v0030_update_unrestorable_snapshots.py
@@ -1,4 +1,5 @@
 """Update unrestorable snapshots."""
+
 import json
 import typing as t
 from collections import defaultdict
diff --git a/sqlmesh/migrations/v0031_remove_dbt_target_fields.py b/sqlmesh/migrations/v0031_remove_dbt_target_fields.py
index 90d0ab77..8ac6c949 100644
--- a/sqlmesh/migrations/v0031_remove_dbt_target_fields.py
+++ b/sqlmesh/migrations/v0031_remove_dbt_target_fields.py
@@ -1,4 +1,5 @@
 """Remove dbt target fields from snapshots outside of limited list of approved fields"""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0032_add_sqlmesh_version.py b/sqlmesh/migrations/v0032_add_sqlmesh_version.py
index 58588ed4..0b17f4a0 100644
--- a/sqlmesh/migrations/v0032_add_sqlmesh_version.py
+++ b/sqlmesh/migrations/v0032_add_sqlmesh_version.py
@@ -1,4 +1,5 @@
 """Add new 'sqlmesh_version' column to the version state table."""
+
 from sqlglot import exp
 
 
diff --git a/sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py b/sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py
index 574ecf60..5660240d 100644
--- a/sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py
+++ b/sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py
@@ -1,4 +1,5 @@
 """Use LONGTEXT type for blob fields in MySQL."""
+
 from sqlglot import exp
 
 
diff --git a/sqlmesh/migrations/v0034_add_default_catalog.py b/sqlmesh/migrations/v0034_add_default_catalog.py
index 32c51269..40be1b59 100644
--- a/sqlmesh/migrations/v0034_add_default_catalog.py
+++ b/sqlmesh/migrations/v0034_add_default_catalog.py
@@ -41,9 +41,9 @@ def normalize_mapping_schema(mapping_schema: t.Dict, dialect: str) -> t.Dict:
     normalized_mapping_schema = {}
     for key, value in mapping_schema.items():
         if isinstance(value, dict):
-            normalized_mapping_schema[
-                normalize_model_name(key, None, dialect)
-            ] = normalize_mapping_schema(value, dialect)
+            normalized_mapping_schema[normalize_model_name(key, None, dialect)] = (
+                normalize_mapping_schema(value, dialect)
+            )
         else:
             normalized_mapping_schema[key] = value
     return normalized_mapping_schema
@@ -331,7 +331,11 @@ def migrate(state_sync, default_catalog: t.Optional[str], **kwargs):  # type: ig
         )
 
     new_seeds = []
-    for (name, identifier, content,) in engine_adapter.fetchall(
+    for (
+        name,
+        identifier,
+        content,
+    ) in engine_adapter.fetchall(
         exp.select(
             "name",
             "identifier",
diff --git a/sqlmesh/migrations/v0035_add_catalog_name_override.py b/sqlmesh/migrations/v0035_add_catalog_name_override.py
index e6725fbb..34cfc8f3 100644
--- a/sqlmesh/migrations/v0035_add_catalog_name_override.py
+++ b/sqlmesh/migrations/v0035_add_catalog_name_override.py
@@ -1,4 +1,5 @@
 """Add support for environment catalog name override."""
+
 from sqlglot import exp
 
 
diff --git a/sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py b/sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py
index 18489fce..e65bdf22 100644
--- a/sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py
+++ b/sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py
@@ -1,4 +1,5 @@
 """Remove dbt is_incremental macro"""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py b/sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py
index 636eab99..047bca59 100644
--- a/sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py
+++ b/sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py
@@ -1,4 +1,5 @@
 """Add the expiration_ts column to the snapshots table."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py b/sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py
index a1c3c69a..db8f6cc0 100644
--- a/sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py
+++ b/sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py
@@ -1,4 +1,5 @@
 """Include environment in plan dag spec."""
+
 import json
 
 import pandas as pd
diff --git a/sqlmesh/schedulers/airflow/dag_generator.py b/sqlmesh/schedulers/airflow/dag_generator.py
index 4fe7ccc8..e9b4e393 100644
--- a/sqlmesh/schedulers/airflow/dag_generator.py
+++ b/sqlmesh/schedulers/airflow/dag_generator.py
@@ -316,9 +316,9 @@ class SnapshotDagGenerator:
             python_callable=promotion_update_state_task,
             op_kwargs={
                 "environment": environment,
-                "no_gaps_snapshot_names": request.no_gaps_snapshot_names
-                if request.no_gaps
-                else set(),
+                "no_gaps_snapshot_names": (
+                    request.no_gaps_snapshot_names if request.no_gaps else set()
+                ),
             },
         )
 
diff --git a/sqlmesh/schedulers/airflow/operators/spark_submit.py b/sqlmesh/schedulers/airflow/operators/spark_submit.py
index 7a0664fd..c8e54031 100644
--- a/sqlmesh/schedulers/airflow/operators/spark_submit.py
+++ b/sqlmesh/schedulers/airflow/operators/spark_submit.py
@@ -139,9 +139,9 @@ class SQLMeshSparkSubmitOperator(BaseOperator):
             "default_catalog": self._target.default_catalog,
             "command_type": command_type.value if command_type else None,
             "ddl_concurrent_tasks": ddl_concurrent_tasks,
-            "payload_path": command_payload_file_path.split("/")[-1]
-            if command_payload_file_path
-            else None,
+            "payload_path": (
+                command_payload_file_path.split("/")[-1] if command_payload_file_path else None
+            ),
         }
         return SparkSubmitHook(
             conf=spark_conf,
diff --git a/sqlmesh/schedulers/airflow/operators/targets.py b/sqlmesh/schedulers/airflow/operators/targets.py
index 2f25c743..031d5b1a 100644
--- a/sqlmesh/schedulers/airflow/operators/targets.py
+++ b/sqlmesh/schedulers/airflow/operators/targets.py
@@ -119,9 +119,9 @@ class SnapshotEvaluationTarget(BaseTarget[commands.EvaluateCommandPayload], Pyda
     """
 
     command_type: commands.CommandType = commands.CommandType.EVALUATE
-    command_handler: t.Callable[
-        [SnapshotEvaluator, commands.EvaluateCommandPayload], None
-    ] = commands.evaluate
+    command_handler: t.Callable[[SnapshotEvaluator, commands.EvaluateCommandPayload], None] = (
+        commands.evaluate
+    )
     ddl_concurrent_tasks: int = 1
 
     snapshot: Snapshot
@@ -196,9 +196,9 @@ class SnapshotPromotionTarget(BaseTarget[commands.PromoteCommandPayload], Pydant
     """
 
     command_type: commands.CommandType = commands.CommandType.PROMOTE
-    command_handler: t.Callable[
-        [SnapshotEvaluator, commands.PromoteCommandPayload], None
-    ] = commands.promote
+    command_handler: t.Callable[[SnapshotEvaluator, commands.PromoteCommandPayload], None] = (
+        commands.promote
+    )
 
     snapshots: t.List[Snapshot]
     environment_naming_info: EnvironmentNamingInfo
@@ -224,9 +224,9 @@ class SnapshotDemotionTarget(BaseTarget[commands.DemoteCommandPayload], Pydantic
     """
 
     command_type: commands.CommandType = commands.CommandType.DEMOTE
-    command_handler: t.Callable[
-        [SnapshotEvaluator, commands.DemoteCommandPayload], None
-    ] = commands.demote
+    command_handler: t.Callable[[SnapshotEvaluator, commands.DemoteCommandPayload], None] = (
+        commands.demote
+    )
 
     snapshots: t.List[SnapshotTableInfo]
     environment_naming_info: EnvironmentNamingInfo
@@ -243,9 +243,9 @@ class SnapshotCleanupTarget(BaseTarget[commands.CleanupCommandPayload], Pydantic
     """The target which contains attributes necessary to perform table cleanup of expired snapshots"""
 
     command_type: commands.CommandType = commands.CommandType.CLEANUP
-    command_handler: t.Callable[
-        [SnapshotEvaluator, commands.CleanupCommandPayload], None
-    ] = commands.cleanup
+    command_handler: t.Callable[[SnapshotEvaluator, commands.CleanupCommandPayload], None] = (
+        commands.cleanup
+    )
     ddl_concurrent_tasks: int = 1
 
     @provide_session
@@ -275,9 +275,9 @@ class SnapshotCreateTablesTarget(BaseTarget[commands.CreateTablesCommandPayload]
     """The target which creates physical tables for the given set of new snapshots."""
 
     command_type: commands.CommandType = commands.CommandType.CREATE_TABLES
-    command_handler: t.Callable[
-        [SnapshotEvaluator, commands.CreateTablesCommandPayload], None
-    ] = commands.create_tables
+    command_handler: t.Callable[[SnapshotEvaluator, commands.CreateTablesCommandPayload], None] = (
+        commands.create_tables
+    )
 
     new_snapshots: t.List[Snapshot]
     ddl_concurrent_tasks: int
@@ -302,9 +302,9 @@ class SnapshotMigrateTablesTarget(BaseTarget[commands.MigrateTablesCommandPayloa
     """
 
     command_type: commands.CommandType = commands.CommandType.MIGRATE_TABLES
-    command_handler: t.Callable[
-        [SnapshotEvaluator, commands.MigrateTablesCommandPayload], None
-    ] = commands.migrate_tables
+    command_handler: t.Callable[[SnapshotEvaluator, commands.MigrateTablesCommandPayload], None] = (
+        commands.migrate_tables
+    )
 
     snapshots: t.List[Snapshot]
     ddl_concurrent_tasks: int
diff --git a/sqlmesh/schedulers/airflow/plan.py b/sqlmesh/schedulers/airflow/plan.py
index 5d5a01d1..bea326e5 100644
--- a/sqlmesh/schedulers/airflow/plan.py
+++ b/sqlmesh/schedulers/airflow/plan.py
@@ -147,9 +147,7 @@ def create_plan_dag_spec(
             if deployability_index_for_creation.is_representative(s)
         }
         if request.no_gaps and not request.is_dev
-        else None
-        if request.no_gaps
-        else set()
+        else None if request.no_gaps else set()
     )
 
     return common.PlanDagSpec(
diff --git a/sqlmesh/utils/dag.py b/sqlmesh/utils/dag.py
index 3c04ee81..69c45855 100644
--- a/sqlmesh/utils/dag.py
+++ b/sqlmesh/utils/dag.py
@@ -5,6 +5,7 @@ A DAG, or directed acyclic graph, is a graph where the edges are directional and
 all the edges pointing in the same direction. SQLMesh uses a DAG to keep track of a project's models. This
 allows SQLMesh to easily determine a model's lineage and to identify upstream and downstream dependencies.
 """
+
 from __future__ import annotations
 
 import typing as t
diff --git a/sqlmesh/utils/yaml.py b/sqlmesh/utils/yaml.py
index 425ce7ec..21dc6c0a 100644
--- a/sqlmesh/utils/yaml.py
+++ b/sqlmesh/utils/yaml.py
@@ -47,13 +47,11 @@ def load(
 
 
 @t.overload
-def dump(value: t.Any, stream: io.IOBase) -> None:
-    ...
+def dump(value: t.Any, stream: io.IOBase) -> None: ...
 
 
 @t.overload
-def dump(value: t.Any) -> str:
-    ...
+def dump(value: t.Any) -> str: ...
 
 
 def dump(value: t.Any, stream: t.Optional[io.IOBase] = None) -> t.Optional[str]:
diff --git a/web/client/src/App.tsx b/web/client/src/App.tsx
index 7fed0076..5c620f5c 100644
--- a/web/client/src/App.tsx
+++ b/web/client/src/App.tsx
@@ -6,18 +6,47 @@ import Footer from './library/pages/root/Footer'
 import { getBrowserRouter } from './routes'
 import Loading from '@components/loading/Loading'
 import Spinner from '@components/logo/Spinner'
-import { useApiMeta } from './api'
+import { useApiEnvironments, useApiMeta, useApiPlanRun } from './api'
 import { useStoreContext } from '@context/context'
 import { useStorePlan } from '@context/plan'
 import { EnumPlanAction, ModelPlanAction } from '@models/plan-action'
-import { isNil, isTrue } from './utils'
+import { isNil, isObjectNotEmpty, isTrue } from './utils'
+import { useChannelEvents } from '@api/channels'
+import { type PlanOverviewTracker } from '@models/tracker-plan-overview'
+import {
+  type Environments,
+  type EnvironmentsEnvironments,
+  Status,
+} from '@api/client'
+import { type PlanApplyTracker } from '@models/tracker-plan-apply'
+import { type PlanCancelTracker } from '@models/tracker-plan-cancel'
 
 export default function App(): JSX.Element {
+  const environment = useStoreContext(s => s.environment)
   const version = useStoreContext(s => s.version)
   const modules = useStoreContext(s => s.modules)
   const setVersion = useStoreContext(s => s.setVersion)
-  const setPlanAction = useStorePlan(s => s.setPlanAction)
   const setModules = useStoreContext(s => s.setModules)
+  const addRemoteEnvironments = useStoreContext(s => s.addRemoteEnvironments)
+
+  const planOverview = useStorePlan(s => s.planOverview)
+  const planApply = useStorePlan(s => s.planApply)
+  const planCancel = useStorePlan(s => s.planCancel)
+  const setPlanOverview = useStorePlan(s => s.setPlanOverview)
+  const setPlanApply = useStorePlan(s => s.setPlanApply)
+  const setPlanCancel = useStorePlan(s => s.setPlanCancel)
+  const setPlanAction = useStorePlan(s => s.setPlanAction)
+
+  const { refetch: getEnvironments, cancel: cancelRequestEnvironments } =
+    useApiEnvironments()
+  const { refetch: planRun } = useApiPlanRun(environment.name, {
+    planOptions: {
+      skip_tests: true,
+      include_unmodified: true,
+    },
+  })
+
+  const channel = useChannelEvents()
 
   const {
     refetch: getMeta,
@@ -26,6 +55,20 @@ export default function App(): JSX.Element {
   } = useApiMeta()
 
   useEffect(() => {
+    const channelPlanOverview = channel<any>(
+      'plan-overview',
+      updatePlanOverviewTracker,
+    )
+    const channelPlanApply = channel<any>('plan-apply', updatePlanApplyTracker)
+    const channelPlanCancel = channel<any>(
+      'plan-cancel',
+      updatePlanCancelTracker,
+    )
+
+    channelPlanOverview.subscribe()
+    channelPlanApply.subscribe()
+    channelPlanCancel.subscribe()
+
     void getMeta().then(({ data }) => {
       setVersion(data?.version)
       setModules(Array.from(new Set(modules.concat(data?.modules ?? []))))
@@ -37,11 +80,64 @@ export default function App(): JSX.Element {
       }
     })
 
+    void getEnvironments().then(({ data }) => updateEnviroments(data))
+
     return () => {
+      channelPlanOverview.unsubscribe()
+      channelPlanApply.unsubscribe()
+      channelPlanCancel.unsubscribe()
+
       void cancelRequestMeta()
+      void cancelRequestEnvironments()
     }
   }, [])
 
+  function updatePlanOverviewTracker(data: PlanOverviewTracker): void {
+    planOverview.update(data)
+
+    setPlanOverview(planOverview)
+  }
+
+  function updatePlanCancelTracker(data: PlanCancelTracker): void {
+    planCancel.update(data)
+
+    setPlanCancel(planCancel)
+  }
+
+  function updatePlanApplyTracker(data: PlanApplyTracker): void {
+    planApply.update(data, planOverview)
+
+    const isFinished =
+      isTrue(data.meta?.done) && data.meta?.status !== Status.init
+
+    if (isFinished) {
+      void getEnvironments().then(({ data }) => {
+        planCancel.reset()
+
+        updateEnviroments(data)
+
+        void planRun()
+
+        setPlanCancel(planCancel)
+      })
+    }
+
+    setPlanApply(planApply)
+  }
+
+  function updateEnviroments(data: Optional<Environments>): void {
+    const { environments, default_target_environment, pinned_environments } =
+      data ?? {}
+
+    if (isObjectNotEmpty<EnvironmentsEnvironments>(environments)) {
+      addRemoteEnvironments(
+        Object.values(environments),
+        default_target_environment,
+        pinned_environments,
+      )
+    }
+  }
+
   const router = getBrowserRouter(modules)
   const isLoadingMeta = isNil(version) || isFetchingMeta
 
diff --git a/web/client/src/library/components/documentation/Documentation.tsx b/web/client/src/library/components/documentation/Documentation.tsx
index 38e18e6f..de2614fe 100644
--- a/web/client/src/library/components/documentation/Documentation.tsx
+++ b/web/client/src/library/components/documentation/Documentation.tsx
@@ -11,9 +11,10 @@ import {
   truncate,
 } from '@utils/index'
 import clsx from 'clsx'
-import { type ModelSQLMeshModel } from '@models/sqlmesh-model'
+import { ModelSQLMeshModel } from '@models/sqlmesh-model'
 import { ModelColumns } from '@components/graph/Graph'
 import { useApiModel } from '@api/index'
+import { useStoreContext } from '@context/context'
 
 const Documentation = function Documentation({
   model,
@@ -30,6 +31,8 @@ const Documentation = function Documentation({
   withDescription?: boolean
   withColumns?: boolean
 }): JSX.Element {
+  const setLastSelectedModel = useStoreContext(s => s.setLastSelectedModel)
+
   const { refetch: getModel, cancel: cancelRequestModel } = useApiModel(
     model.name,
   )
@@ -37,6 +40,8 @@ const Documentation = function Documentation({
   useEffect(() => {
     void getModel().then(({ data }) => {
       model.update(data)
+
+      setLastSelectedModel(new ModelSQLMeshModel(model))
     })
 
     return () => {
@@ -100,7 +105,7 @@ const Documentation = function Documentation({
             className="max-h-[15rem]"
             nodeId={model.name}
             columns={model.columns}
-            disabled={model?.isModelPython}
+            disabled={model.isModelPython}
             withHandles={false}
             withSource={false}
             withDescription={true}
diff --git a/web/client/src/library/components/editor/EditorInspector.tsx b/web/client/src/library/components/editor/EditorInspector.tsx
index 09caae0f..2fdb7a00 100644
--- a/web/client/src/library/components/editor/EditorInspector.tsx
+++ b/web/client/src/library/components/editor/EditorInspector.tsx
@@ -22,7 +22,6 @@ import { type ModelSQLMeshModel } from '@models/sqlmesh-model'
 import {
   useApiEvaluate,
   useApiFetchdf,
-  useApiModel,
   useApiRender,
   useApiTableDiff,
 } from '@api/index'
@@ -92,20 +91,6 @@ function InspectorModel({
     .filter(({ isRemote }) => isRemote)
     .map(({ name }) => ({ text: name, value: name }))
 
-  const { refetch: getModel, cancel: cancelRequestModel } = useApiModel(
-    model.name,
-  )
-
-  useEffect(() => {
-    void getModel().then(({ data }) => {
-      model.update(data)
-    })
-
-    return () => {
-      cancelRequestModel()
-    }
-  }, [model.name])
-
   return (
     <Tab.Group>
       <div className="flex w-full items-center">
diff --git a/web/client/src/library/components/plan/Plan.tsx b/web/client/src/library/components/plan/Plan.tsx
index 1ea5da8f..f52e5d3b 100644
--- a/web/client/src/library/components/plan/Plan.tsx
+++ b/web/client/src/library/components/plan/Plan.tsx
@@ -1,4 +1,4 @@
-import { isFalse, isNil, isTrue } from '~/utils'
+import { isNil, isTrue } from '~/utils'
 import { useStorePlan } from '~/context/plan'
 import { useApiPlanRun, useApiPlanApply, useApiCancelPlan } from '~/api'
 import PlanHeader from './PlanHeader'
@@ -6,16 +6,11 @@ import PlanActions from './PlanActions'
 import PlanOptions from './PlanOptions'
 import { EnumPlanActions, usePlanDispatch } from './context'
 import { useApplyPayload, usePlanPayload } from './hooks'
-import Loading from '@components/loading/Loading'
-import Spinner from '@components/logo/Spinner'
-import { EnumVariant } from '~/types/enum'
 import PlanApplyStageTracker from './PlanApplyStageTracker'
 import { EnumPlanAction, ModelPlanAction } from '@models/plan-action'
 import { useStoreProject } from '@context/project'
 import { useIDE } from '~/library/pages/ide/context'
 import { useStoreContext } from '@context/context'
-import { Transition } from '@headlessui/react'
-import PlanActionsDescription from './PlanActionsDescription'
 import { Modules } from '@api/client'
 
 function Plan(): JSX.Element {
@@ -27,6 +22,7 @@ function Plan(): JSX.Element {
 
   const planOverviewTracker = useStorePlan(s => s.planOverview)
   const planApplyTracker = useStorePlan(s => s.planApply)
+  const planCancelTracker = useStorePlan(s => s.planCancel)
   const planAction = useStorePlan(s => s.planAction)
   const setPlanAction = useStorePlan(s => s.setPlanAction)
 
@@ -55,7 +51,7 @@ function Plan(): JSX.Element {
   }
 
   function reset(): void {
-    planApplyTracker.reset()
+    resetPlanTrackers()
 
     cleanUp()
     clearErrors()
@@ -86,97 +82,46 @@ function Plan(): JSX.Element {
   }
 
   function apply(): void {
+    resetPlanTrackers()
+
     dispatch([{ type: EnumPlanActions.ResetTestsReport }])
 
     planApply().catch(console.log)
   }
 
   function run(): void {
-    setTests(undefined)
+    resetPlanTrackers()
 
-    planOverviewTracker.reset()
-    planApplyTracker.reset()
+    setTests(undefined)
 
     dispatch([{ type: EnumPlanActions.ResetTestsReport }])
 
     void planRun()
   }
 
+  function resetPlanTrackers(): void {
+    planOverviewTracker.reset()
+    planApplyTracker.reset()
+    planCancelTracker.reset()
+  }
+
   return (
-    <div className="flex flex-col w-full h-full overflow-hidden">
-      <PlanHeader />
-      <div className="w-full h-full px-4 overflow-y-scroll hover:scrollbar scrollbar--vertical">
-        <Transition
-          appear
-          show
-          enter="transition ease duration-700 transform"
-          enterFrom="opacity-0 -translate-y-full"
-          enterTo="opacity-100 translate-y-0"
-          leave="transition ease duration-1000 transform"
-          leaveFrom="opacity-100 translate-y-0"
-          leaveTo="opacity-0 -translate-y-full"
-        >
-          <PlanOptions />
-        </Transition>
-        <Transition
-          appear
-          show
-          enter="transition ease duration-700 transform"
-          enterFrom="opacity-0 scale-95"
-          enterTo="opacity-100 scale-100"
-          leave="transition ease duration-1000 transform"
-          leaveFrom="opacity-100 scale-100"
-          leaveTo="opacity-0 scale-95"
-        >
-          {planAction.isCancelling ? (
-            <CancellingPlanApply />
-          ) : (
-            <PlanApplyStageTracker />
-          )}
-        </Transition>
+    <div className="flex flex-col w-full h-full">
+      {environment.isProd && <PlanHeader />}
+      <div className="w-full h-full flex flex-col pt-2 pl-4 pr-2 overflow-y-scroll hover:scrollbar scrollbar--vertical">
+        <PlanOptions />
+        <PlanApplyStageTracker />
       </div>
       {modules.includes(Modules.plans) && (
-        <>
-          <Transition
-            appear
-            show={isFalse(planAction.isDone)}
-            enter="transition ease duration-1000 transform"
-            enterFrom="opacity-0 translate-y-full"
-            enterTo="opacity-100 translate-y-0"
-            leave="transition ease duration-500 transform"
-            leaveFrom="opacity-100 translate-y-0"
-            leaveTo="opacity-0 translate-y-full"
-          >
-            <PlanActionsDescription />
-          </Transition>
-          <PlanActions
-            apply={apply}
-            run={run}
-            cancel={cancel}
-            reset={reset}
-          />
-        </>
+        <PlanActions
+          apply={apply}
+          run={run}
+          cancel={cancel}
+          reset={reset}
+        />
       )}
     </div>
   )
 }
 
 export default Plan
-
-function CancellingPlanApply(): JSX.Element {
-  return (
-    <div className="w-full h-full p-4">
-      <div className="w-full h-full flex justify-center items-center p-4 bg-warning-10 rounded-lg overflow-hidden">
-        <Loading className="inline-block">
-          <Spinner
-            variant={EnumVariant.Warning}
-            className="w-3 h-3 border border-neutral-10 mr-4"
-          />
-          <h3 className="text-2xl text-warning-500 font-bold">
-            Cancelling Plan...
-          </h3>
-        </Loading>
-      </div>
-    </div>
-  )
-}
diff --git a/web/client/src/library/components/plan/PlanActions.tsx b/web/client/src/library/components/plan/PlanActions.tsx
index 2da2474f..6a31778d 100644
--- a/web/client/src/library/components/plan/PlanActions.tsx
+++ b/web/client/src/library/components/plan/PlanActions.tsx
@@ -1,12 +1,13 @@
 import { type MouseEvent } from 'react'
 import useActiveFocus from '~/hooks/useActiveFocus'
 import { EnumSize, EnumVariant } from '~/types/enum'
-import { includes, isFalse } from '~/utils'
+import { isFalse } from '~/utils'
 import { Button } from '../button/Button'
 import { EnumPlanAction, ModelPlanAction } from '@models/plan-action'
 import { useStorePlan } from '@context/plan'
 import { useStoreContext } from '@context/context'
 import { AddEnvironment, SelectEnvironemnt } from '~/library/pages/root/Page'
+import { Transition } from '@headlessui/react'
 
 export default function PlanActions({
   run,
@@ -25,6 +26,7 @@ export default function PlanActions({
   const setShowConfirmation = useStoreContext(s => s.setShowConfirmation)
 
   const planAction = useStorePlan(s => s.planAction)
+  const planCancel = useStorePlan(s => s.planCancel)
 
   const setFocus = useActiveFocus<HTMLButtonElement>()
 
@@ -90,83 +92,112 @@ export default function PlanActions({
     run()
   }
 
+  const showPlanAction =
+    isFalse(planAction.isCancelling) &&
+    isFalse(planAction.isDone) &&
+    (planAction.isProcessing ? isFalse(planCancel.isSuccessed) : true)
+
   return (
     <>
-      <div className="flex justify-between px-4 pb-2">
+      <div className="flex justify-between pt-2 pl-4 pr-2 pb-2">
         <div className="flex w-full items-center">
-          {(planAction.isRun || planAction.isRunning) && (
-            <Button
-              disabled={planAction.isRunning}
-              onClick={handleRun}
-              ref={setFocus}
-              variant={EnumVariant.Primary}
-              autoFocus
-            >
-              <span>
-                {ModelPlanAction.getActionDisplayName(planAction, [
-                  EnumPlanAction.RunningTask,
-                  EnumPlanAction.Running,
-                  EnumPlanAction.Run,
-                ])}
-              </span>
-            </Button>
-          )}
-          {(planAction.isApply || planAction.isApplying) && (
-            <Button
-              onClick={handleApply}
-              disabled={planAction.isApplying}
-              ref={setFocus}
-              variant={EnumVariant.Primary}
-            >
-              {ModelPlanAction.getActionDisplayName(
-                planAction,
-                [
-                  EnumPlanAction.Applying,
-                  EnumPlanAction.ApplyBackfill,
-                  EnumPlanAction.ApplyVirtual,
-                  EnumPlanAction.ApplyChangesAndBackfill,
-                  EnumPlanAction.ApplyMetadata,
-                ],
-                'Apply',
-              )}
-            </Button>
-          )}
-          {planAction.isProcessing && (
-            <Button
-              onClick={handleCancel}
-              variant={EnumVariant.Danger}
-              className="justify-self-end"
-              disabled={planAction.isCancelling}
-            >
-              {ModelPlanAction.getActionDisplayName(
-                planAction,
-                [EnumPlanAction.Cancelling],
-                'Cancel',
-              )}
-            </Button>
-          )}
+          <Transition
+            appear
+            show={showPlanAction}
+            enter="transition ease duration-300 transform"
+            enterFrom="opacity-0 scale-95"
+            enterTo="opacity-100 scale-100"
+            leave="transition ease duration-300 transform"
+            leaveFrom="opacity-100 scale-100"
+            leaveTo="opacity-0 scale-95"
+            className="trasition-all duration-300 ease-in-out"
+          >
+            {showPlanAction && (
+              <Button
+                disabled={
+                  planAction.isProcessing ||
+                  planCancel.isSuccessed ||
+                  planAction.isDone
+                }
+                onClick={
+                  planAction.isRun
+                    ? handleRun
+                    : planCancel.isSuccessed
+                    ? undefined
+                    : handleApply
+                }
+                ref={setFocus}
+                variant={
+                  planCancel.isSuccessed
+                    ? EnumVariant.Danger
+                    : EnumVariant.Primary
+                }
+                autoFocus
+                className="trasition-all duration-300 ease-in-out"
+              >
+                <span>
+                  {ModelPlanAction.getActionDisplayName(
+                    planAction,
+                    planCancel.isSuccessed
+                      ? []
+                      : [
+                          EnumPlanAction.RunningTask,
+                          EnumPlanAction.Running,
+                          EnumPlanAction.Run,
+                          EnumPlanAction.Applying,
+                          EnumPlanAction.ApplyBackfill,
+                          EnumPlanAction.ApplyVirtual,
+                          EnumPlanAction.ApplyChangesAndBackfill,
+                          EnumPlanAction.ApplyMetadata,
+                        ],
+                    planCancel.isSuccessed ? 'Canceled' : 'Done',
+                  )}
+                </span>
+              </Button>
+            )}
+          </Transition>
         </div>
         <div className="flex items-center">
-          {[planAction.isProcessing, planAction.isRun].every(isFalse) && (
+          <Transition
+            appear
+            show={planAction.isProcessing || isFalse(planAction.isRun)}
+            enter="transition ease duration-300 transform"
+            enterFrom="opacity-0 scale-95"
+            enterTo="opacity-100 scale-100"
+            leave="transition ease duration-300 transform"
+            leaveFrom="opacity-100 scale-100"
+            leaveTo="opacity-0 scale-95"
+            className="trasition-all duration-300 ease-in-out"
+          >
             <Button
-              onClick={handleReset}
-              variant={EnumVariant.Neutral}
-              disabled={includes(
-                [
-                  EnumPlanAction.Running,
-                  EnumPlanAction.Applying,
-                  EnumPlanAction.Cancelling,
-                ],
-                planAction.value,
-              )}
+              onClick={
+                planAction.isRunning || planAction.isApplying
+                  ? handleCancel
+                  : handleReset
+              }
+              variant={
+                planAction.isProcessing
+                  ? EnumVariant.Danger
+                  : EnumVariant.Neutral
+              }
+              disabled={
+                planAction.isCancelling ||
+                (planAction.isProcessing && planCancel.isSuccessed)
+              }
             >
               {ModelPlanAction.getActionDisplayName(
                 planAction,
-                [],
-                'Start Over',
+                planAction.isProcessing && isFalse(planCancel.isSuccessed)
+                  ? [EnumPlanAction.Cancelling]
+                  : [],
+                planAction.isProcessing
+                  ? planCancel.isSuccessed
+                    ? 'Finishing Cancellation...'
+                    : 'Cancel'
+                  : 'Start Over',
               )}
             </Button>
-          )}
+          </Transition>
         </div>
       </div>
     </>
diff --git a/web/client/src/library/components/plan/PlanActionsDescription.tsx b/web/client/src/library/components/plan/PlanActionsDescription.tsx
deleted file mode 100644
index cb22a086..00000000
--- a/web/client/src/library/components/plan/PlanActionsDescription.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { useStoreContext } from '~/context/context'
-import { usePlan } from './context'
-import { isFalse, isNotNil, isStringEmptyOrNil, isTrue } from '@utils/index'
-import Banner from '@components/banner/Banner'
-import { EnumSize, EnumVariant } from '~/types/enum'
-
-export default function PlanActionsDescription(): JSX.Element {
-  const {
-    start,
-    end,
-    skip_tests,
-    skip_backfill,
-    no_gaps,
-    no_auto_categorization,
-    forward_only,
-    restate_models,
-    include_unmodified,
-    auto_apply,
-    create_from,
-  } = usePlan()
-
-  const environment = useStoreContext(s => s.environment)
-
-  return (
-    <div className="text-xs px-4 py-2">
-      <Banner
-        variant={EnumVariant.Warning}
-        size={EnumSize.sm}
-      >
-        <p className="text-sm">
-          <span>Plan for</span>
-          <b className="text-primary-500 font-bold mx-1">{environment.name}</b>
-          <span className="inline-block mr-1">environment</span>
-          {isFalse(environment.isProd) && isNotNil(create_from) && (
-            <span className="inline-block mr-1">
-              <span>based of</span>
-              <b className="text-primary-500 font-bold mx-1">{create_from}</b>
-            </span>
-          )}
-          {isTrue(auto_apply) && (
-            <span className="inline-block mr-1">
-              and <b>Apply</b> automatically
-            </span>
-          )}
-          {isTrue(skip_tests) && (
-            <span className="inline-block mr-1">
-              without <b>Tests</b>
-            </span>
-          )}
-          {
-            <span className="inline-block mr-1">
-              <span>from&nbsp;</span>
-              <b>
-                {isFalse(isStringEmptyOrNil(start))
-                  ? start
-                  : 'the begining of history'}
-              </b>
-            </span>
-          }
-          {
-            <span className="inline-block mr-1">
-              till <b>{isFalse(isStringEmptyOrNil(start)) ? end : 'today'}</b>
-            </span>
-          }
-          {isTrue(no_gaps) && (
-            <span className="inline-block mr-1">
-              with <b>No Gaps</b>
-            </span>
-          )}
-          {isTrue(skip_backfill) && (
-            <span className="inline-block mr-1">
-              without <b>Backfills</b>
-            </span>
-          )}
-          {isTrue(forward_only) && (
-            <span className="inline-block mr-1">
-              all changes will be <b>Forward Only</b>
-            </span>
-          )}
-          {isTrue(no_auto_categorization) && (
-            <span className="inline-block mr-1">
-              also set <b>Change Category</b> manually
-            </span>
-          )}
-          {isFalse(isStringEmptyOrNil(restate_models)) && (
-            <span className="inline-block mr-1">
-              and restate the following models <b>{restate_models}</b>
-            </span>
-          )}
-          {isTrue(include_unmodified) && (
-            <span className="inline-block mr-1">
-              and with views for all models in the target development
-              environment
-            </span>
-          )}
-        </p>
-      </Banner>
-    </div>
-  )
-}
diff --git a/web/client/src/library/components/plan/PlanApplyStageTracker.tsx b/web/client/src/library/components/plan/PlanApplyStageTracker.tsx
index a0cd8597..ef4c9de8 100644
--- a/web/client/src/library/components/plan/PlanApplyStageTracker.tsx
+++ b/web/client/src/library/components/plan/PlanApplyStageTracker.tsx
@@ -44,65 +44,81 @@ export default function PlanApplyStageTracker(): JSX.Element {
 
   const planApply = useStorePlan(s => s.planApply)
   const planOverview = useStorePlan(s => s.planOverview)
+  const planCancel = useStorePlan(s => s.planCancel)
   const planAction = useStorePlan(s => s.planAction)
 
-  const { plan_options } = getPlanOverviewDetails(planApply, planOverview)
+  const { plan_options } = getPlanOverviewDetails(
+    planApply,
+    planOverview,
+    planCancel,
+  )
 
   const hasTestsDetails = isNotNil(tests) && Boolean(tests.total)
   const showTestsMessage =
     isNotNil(tests) && Boolean(tests.message) && isFalse(hasTestsDetails)
 
+  const hasFailedTests = isNotNil(tests) && Boolean(tests.failures)
+  const showChangesAndBackfills =
+    isFalse(planAction.isProcessing) &&
+    isFalse(planAction.isDone) &&
+    isFalse(planCancel.isFinished)
+
   return (
     <Transition
+      appear
       show={isFalse(planAction.isRun)}
-      enter="transition ease duration-500 transform"
+      enter="transition ease duration-300 transform"
       enterFrom="opacity-0 scale-95"
       enterTo="opacity-100 scale-100"
-      leave="transition ease duration-500 transform"
+      leave="transition ease duration-300 transform"
       leaveFrom="opacity-100 scale-100"
       leaveTo="opacity-0 scale-95"
+      className="my-2"
     >
-      <div className="mt-8 mb-4">
-        <StageChanges isOpen={isFalse(planAction.isApplying)} />
-        <StageBackfills isOpen={isFalse(planAction.isApplying)} />
-        {isTrue(plan_options?.skip_tests) ? (
-          <Banner
-            className="flex items-center mb-1"
-            size={EnumSize.sm}
-            hasBackground={false}
-          >
-            <CheckIcon className="w-5 mr-2" />
-            <Banner.Label className="mr-2 text-sm">Tests Skipped</Banner.Label>
-          </Banner>
-        ) : hasTestsDetails ? (
-          <StageTestsFailed
-            isOpen={true}
-            report={tests}
-          />
-        ) : showTestsMessage ? (
-          <StageTestsCompleted report={tests} />
-        ) : (
-          <Banner
-            className="flex items-center mb-1"
-            size={EnumSize.sm}
-            hasBackground={false}
-          >
-            <CheckIcon className="w-5 mr-2" />
-            <Banner.Label className="mr-2 text-sm">No Tests</Banner.Label>
-          </Banner>
-        )}
-        <StageValidate />
-        <StageVirtualUpdate />
+      <StageChanges isOpen={showChangesAndBackfills} />
+      <StageBackfills isOpen={showChangesAndBackfills} />
+      {isTrue(plan_options?.skip_tests) ? (
+        <Banner
+          className="flex items-center mb-1"
+          size={EnumSize.sm}
+          hasBackground={false}
+        >
+          <CheckIcon className="w-4 mr-2" />
+          <Banner.Label className="mr-2 text-sm">Tests Skipped</Banner.Label>
+        </Banner>
+      ) : hasTestsDetails ? (
+        <StageTestsFailed
+          isOpen={true}
+          report={tests}
+        />
+      ) : showTestsMessage ? (
+        <StageTestsCompleted report={tests} />
+      ) : (
+        <Banner
+          className="flex items-center mb-1"
+          size={EnumSize.sm}
+          hasBackground={false}
+        >
+          <CheckIcon className="w-4 mr-2" />
+          <Banner.Label className="mr-2 text-sm">No Tests</Banner.Label>
+        </Banner>
+      )}
+      <StageVirtualUpdate />
+      {(hasFailedTests || planApply.shouldShowEvaluation) && (
         <StageEvaluate
           start={planApply.evaluationStart}
-          end={planApply.evaluationEnd}
+          end={
+            planApply.isFinished
+              ? planApply.evaluationEnd ?? planCancel.meta?.end
+              : undefined
+          }
         >
           <StageCreation />
           <StageRestate />
           <StageBackfill />
           <StagePromote />
         </StageEvaluate>
-      </div>
+      )}
     </Transition>
   )
 }
@@ -110,92 +126,69 @@ export default function PlanApplyStageTracker(): JSX.Element {
 function StageChanges({ isOpen = false }: { isOpen?: boolean }): JSX.Element {
   const planApply = useStorePlan(s => s.planApply)
   const planOverview = useStorePlan(s => s.planOverview)
-  const planAction = useStorePlan(s => s.planAction)
+  const planCancel = useStorePlan(s => s.planCancel)
 
   const { meta, stageChanges, hasChanges } = getPlanOverviewDetails(
     planApply,
     planOverview,
+    planCancel,
   )
-
-  const showChanges = planAction.isRunning || isTrue(hasChanges)
+  const tempMeta = stageChanges?.meta ?? meta
+  const showChanges = tempMeta?.status === Status.init || isTrue(hasChanges)
 
   return showChanges ? (
-    <Transition
-      show={showChanges}
-      enter="transition ease duration-700 transform"
-      enterFrom="opacity-0 scale-95"
-      enterTo="opacity-100 scale-100"
-      leave="transition ease duration-1000 transform"
-      leaveFrom="opacity-100 scale-100"
-      leaveTo="opacity-0 scale-95"
-    >
-      <Stage
-        meta={stageChanges?.meta ?? meta ?? { status: Status.init }}
-        states={['Changes', 'Failed Getting Changes', 'Getting Changes...']}
-        isOpen={isOpen}
-        panel={<PlanChanges />}
-      />
-    </Transition>
+    <Stage
+      meta={tempMeta}
+      states={['Changes', 'Failed Getting Changes', 'Getting Changes...']}
+      isOpen={isOpen && isTrue(hasChanges)}
+      panel={<PlanChanges />}
+    />
   ) : (
     <Banner
       className="flex items-center mb-1"
       size={EnumSize.sm}
       hasBackground={false}
     >
-      <CheckIcon className="w-5 mr-2" />
+      <CheckIcon className="w-4 mr-2" />
       <Banner.Label className="mr-2 text-sm">No Changes</Banner.Label>
     </Banner>
   )
 }
 
-function StageBackfills({ isOpen }: { isOpen?: boolean }): JSX.Element {
+function StageBackfills({ isOpen = false }: { isOpen?: boolean }): JSX.Element {
   const planApply = useStorePlan(s => s.planApply)
   const planOverview = useStorePlan(s => s.planOverview)
-  const planAction = useStorePlan(s => s.planAction)
+  const planCancel = useStorePlan(s => s.planCancel)
 
   const { meta, stageBackfills, backfills, hasBackfills } =
-    getPlanOverviewDetails(planApply, planOverview)
-
-  const showBackfills = planAction.isRunning || isTrue(hasBackfills)
+    getPlanOverviewDetails(planApply, planOverview, planCancel)
+  const tempMeta = stageBackfills?.meta ?? meta
+  const showBackfills = tempMeta?.status === Status.init || isTrue(hasBackfills)
 
   return showBackfills ? (
-    <Transition
-      show={showBackfills}
-      enter="transition ease duration-700 transform"
-      enterFrom="opacity-0 scale-95"
-      enterTo="opacity-100 scale-100"
-      leave="transition ease duration-1000 transform"
-      leaveFrom="opacity-100 scale-100"
-      leaveTo="opacity-0 scale-95"
-    >
-      <Stage
-        meta={stageBackfills?.meta ?? meta ?? { status: Status.init }}
-        states={[
-          'Backfills',
-          'Failed Getting Backfills',
-          'Getting Backfills...',
-        ]}
-        isOpen={isOpen}
-        panel={
-          <PlanChangePreview
-            headline={`Models ${backfills.length}`}
+    <Stage
+      meta={tempMeta}
+      states={['Backfills', 'Failed Getting Backfills', 'Getting Backfills...']}
+      isOpen={isOpen && isTrue(hasBackfills)}
+      panel={
+        <PlanChangePreview
+          headline={`Models ${backfills.length}`}
+          type={EnumPlanChangeType.Default}
+        >
+          <PlanChangePreview.Default
             type={EnumPlanChangeType.Default}
-          >
-            <PlanChangePreview.Default
-              type={EnumPlanChangeType.Default}
-              changes={backfills}
-            />
-          </PlanChangePreview>
-        }
-      />
-    </Transition>
+            changes={backfills}
+          />
+        </PlanChangePreview>
+      }
+    />
   ) : (
     <Banner
       className="flex items-center mb-1"
       size={EnumSize.sm}
       hasBackground={false}
     >
-      <CheckIcon className="w-5 mr-2" />
+      <CheckIcon className="w-4 mr-2" />
       <Banner.Label className="mr-2 text-sm">No Backfills</Banner.Label>
     </Banner>
   )
@@ -238,25 +231,6 @@ function StageTestsFailed({
   )
 }
 
-function StageValidate(): JSX.Element {
-  const planApply = useStorePlan(s => s.planApply)
-  const planOverview = useStorePlan(s => s.planOverview)
-
-  const { stageValidation } = getPlanOverviewDetails(planApply, planOverview)
-
-  return (
-    <Stage
-      meta={stageValidation?.meta}
-      states={[
-        'Plan Validated',
-        'Plan Validation Failed',
-        'Validating Plan...',
-      ]}
-      showDetails={false}
-    />
-  )
-}
-
 function StageEvaluate({
   start,
   end,
@@ -266,54 +240,42 @@ function StageEvaluate({
   end?: PlanOverviewStageTrackerEnd
   children: React.ReactNode
 }): JSX.Element {
-  const tests = useStoreProject(s => s.tests)
-  const planApply = useStorePlan(s => s.planApply)
-
   const elStageEvaluate = useRef<HTMLDivElement>(null)
 
-  const hasFailedTests = isNotNil(tests) && Boolean(tests.failures)
+  useEffect(() => {
+    setTimeout(() => {
+      elStageEvaluate.current?.scrollIntoView({
+        behavior: 'smooth',
+        block: 'start',
+      })
+    }, 500)
+  }, [])
 
   return (
-    <Transition
-      show={hasFailedTests || planApply.shouldShowEvaluation}
-      enter="transition ease duration-500 transform delay-[1000ms]"
-      enterFrom="opacity-0 scale-95"
-      enterTo="opacity-100 scale-100"
-      leave="transition ease duration-500 transform"
-      leaveFrom="opacity-100 scale-100"
-      leaveTo="opacity-0 scale-95"
+    <div
+      ref={elStageEvaluate}
+      className="pt-4 pb-2 text-xs"
     >
-      <div
-        ref={elStageEvaluate}
-        className="pt-6 pb-2"
-      >
-        {isNotNil(start) && (
-          <>
-            <small className="text-neutral-500 block px-4 mb-1">
-              Evaluation started at{' '}
-              <b>{toDateFormat(new Date(start), 'yyyy-mm-dd hh-mm-ss')}</b>
-            </small>
-            <Divider />
-            <small className="text-neutral-500 block px-4 mt-1">
-              Given a plan, it pushes snapshots into the state and then kicks
-              off the backfill process for all affected snapshots. Once backfill
-              is done, snapshots that are part of the plan are promoted in the
-              environment targeted by this plan.
-            </small>
-          </>
-        )}
-        <div className="py-2">{children}</div>
-        {isNotNil(end) && (
-          <>
-            <Divider />
-            <small className="text-neutral-500 block px-4 mt-1">
-              Evaluation stopped at{' '}
-              <b>{toDateFormat(new Date(end), 'yyyy-mm-dd hh-mm-ss')}</b>
-            </small>
-          </>
-        )}
-      </div>
-    </Transition>
+      {isNotNil(start) && (
+        <>
+          <span className="text-neutral-500 block px-4 mb-1">
+            Evaluation started at{' '}
+            {toDateFormat(new Date(start), 'yyyy-mm-dd hh-mm-ss')}
+          </span>
+          <Divider />
+        </>
+      )}
+      <div className="py-2">{children}</div>
+      {isNotNil(end) && (
+        <>
+          <Divider />
+          <span className="text-neutral-500 block px-4 mt-1">
+            Evaluation stopped at{' '}
+            {toDateFormat(new Date(end), 'yyyy-mm-dd hh-mm-ss')}
+          </span>
+        </>
+      )}
+    </div>
   )
 }
 
@@ -372,7 +334,7 @@ function StageRestate(): JSX.Element {
       size={EnumSize.sm}
       hasBackground={false}
     >
-      <CheckIcon className="w-5 mr-2" />
+      <CheckIcon className="w-4 mr-2" />
       <Banner.Label className="mr-2 text-sm">No Models To Restate</Banner.Label>
     </Banner>
   ) : (
@@ -388,23 +350,12 @@ function StageRestate(): JSX.Element {
 }
 
 function StageBackfill(): JSX.Element {
-  const elStageBackfill = useRef<HTMLDivElement>(null)
-
   const planApply = useStorePlan(s => s.planApply)
   const planAction = useStorePlan(s => s.planAction)
 
   const environment = planApply.environment
   const stageBackfill = planApply.stageBackfill
 
-  useEffect(() => {
-    setTimeout(() => {
-      elStageBackfill.current?.scrollIntoView({
-        behavior: 'smooth',
-        block: 'start',
-      })
-    }, 1000)
-  }, [])
-
   const { change_categorization } = usePlan()
 
   const categories = useMemo(
@@ -472,49 +423,47 @@ function StageBackfill(): JSX.Element {
   if (isNil(stageBackfill) || isNil(environment)) return <></>
 
   return (
-    <div ref={elStageBackfill}>
-      <Stage
-        meta={stageBackfill.meta}
-        states={[
-          'Intervals Backfilled',
-          'Intervals Backfilling Failed',
-          'Backfilling Intervals...',
-        ]}
-        showDetails={true}
-        isOpen={true}
-        shouldCollapse={false}
-      >
-        <TasksOverview tasks={tasks}>
-          {({ total, completed, models, completedBatches, totalBatches }) => (
-            <>
-              <TasksOverview.Summary
-                headline="Target Environment"
-                environment={environment}
-                completed={completed}
-                total={total}
-                completedBatches={completedBatches}
-                totalBatches={totalBatches}
-                updateType={planAction.isApplyVirtual ? 'Virtual' : 'Backfill'}
+    <Stage
+      meta={stageBackfill.meta}
+      states={[
+        'Intervals Backfilled',
+        'Intervals Backfilling Failed',
+        'Backfilling Intervals...',
+      ]}
+      showDetails={true}
+      isOpen={true}
+      shouldCollapse={false}
+    >
+      <TasksOverview tasks={tasks}>
+        {({ total, completed, models, completedBatches, totalBatches }) => (
+          <>
+            <TasksOverview.Summary
+              headline="Target Environment"
+              environment={environment}
+              completed={completed}
+              total={total}
+              completedBatches={completedBatches}
+              totalBatches={totalBatches}
+              updateType={planAction.isApplyVirtual ? 'Virtual' : 'Backfill'}
+            />
+            {isNotNil(models) && (
+              <TasksOverview.Details
+                models={models}
+                added={planApply.added}
+                removed={planApply.removed}
+                direct={planApply.direct}
+                indirect={planApply.indirect}
+                metadata={planApply.metadata}
+                queue={planApply.queue}
+                showBatches={true}
+                showVirtualUpdate={planAction.isApplyVirtual}
+                showProgress={true}
               />
-              {isNotNil(models) && (
-                <TasksOverview.Details
-                  models={models}
-                  added={planApply.added}
-                  removed={planApply.removed}
-                  direct={planApply.direct}
-                  indirect={planApply.indirect}
-                  metadata={planApply.metadata}
-                  queue={planApply.queue}
-                  showBatches={true}
-                  showVirtualUpdate={planAction.isApplyVirtual}
-                  showProgress={true}
-                />
-              )}
-            </>
-          )}
-        </TasksOverview>
-      </Stage>
-    </div>
+            )}
+          </>
+        )}
+      </TasksOverview>
+    </Stage>
   )
 }
 
@@ -572,10 +521,10 @@ function StageVirtualUpdate(): JSX.Element {
 
   const planApply = useStorePlan(s => s.planApply)
   const planOverview = useStorePlan(s => s.planOverview)
-  const isVirtualUpdate = planOverview.isLatest
-    ? isTrue(planApply.overview?.isVirtualUpdate)
-    : planOverview.isVirtualUpdate
-  const isUpdated = isTrue(planApply.stagePromote?.meta?.done)
+
+  const isVirtualUpdate =
+    planApply.overview?.isVirtualUpdate ?? planOverview.isVirtualUpdate
+  const isUpdated = isTrue(planApply.isFinished)
 
   return isVirtualUpdate ? (
     <Stage
@@ -599,9 +548,10 @@ function StageVirtualUpdate(): JSX.Element {
 function PlanChanges(): JSX.Element {
   const planOverview = useStorePlan(s => s.planOverview)
   const planApply = useStorePlan(s => s.planApply)
+  const planCancel = useStorePlan(s => s.planCancel)
 
   const { hasChanges, added, removed, direct, indirect, metadata } =
-    getPlanOverviewDetails(planApply, planOverview)
+    getPlanOverviewDetails(planApply, planOverview, planCancel)
 
   return (
     <div className="w-full my-2">
@@ -694,7 +644,7 @@ function Stage({
 
   const hasChildren = isNotNil(panel) || isNotNil(children)
 
-  const [open, setOpen] = useState(false)
+  const [open, setOpen] = useState(isOpen)
 
   useEffect(() => {
     if (isNil(elTrigger.current)) return
@@ -707,35 +657,34 @@ function Stage({
   }, [elTrigger, planOverview, planApply, shouldCollapse])
 
   useEffect(() => {
-    setOpen(isOpen || (meta?.status !== Status.init && hasChildren))
-  }, [meta?.status, hasChildren, isOpen])
-
-  if (isNil(meta)) return <></>
+    setOpen(isOpen && hasChildren)
+  }, [isOpen, hasChildren])
 
   const variant =
-    meta.status === Status.success
+    meta?.status === Status.success
       ? EnumVariant.Success
-      : meta.status === Status.fail
+      : meta?.status === Status.fail
       ? EnumVariant.Danger
       : EnumVariant.Info
   const [titleSuccess, titleFail, titleDefault] = states
   const text =
-    meta.status === Status.success
+    meta?.status === Status.success
       ? titleSuccess
-      : meta.status === Status.fail
+      : meta?.status === Status.fail
       ? titleFail
       : titleDefault
 
   return (
     <Transition
       appear
-      show
-      enter="transition-all ease-in duration-500 delay-[200ms]"
-      enterFrom="opacity-0 translate-y-6"
-      enterTo="opacity-100 translate-y-0"
-      leave="transition-all ease-out duration-200"
-      leaveFrom="opacity-100"
-      leaveTo="opacity-0"
+      show={isNotNil(meta)}
+      enter="transition ease duration-300 transform"
+      enterFrom="opacity-0 scale-95"
+      enterTo="opacity-100 scale-100"
+      leave="transition ease duration-300 transform"
+      leaveFrom="opacity-100 scale-100"
+      leaveTo="opacity-0 scale-95"
+      className="my-2"
     >
       <Disclosure>
         <Banner
@@ -756,16 +705,7 @@ function Stage({
           >
             {isNil(trigger) ? (
               <>
-                <Transition
-                  show={meta.status === Status.init}
-                  className="flex items-center h-full"
-                  enter="transition-all ease-in-out duration-500"
-                  enterFrom="opacity-0 translate-y-6"
-                  enterTo="opacity-100 translate-y-0"
-                  leave="transition-all ease-in-out duration-200"
-                  leaveFrom="opacity-100"
-                  leaveTo="opacity-0"
-                >
+                {meta?.status === Status.init && (
                   <Loading
                     text={text}
                     hasSpinner
@@ -773,43 +713,36 @@ function Stage({
                     variant={EnumVariant.Primary}
                     className="w-full"
                   />
-                </Transition>
-                <Transition
-                  show={meta.status !== Status.init}
-                  className="flex items-center h-full"
-                  enter="transition-all ease-in-out duration-400 delay-[300ms]"
-                  enterFrom="opacity-0 translate-y-6"
-                  enterTo="opacity-100 translate-y-0"
-                  leave="transition-all ease-in-out duration-200"
-                  leaveFrom="opacity-100"
-                  leaveTo="opacity-0"
-                >
-                  {showDetails ? (
-                    <>
-                      {open ? (
-                        <MinusCircleIcon className="w-6 mr-2" />
-                      ) : (
-                        <PlusCircleIcon className="w-6 mr-2" />
-                      )}
-                    </>
-                  ) : (
-                    <>
-                      {meta.status === Status.success && (
-                        <CheckIcon className="w-6 mr-2" />
-                      )}
-                      {meta.status === Status.fail && (
-                        <ExclamationCircleIcon className="w-6 mr-2" />
-                      )}
-                    </>
-                  )}
-                  <Banner.Label className="mr-2 text-sm w-full">
-                    <Title
-                      text={text}
-                      size={EnumSize.sm}
-                      variant={variant}
-                    />
-                  </Banner.Label>
-                </Transition>
+                )}
+                {meta?.status !== Status.init && (
+                  <div className="flex items-center h-full">
+                    {showDetails ? (
+                      <>
+                        {open ? (
+                          <MinusCircleIcon className="min-w-4 max-w-4 mr-2" />
+                        ) : (
+                          <PlusCircleIcon className="min-w-4 max-w-4 mr-2" />
+                        )}
+                      </>
+                    ) : (
+                      <>
+                        {meta?.status === Status.success && (
+                          <CheckIcon className="min-w-4 max-w-4 mr-2" />
+                        )}
+                        {meta?.status === Status.fail && (
+                          <ExclamationCircleIcon className="min-w-4 max-w-4 mr-2" />
+                        )}
+                      </>
+                    )}
+                    <Banner.Label className="mr-2 text-sm w-full">
+                      <Title
+                        text={text}
+                        size={EnumSize.sm}
+                        variant={variant}
+                      />
+                    </Banner.Label>
+                  </div>
+                )}
               </>
             ) : (
               trigger
@@ -817,13 +750,15 @@ function Stage({
           </Disclosure.Button>
         </Banner>
         <Transition
+          appear
           show={open}
-          enter="transition-all ease-in-out duration-300 delay-[200ms]"
-          enterFrom="opacity-0 translate-y-6"
-          enterTo="opacity-100 translate-y-0"
-          leave="transition-all ease-in-out duration-200"
-          leaveFrom="opacity-100"
-          leaveTo="opacity-0"
+          enter="transition ease duration-300 transform"
+          enterFrom="opacity-0 scale-95"
+          enterTo="opacity-100 scale-100"
+          leave="transition ease duration-300 transform"
+          leaveFrom="opacity-100 scale-100"
+          leaveTo="opacity-0 scale-95"
+          className="trasition-all duration-300 ease-in-out"
         >
           <Disclosure.Panel
             static
@@ -841,7 +776,7 @@ function Stage({
                 {children}
               </div>
             )}
-            {meta.status !== Status.fail && panel}
+            {meta?.status !== Status.fail && panel}
           </Disclosure.Panel>
         </Transition>
       </Disclosure>
diff --git a/web/client/src/library/components/plan/PlanHeader.tsx b/web/client/src/library/components/plan/PlanHeader.tsx
index f6d44df9..92b487e3 100644
--- a/web/client/src/library/components/plan/PlanHeader.tsx
+++ b/web/client/src/library/components/plan/PlanHeader.tsx
@@ -3,31 +3,33 @@ import { useStoreContext } from '~/context/context'
 import { EnumVariant } from '~/types/enum'
 import { Disclosure } from '@headlessui/react'
 import { MinusCircleIcon, PlusCircleIcon } from '@heroicons/react/24/solid'
-import { useStorePlan } from '@context/plan'
-import { isFalse } from '@utils/index'
 
 export default function PlanHeader(): JSX.Element {
   const environment = useStoreContext(s => s.environment)
 
-  const planApply = useStorePlan(s => s.planApply)
-
   return (
     <div className="flex flex-col px-4 py-2 w-full">
-      {environment.isInitialProd && isFalse(planApply.isFinished) && (
-        <Banner variant={EnumVariant.Warning}>
-          <Disclosure defaultOpen={false}>
-            {({ open }) => (
-              <>
-                <Disclosure.Button className="w-full flex items-center justify-between">
-                  <Banner.Label className="w-full">
-                    Initializing Prod Environment
-                  </Banner.Label>
-                  {open ? (
-                    <MinusCircleIcon className="w-5 text-warning-500" />
-                  ) : (
-                    <PlusCircleIcon className="w-5 text-warning-500" />
-                  )}
-                </Disclosure.Button>
+      <Banner variant={EnumVariant.Warning}>
+        <Disclosure defaultOpen={false}>
+          {({ open }) => (
+            <>
+              <Disclosure.Button className="w-full flex items-center justify-between">
+                <Banner.Label className="w-full">
+                  {environment.isInitialProd
+                    ? 'Initializing Prod Environment'
+                    : 'Prod Environment'}
+                </Banner.Label>
+                {environment.isInitialProd && (
+                  <>
+                    {open ? (
+                      <MinusCircleIcon className="w-5 text-warning-500" />
+                    ) : (
+                      <PlusCircleIcon className="w-5 text-warning-500" />
+                    )}
+                  </>
+                )}
+              </Disclosure.Button>
+              {environment.isInitialProd && (
                 <Disclosure.Panel className="px-2 text-sm mt-2">
                   <Banner.Description>
                     Prod will be completely backfilled in order to ensure there
@@ -36,11 +38,11 @@ export default function PlanHeader(): JSX.Element {
                     deploying to production.
                   </Banner.Description>
                 </Disclosure.Panel>
-              </>
-            )}
-          </Disclosure>
-        </Banner>
-      )}
+              )}
+            </>
+          )}
+        </Disclosure>
+      </Banner>
     </div>
   )
 }
diff --git a/web/client/src/library/components/plan/help.ts b/web/client/src/library/components/plan/help.ts
index e0425131..78b58dc7 100644
--- a/web/client/src/library/components/plan/help.ts
+++ b/web/client/src/library/components/plan/help.ts
@@ -1,6 +1,7 @@
 import { type ModelPlanOverviewTracker } from '@models/tracker-plan-overview'
 import { isArrayNotEmpty, isNotNil } from '../../../utils'
 import { type ModelPlanApplyTracker } from '@models/tracker-plan-apply'
+import { type ModelPlanCancelTracker } from '@models/tracker-plan-cancel'
 
 export function isModified<T extends object>(modified?: T): boolean {
   return Object.values(modified ?? {}).some(isArrayNotEmpty)
@@ -28,10 +29,12 @@ type PlanOverviewDetails = Pick<
 export function getPlanOverviewDetails(
   planApply: ModelPlanApplyTracker,
   planOverview: ModelPlanOverviewTracker,
+  planCancel: ModelPlanCancelTracker,
 ): PlanOverviewDetails {
   const isLatest =
-    planApply.isFinished &&
-    (planOverview.isLatest || planOverview.isRunning) &&
+    ((planApply.isFinished &&
+      (planOverview.isLatest || planOverview.isRunning)) ||
+      planCancel.isFinished) &&
     isNotNil(planApply.overview)
   const overview = isLatest ? planApply.overview : planOverview
   const plan = isLatest ? planApply : planOverview
diff --git a/web/client/src/library/components/report/ReportErrors.tsx b/web/client/src/library/components/report/ReportErrors.tsx
index 9aed97de..f5315d7f 100644
--- a/web/client/src/library/components/report/ReportErrors.tsx
+++ b/web/client/src/library/components/report/ReportErrors.tsx
@@ -40,8 +40,8 @@ export default function ReportErrors(): JSX.Element {
             className={clsx(
               'block ml-1 px-3 py-1 first-child:ml-0 rounded-full whitespace-nowrap font-bold text-xs text-center',
               hasError
-                ? 'border-danger-10 text-danger-500 bg-danger-10 cursor-pointer'
-                : 'border-neutral-10 cursor-default text-neutral-500 bg-neutral-10 dark:text-neutral-300',
+                ? 'text-danger-500 bg-danger-10 dark:bg-danger-20 cursor-pointer'
+                : 'bg-neutral-5 dark:bg-neutral-20 cursor-default text-neutral-500 dark:text-neutral-300',
             )}
           >
             {hasError ? (
diff --git a/web/client/src/library/components/tasksOverview/TasksOverview.tsx b/web/client/src/library/components/tasksOverview/TasksOverview.tsx
index de085495..12f5b0de 100644
--- a/web/client/src/library/components/tasksOverview/TasksOverview.tsx
+++ b/web/client/src/library/components/tasksOverview/TasksOverview.tsx
@@ -14,6 +14,7 @@ import { type EnvironmentName } from '~/models/environment'
 import { EnumPlanChangeType, type PlanChangeType } from '../plan/context'
 import Title from '@components/title/Title'
 import { type ModelSQLMeshChangeDisplay } from '@models/sqlmesh-change-display'
+import { Transition } from '@headlessui/react'
 
 const TasksOverview = function TasksOverview({
   children,
@@ -159,8 +160,17 @@ function TasksDetails({
 }): JSX.Element {
   return (
     <>
-      {isArrayNotEmpty(queue) && (
-        <div className="p-4 mt-6 shadow-lg rounded-lg">
+      <Transition
+        show={isArrayNotEmpty(queue)}
+        enter="transition ease duration-300 transform"
+        enterFrom="opacity-0 scale-95"
+        enterTo="opacity-100 scale-100"
+        leave="transition ease duration-300 transform"
+        leaveFrom="opacity-100 scale-100"
+        leaveTo="opacity-0 scale-95"
+        className="trasition-all duration-300 ease-in-out"
+      >
+        <div className="px-4 pt-3 pb-2 mt-4 shadow-lg rounded-lg">
           <Title text="Currently in proccess" />
           <Tasks models={queue}>
             {task => (
@@ -228,7 +238,7 @@ function TasksDetails({
             )}
           </Tasks>
         </div>
-      )}
+      </Transition>
       <TasksBlock className={className}>
         <Tasks models={models}>
           {task => (
@@ -343,7 +353,7 @@ function Tasks({
   return (
     <ul
       className={clsx(
-        'rounded-lg py-4 overflow-auto text-prose hover:scrollbar scrollbar--vertical scrollbar--horizontal',
+        'rounded-lg pt-4 overflow-auto text-prose hover:scrollbar scrollbar--vertical scrollbar--horizontal',
         className,
       )}
     >
diff --git a/web/client/src/library/pages/docs/Content.tsx b/web/client/src/library/pages/docs/Content.tsx
index fb51a547..866ba929 100644
--- a/web/client/src/library/pages/docs/Content.tsx
+++ b/web/client/src/library/pages/docs/Content.tsx
@@ -44,7 +44,7 @@ export default function Content(): JSX.Element {
     }
 
     setLastSelectedModel(model)
-  }, [model])
+  }, [model, model?.sql])
 
   const modelExtensions = isNil(model)
     ? []
@@ -100,7 +100,7 @@ export default function Content(): JSX.Element {
                     path={model.path}
                   >
                     {({ file }) => (
-                      <Tab.Group defaultIndex={model.isModelSQL ? 1 : 0}>
+                      <Tab.Group>
                         <TabList
                           list={
                             [
@@ -122,10 +122,7 @@ export default function Content(): JSX.Element {
                             />
                           </Tab.Panel>
                           {model.isModelSQL && (
-                            <Tab.Panel
-                              unmount={false}
-                              className="w-full h-full"
-                            >
+                            <Tab.Panel className="w-full h-full">
                               <CodeEditorDefault
                                 type={EnumFileExtensions.SQL}
                                 content={model.sql ?? ''}
diff --git a/web/client/src/library/pages/ide/IDE.tsx b/web/client/src/library/pages/ide/IDE.tsx
index 3ba7f110..bc9ad7a8 100644
--- a/web/client/src/library/pages/ide/IDE.tsx
+++ b/web/client/src/library/pages/ide/IDE.tsx
@@ -2,33 +2,18 @@ import React, { useEffect } from 'react'
 import {
   useApiModels,
   useApiFiles,
-  useApiEnvironments,
   useApiPlanRun,
   useApiPlanApply,
   useApiCancelPlan,
 } from '../../../api'
 import { useChannelEvents } from '../../../api/channels'
-import {
-  isArrayEmpty,
-  isFalse,
-  isNil,
-  isNotNil,
-  isObjectNotEmpty,
-  isTrue,
-} from '~/utils'
+import { includes, isArrayEmpty, isFalse, isNil, isNotNil } from '~/utils'
 import { useStoreContext } from '~/context/context'
 import { Outlet, useLocation, useNavigate } from 'react-router-dom'
 import { EnumRoutes } from '~/routes'
 import { type Tests, useStoreProject } from '@context/project'
 import { EnumErrorKey, type ErrorIDE, useIDE } from './context'
-import {
-  type Directory,
-  type Model,
-  type Environments,
-  type EnvironmentsEnvironments,
-  Status,
-  Modules,
-} from '@api/client'
+import { type Directory, type Model, Status, Modules } from '@api/client'
 import { Button } from '@components/button/Button'
 import Container from '@components/container/Container'
 import { useStoreEditor, createLocalFile } from '@context/editor'
@@ -41,12 +26,11 @@ import {
   EnumFileExplorerChange,
   type FileExplorerChange,
 } from '@components/fileExplorer/context'
-import { type PlanOverviewTracker } from '@models/tracker-plan-overview'
-import { type PlanApplyTracker } from '@models/tracker-plan-apply'
-import { type PlanCancelTracker } from '@models/tracker-plan-cancel'
 import { EnumPlanAction, ModelPlanAction } from '@models/plan-action'
 import { useStorePlan } from '@context/plan'
 
+let planActionUpdateTimeoutId: Optional<number>
+
 export default function PageIDE(): JSX.Element {
   const location = useLocation()
   const navigate = useNavigate()
@@ -60,8 +44,8 @@ export default function PageIDE(): JSX.Element {
   const confirmations = useStoreContext(s => s.confirmations)
   const removeConfirmation = useStoreContext(s => s.removeConfirmation)
   const environment = useStoreContext(s => s.environment)
+
   const setModels = useStoreContext(s => s.setModels)
-  const addRemoteEnvironments = useStoreContext(s => s.addRemoteEnvironments)
 
   const planOverview = useStorePlan(s => s.planOverview)
   const planApply = useStorePlan(s => s.planApply)
@@ -95,8 +79,7 @@ export default function PageIDE(): JSX.Element {
   // all pages have access to models and files
   const { refetch: getModels, cancel: cancelRequestModels } = useApiModels()
   const { refetch: getFiles, cancel: cancelRequestFiles } = useApiFiles()
-  const { refetch: getEnvironments, cancel: cancelRequestEnvironments } =
-    useApiEnvironments()
+
   const { isFetching: isFetchingPlanApply } = useApiPlanApply(environment.name)
   const { refetch: cancelPlanOrPlanApply, isFetching: isFetchingPlanCancel } =
     useApiCancelPlan()
@@ -115,15 +98,7 @@ export default function PageIDE(): JSX.Element {
     const channelModels = channel<Model[]>('models', updateModels)
     const channelTests = channel<Tests>('tests', updateTests)
     const channelErrors = channel<ErrorIDE>('errors', displayErrors)
-    const channelPlanOverview = channel<any>(
-      'plan-overview',
-      updatePlanOverviewTracker,
-    )
-    const channelPlanApply = channel<any>('plan-apply', updatePlanApplyTracker)
-    const channelPlanCancel = channel<any>(
-      'plan-cancel',
-      updatePlanCancelTracker,
-    )
+
     const channelFile = channel<{
       changes: Array<{
         change: FileExplorerChange
@@ -226,8 +201,6 @@ export default function PageIDE(): JSX.Element {
       setProject(project)
     })
 
-    void getEnvironments().then(({ data }) => updateEnviroments(data))
-
     void getModels().then(({ data }) => {
       updateModels(data as Model[])
     })
@@ -235,26 +208,19 @@ export default function PageIDE(): JSX.Element {
     channelFormatFile.subscribe()
     channelModels.subscribe()
     channelErrors.subscribe()
-    channelPlanOverview.subscribe()
-    channelPlanApply.subscribe()
     channelFile.subscribe()
-    channelPlanCancel.subscribe()
     channelTests.subscribe()
 
     return () => {
       void cancelRequestModels()
       void cancelRequestFiles()
-      void cancelRequestEnvironments()
       void cancelRequestPlan()
 
       channelFormatFile.unsubscribe()
       channelTests.unsubscribe()
       channelModels.unsubscribe()
       channelErrors.unsubscribe()
-      channelPlanOverview.unsubscribe()
-      channelPlanApply.unsubscribe()
       channelFile.unsubscribe()
-      channelPlanCancel.unsubscribe()
     }
   }, [])
 
@@ -280,30 +246,35 @@ export default function PageIDE(): JSX.Element {
       isFalse(planAction.isRunningTask)
     ) {
       planApply.reset()
+      planCancel.reset()
 
       void planRun()
     }
   }, [models, environment])
 
   useEffect(() => {
-    planOverview.isFetching = isFetchingPlanRun
+    if (isFetchingPlanRun) {
+      planOverview.isFetching = true
 
-    setPlanOverview(planOverview)
-  }, [isFetchingPlanRun])
+      setPlanOverview(planOverview)
+    }
 
-  useEffect(() => {
-    planOverview.isFetching = isFetchingPlanApply
+    if (isFetchingPlanApply) {
+      planApply.isFetching = true
 
-    setPlanApply(planApply)
-  }, [isFetchingPlanApply])
+      setPlanApply(planApply)
+    }
 
-  useEffect(() => {
-    planOverview.isFetching = isFetchingPlanCancel
+    if (isFetchingPlanCancel) {
+      planCancel.isFetching = true
 
-    setPlanCancel(planCancel)
-  }, [isFetchingPlanCancel])
+      setPlanCancel(planCancel)
+    }
+  }, [isFetchingPlanRun, isFetchingPlanApply, isFetchingPlanCancel])
 
   useEffect(() => {
+    clearTimeout(planActionUpdateTimeoutId)
+
     const value = ModelPlanAction.getPlanAction({
       planOverview,
       planApply,
@@ -312,9 +283,31 @@ export default function PageIDE(): JSX.Element {
 
     // This inconsistency can happen when we receive flag from the backend
     // that some task is runninng but not yet recived SSE event with data
-    if (value === EnumPlanAction.Done && planAction.isRunningTask) return
+    if (
+      (value === EnumPlanAction.Done && planAction.isRunningTask) ||
+      value === planAction.value
+    )
+      return
+
+    // Delaying the update of the plan action to avoid flickering
+    planActionUpdateTimeoutId = setTimeout(
+      () => {
+        setPlanAction(new ModelPlanAction({ value }))
 
-    setPlanAction(new ModelPlanAction({ value }))
+        planActionUpdateTimeoutId = undefined
+      },
+      includes(
+        [
+          EnumPlanAction.Running,
+          EnumPlanAction.RunningTask,
+          EnumPlanAction.Cancelling,
+          EnumPlanAction.Applying,
+        ],
+        value,
+      )
+        ? 0
+        : 500,
+    ) as unknown as number
   }, [planOverview, planApply, planCancel])
 
   function updateModels(models?: Model[]): void {
@@ -336,48 +329,6 @@ export default function PageIDE(): JSX.Element {
     setTests(tests)
   }
 
-  function updatePlanOverviewTracker(data: PlanOverviewTracker): void {
-    planOverview.update(data)
-
-    setPlanOverview(planOverview)
-  }
-
-  function updatePlanCancelTracker(data: PlanCancelTracker): void {
-    planCancel.update(data)
-
-    setPlanCancel(planCancel)
-  }
-
-  function updatePlanApplyTracker(data: PlanApplyTracker): void {
-    planApply.update(data, planOverview)
-
-    const isFinished =
-      isTrue(data.meta?.done) && data.meta?.status !== Status.init
-
-    if (isFinished) {
-      void getEnvironments().then(({ data }) => {
-        void planRun()
-
-        updateEnviroments(data)
-      })
-    }
-
-    setPlanApply(planApply)
-  }
-
-  function updateEnviroments(data: Optional<Environments>): void {
-    const { environments, default_target_environment, pinned_environments } =
-      data ?? {}
-
-    if (isObjectNotEmpty<EnvironmentsEnvironments>(environments)) {
-      addRemoteEnvironments(
-        Object.values(environments),
-        default_target_environment,
-        pinned_environments,
-      )
-    }
-  }
-
   function restoreEditorTabsFromSaved(files: ModelFile[]): void {
     if (isArrayEmpty(storedTabs)) return
 
diff --git a/web/client/src/library/pages/root/Page.tsx b/web/client/src/library/pages/root/Page.tsx
index 90dc20f0..e55aeb9b 100644
--- a/web/client/src/library/pages/root/Page.tsx
+++ b/web/client/src/library/pages/root/Page.tsx
@@ -58,9 +58,10 @@ import {
 import { Menu, Transition, Popover } from '@headlessui/react'
 import { type ModelEnvironment } from '@models/environment'
 import { type ModelSQLMeshChangeDisplay } from '@models/sqlmesh-change-display'
-import { Fragment, useState, type MouseEvent } from 'react'
+import { Fragment, useState, type MouseEvent, useEffect } from 'react'
 import Input from '@components/input/Input'
 import ReportErrors from '@components/report/ReportErrors'
+import { ModelPlanOverviewTracker } from '@models/tracker-plan-overview'
 
 export default function Page({
   sidebar,
@@ -154,81 +155,73 @@ function EnvironmentDetails(): JSX.Element {
       ) : (
         <>
           {withPlanModule && (
-            <>
-              {planAction.isProcessing ? (
-                <ModuleLink
-                  title={planAction.displayStatus(planOverview)}
-                  to={EnumRoutes.Plan}
-                  icon={
-                    <Spinner
-                      variant={EnumVariant.Success}
-                      className="w-3"
-                    />
-                  }
-                  iconActive={
-                    <Spinner
-                      variant={EnumVariant.Success}
-                      className="w-3"
-                    />
-                  }
-                  before={
-                    <p className="block mx-1 text-xs text-success-100 font-bold">
-                      {planAction.displayStatus(planOverview)}
-                    </p>
-                  }
-                  className="px-2 py-1 bg-success-500"
-                  classActive="bg-success-500"
-                />
-              ) : (
-                <ModuleLink
-                  title="Plan"
-                  to={EnumRoutes.Plan}
-                  icon={
-                    <OutlinePlayCircleIcon
-                      className={clsx(
-                        'w-5',
-                        planOverview.isFailed
-                          ? 'text-danger-500 dark:text-danger-100'
-                          : 'text-success-100',
-                      )}
-                    />
-                  }
-                  iconActive={
-                    <PlayCircleIcon
-                      className={clsx(
-                        'w-5',
-                        planOverview.isFailed
-                          ? 'text-danger-500 dark:text-danger-100'
-                          : 'text-success-100',
-                      )}
-                    />
-                  }
-                  before={
-                    <p
-                      className={clsx(
-                        'block mx-1 text-xs font-bold',
-                        planOverview.isFailed
-                          ? 'text-danger-500 dark:text-danger-100'
-                          : 'text-success-100',
-                      )}
-                    >
-                      Plan
-                    </p>
-                  }
+            <ModuleLink
+              title={
+                planAction.isProcessing
+                  ? planAction.displayStatus(planOverview)
+                  : 'Plan'
+              }
+              to={EnumRoutes.Plan}
+              icon={
+                planAction.isProcessing ? (
+                  <Spinner
+                    variant={EnumVariant.Success}
+                    className="w-3 py-1"
+                  />
+                ) : (
+                  <OutlinePlayCircleIcon
+                    className={clsx(
+                      'w-5',
+                      planOverview.isFailed
+                        ? 'text-danger-500 dark:text-danger-100'
+                        : 'text-success-100',
+                    )}
+                  />
+                )
+              }
+              iconActive={
+                planAction.isProcessing ? (
+                  <Spinner
+                    variant={EnumVariant.Success}
+                    className="w-3 py-1"
+                  />
+                ) : (
+                  <PlayCircleIcon
+                    className={clsx(
+                      'w-5',
+                      planOverview.isFailed
+                        ? 'text-danger-500 dark:text-danger-100'
+                        : 'text-success-100',
+                    )}
+                  />
+                )
+              }
+              before={
+                <p
                   className={clsx(
-                    'px-2',
-                    planOverview.isFailed
-                      ? 'bg-danger-10 dark:bg-danger-500'
-                      : 'bg-success-500',
-                  )}
-                  classActive={clsx(
+                    'block mx-1 text-xs font-bold',
                     planOverview.isFailed
-                      ? 'bg-danger-10 dark:bg-danger-500'
-                      : 'bg-success-500',
+                      ? 'text-danger-500 dark:text-danger-100'
+                      : 'text-success-100',
                   )}
-                />
+                >
+                  {planAction.isProcessing
+                    ? planAction.displayStatus(planOverview)
+                    : 'Plan'}
+                </p>
+              }
+              className={clsx(
+                'px-2',
+                planOverview.isFailed
+                  ? 'bg-danger-10 dark:bg-danger-500'
+                  : 'bg-success-500',
               )}
-            </>
+              classActive={clsx(
+                planOverview.isFailed
+                  ? 'bg-danger-10 dark:bg-danger-500'
+                  : 'bg-success-500',
+              )}
+            />
           )}
           <SelectEnvironemnt
             className="border-none h-6 "
@@ -240,8 +233,9 @@ function EnvironmentDetails(): JSX.Element {
               environment.isInitialProd
             }
           />
-          {isFalse(planOverview.isLatest) &&
-            isTrue(planOverview.hasChanges) && <PlanChanges />}
+          {[Modules['plan-progress'], Modules.plans].some(m =>
+            modules.includes(m),
+          ) && <PlanChanges />}
           {modules.includes(Modules.errors) && <ReportErrors />}
         </>
       )}
@@ -383,62 +377,79 @@ function ActionStatus({
 function PlanChanges(): JSX.Element {
   const planOverview = useStorePlan(s => s.planOverview)
 
-  return (
+  const [planOverviewTracker, setPlanOverviewTracker] =
+    useState<ModelPlanOverviewTracker>(planOverview)
+
+  useEffect(() => {
+    if (isFalse(planOverview.isEmpty) && planOverview.isFinished) {
+      setPlanOverviewTracker(new ModelPlanOverviewTracker(planOverview))
+    }
+  }, [planOverview])
+
+  const shouldShow =
+    (isFalse(planOverview.isLatest) || planOverview.isFetching) &&
+    planOverviewTracker.hasUpdates
+
+  return shouldShow ? (
     <span className="flex group items-center bg-neutral-10 px-1 py-1 rounded-full">
-      {isTrue(planOverview.hasChanges) && (
+      {isTrue(planOverviewTracker.hasChanges) && (
         <p className="flex text-xs ml-2 mr-2 dark:text-neutral-300">Changes</p>
       )}
-      {isArrayNotEmpty(planOverview.added) && (
+      {isArrayNotEmpty(planOverviewTracker.added) && (
         <ChangesPreview
           headline="Added"
           type={EnumPlanChangeType.Add}
-          changes={planOverview.added}
+          changes={planOverviewTracker.added}
           className="-mr-2 group-hover:mr-0 z-[5]"
         />
       )}
-      {isArrayNotEmpty(planOverview.direct) && (
+      {isArrayNotEmpty(planOverviewTracker.direct) && (
         <ChangesPreview
           headline="Directly Modified"
           type={EnumPlanChangeType.Direct}
-          changes={planOverview.direct}
+          changes={planOverviewTracker.direct}
           className="-mr-2 group-hover:mr-0 z-[4]"
         />
       )}
-      {isArrayNotEmpty(planOverview.indirect) && (
+      {isArrayNotEmpty(planOverviewTracker.indirect) && (
         <ChangesPreview
           headline="Indirectly Modified"
           type={EnumPlanChangeType.Indirect}
-          changes={planOverview.indirect}
+          changes={planOverviewTracker.indirect}
           className="-mr-2 group-hover:mr-0 z-[3]"
         />
       )}
-      {isArrayNotEmpty(planOverview.metadata) && (
+      {isArrayNotEmpty(planOverviewTracker.metadata) && (
         <ChangesPreview
           headline="Metadata"
           type={EnumPlanChangeType.Metadata}
-          changes={planOverview.metadata}
+          changes={planOverviewTracker.metadata}
           className="-mr-2 group-hover:mr-0 z-[2]"
         />
       )}
-      {isArrayNotEmpty(planOverview.removed) && (
+      {isArrayNotEmpty(planOverviewTracker.removed) && (
         <ChangesPreview
           headline="Removed"
           type={EnumPlanChangeType.Remove}
-          changes={planOverview.removed}
+          changes={planOverviewTracker.removed}
           className="-mr-2 group-hover:mr-0 z-[1]"
         />
       )}
-      {isArrayNotEmpty(planOverview.backfills) ? (
+      {isArrayNotEmpty(planOverviewTracker.backfills) ? (
         <ChangesPreview
           headline="Backfills"
           type={EnumPlanChangeType.Default}
-          changes={planOverview.backfills}
+          changes={planOverviewTracker.backfills}
           className="ml-4 group-hover:ml-2 z-[6]"
         />
       ) : (
         <div className="ml-2 group-hover:ml-2 z-[6]"></div>
       )}
     </span>
+  ) : (
+    <span className="block ml-1 px-3 py-1 first-child:ml-0 rounded-full whitespace-nowrap font-bold text-xs text-center bg-neutral-5 dark:bg-neutral-20 cursor-default text-neutral-500 dark:text-neutral-300">
+      No Changes
+    </span>
   )
 }
 
diff --git a/web/client/src/models/plan-action.ts b/web/client/src/models/plan-action.ts
index a0fbddd6..c4af976e 100644
--- a/web/client/src/models/plan-action.ts
+++ b/web/client/src/models/plan-action.ts
@@ -106,13 +106,14 @@ export class ModelPlanAction<
   }
 
   displayStatus(planOverview: ModelPlanOverviewTracker): string {
-    if (this.isRunningTask) return 'Running Task...'
+    if (this.isCancelling) return 'Cancelling Plan...'
     if (this.isApplying) return 'Applying Plan...'
     if (this.isRunning && isNil(planOverview.hasChanges))
       return 'Getting Changes...'
     if (this.isRunning && isNil(planOverview.hasBackfills))
       return 'Getting Backfills...'
     if (this.isRunning) return 'Checking Plan...'
+    if (this.isRunningTask) return 'Running Task...'
 
     return 'Plan'
   }
@@ -177,10 +178,13 @@ export class ModelPlanAction<
     const isRunningPlan = planOverview.isRunning
     const isRunningApply = planApply.isRunning
     const isRunningCancel = planCancel.isRunning
+    const isFinished =
+      (planApply.isFinished && planOverview.isFinished) || planCancel.isFinished
 
-    if (isRunningPlan) return EnumPlanAction.Running
     if (isRunningCancel) return EnumPlanAction.Cancelling
     if (isRunningApply) return EnumPlanAction.Applying
+    if (isRunningPlan) return EnumPlanAction.Running
+    if (isFinished) return EnumPlanAction.Done
     if (planOverview.isVirtualUpdate) return EnumPlanAction.ApplyVirtual
     if (planOverview.isMetadataUpdate) return EnumPlanAction.ApplyMetadata
     if (planOverview.isBackfillUpdate) return EnumPlanAction.ApplyBackfill
diff --git a/web/client/src/models/tracker-plan-cancel.ts b/web/client/src/models/tracker-plan-cancel.ts
index 9098a7e5..d49f5365 100644
--- a/web/client/src/models/tracker-plan-cancel.ts
+++ b/web/client/src/models/tracker-plan-cancel.ts
@@ -13,8 +13,9 @@ export class ModelPlanCancelTracker extends ModelPlanTracker<PlanCancelTracker>
 
   get isCancelling(): boolean {
     return (
-      isFalseOrNil(this._current?.cancel?.meta?.done) &&
-      this._current?.cancel?.meta?.status === Status.init
+      (isFalseOrNil(this.current?.meta?.done) &&
+        this.current?.meta?.status === Status.init) ||
+      this.isFetching
     )
   }
 
diff --git a/web/client/src/models/tracker-plan-overview.ts b/web/client/src/models/tracker-plan-overview.ts
index 5fe8fca7..2ce1cb0d 100644
--- a/web/client/src/models/tracker-plan-overview.ts
+++ b/web/client/src/models/tracker-plan-overview.ts
@@ -4,7 +4,7 @@ import {
   type PlanStageChanges,
 } from '@api/client'
 import { ModelPlanTracker, type PlanTracker } from './tracker-plan'
-import { isArrayNotEmpty, isFalse, isNil, isNotNil } from '@utils/index'
+import { isArrayNotEmpty, isFalse, isNil, isNotNil, isTrue } from '@utils/index'
 import {
   type InitialChangeDisplay,
   ModelSQLMeshChangeDisplay,
@@ -82,6 +82,10 @@ export class ModelPlanOverviewTracker
       : isArrayNotEmpty(this._current.backfills?.models)
   }
 
+  get hasUpdates(): boolean {
+    return isTrue(this.hasChanges) || isTrue(this.hasBackfills)
+  }
+
   get backfills(): ModelSQLMeshChangeDisplay[] {
     return this._backfills
   }
diff --git a/web/server/api/endpoints/commands.py b/web/server/api/endpoints/commands.py
index 6127aade..a059bf48 100644
--- a/web/server/api/endpoints/commands.py
+++ b/web/server/api/endpoints/commands.py
@@ -198,19 +198,19 @@ def _run_plan_apply(
 ) -> None:
     """Run plan apply"""
     plan_options = plan_options or models.PlanOptions()
-
+    tracker_apply = models.PlanApplyStageTracker(environment=environment, plan_options=plan_options)
+    api_console.start_plan_tracker(tracker_apply)
     plan_builder = get_plan_builder(context, plan_options, environment, plan_dates)
     plan = plan_builder.build()
+    tracker_apply.start = plan.start
+    tracker_apply.end = plan.end
+    api_console.start_plan_tracker(tracker_apply)
 
     if categories is not None:
         for new, _ in plan.context_diff.modified_snapshots.values():
             if plan.is_new_snapshot(new) and new.name in categories:
                 plan_builder.set_choice(new, categories[new.name])
         plan = plan_builder.build()
-
-    tracker_apply = models.PlanApplyStageTracker(environment=environment, plan_options=plan_options)
-    tracker_apply.start = plan.start
-    tracker_apply.end = plan.end
     api_console.start_plan_tracker(tracker_apply)
     try:
         context.apply(plan, circuit_breaker)
diff --git a/web/server/api/endpoints/plan.py b/web/server/api/endpoints/plan.py
index cfd93eb6..2a467128 100644
--- a/web/server/api/endpoints/plan.py
+++ b/web/server/api/endpoints/plan.py
@@ -62,16 +62,17 @@ async def cancel_plan(
             origin="API -> plan -> cancel_plan",
         )
 
+    tracker = models.PlanCancelStageTracker()
+    api_console.start_plan_tracker(tracker)
+    tracker_stage_cancel = models.PlanStageCancel()
+    tracker.add_stage(stage=models.PlanStage.cancel, data=tracker_stage_cancel)
     request.app.state.circuit_breaker.set()
     request.app.state.task.cancel()
     try:
         await request.app.state.task
     except asyncio.CancelledError:
         pass
-    tracker = models.PlanCancelStageTracker()
-    api_console.start_plan_tracker(tracker)
-    tracker_stage_cancel = models.PlanStageCancel()
-    tracker.add_stage(stage=models.PlanStage.cancel, data=tracker_stage_cancel)
+
     tracker_stage_cancel.stop(success=True)
     api_console.stop_plan_tracker(tracker)
     response.status_code = HTTP_204_NO_CONTENT
diff --git a/web/server/console.py b/web/server/console.py
index 9485f5e1..c4cb06e2 100644
--- a/web/server/console.py
+++ b/web/server/console.py
@@ -201,13 +201,14 @@ class ApiConsole(TerminalConsole):
             self.plan_apply_stage_tracker.stop(success=success)
             self.log_event_plan_apply()
             self.plan_apply_stage_tracker = None
+            self.plan_overview_stage_tracker = None
         elif (
             isinstance(tracker, models.PlanOverviewStageTracker)
             and self.plan_overview_stage_tracker
         ):
             self.plan_overview_stage_tracker.stop(success=success)
             self.log_event_plan_overview()
-            self.plan_overview_stage_tracker = None
+            self.plan_apply_stage_tracker = None
         elif isinstance(tracker, models.PlanCancelStageTracker) and self.plan_cancel_stage_tracker:
             self.plan_cancel_stage_tracker.stop(success=success)
             self.log_event_plan_cancel()
@@ -267,9 +268,9 @@ class ApiConsole(TerminalConsole):
     def log_event_plan_overview(self) -> None:
         self.log_event(
             event=models.EventName.PLAN_OVERVIEW,
-            data=self.plan_overview_stage_tracker.dict()
-            if self.plan_overview_stage_tracker
-            else {},
+            data=(
+                self.plan_overview_stage_tracker.dict() if self.plan_overview_stage_tracker else {}
+            ),
         )
 
     def log_event_plan_cancel(self) -> None:


Test patch:
diff --git a/sqlmesh/core/test/result.py b/sqlmesh/core/test/result.py
index 18a3ea3c..5a8da783 100644
--- a/sqlmesh/core/test/result.py
+++ b/sqlmesh/core/test/result.py
@@ -8,8 +8,9 @@ class ModelTextTestResult(unittest.TextTestResult):
     def addFailure(
         self,
         test: unittest.TestCase,
-        err: tuple[type[BaseException], BaseException, types.TracebackType]
-        | tuple[None, None, None],
+        err: (
+            tuple[type[BaseException], BaseException, types.TracebackType] | tuple[None, None, None]
+        ),
     ) -> None:
         """Called when the test case test signals a failure.
 
diff --git a/tests/core/engine_adapter/test_integration.py b/tests/core/engine_adapter/test_integration.py
index c5f0a9b6..6a149bee 100644
--- a/tests/core/engine_adapter/test_integration.py
+++ b/tests/core/engine_adapter/test_integration.py
@@ -861,9 +861,9 @@ def test_replace_query(ctx: TestContext):
         ctx.engine_adapter.replace_query(
             table,
             ctx.input_data(replace_data),
-            columns_to_types=ctx.columns_to_types
-            if ctx.dialect in ["spark", "databricks"]
-            else None,
+            columns_to_types=(
+                ctx.columns_to_types if ctx.dialect in ["spark", "databricks"] else None
+            ),
         )
         results = ctx.get_metadata_results()
         assert len(results.views) == 0
@@ -914,9 +914,9 @@ def test_replace_query_batched(ctx: TestContext):
         ctx.engine_adapter.replace_query(
             table,
             ctx.input_data(replace_data),
-            columns_to_types=ctx.columns_to_types
-            if ctx.dialect in ["spark", "databricks"]
-            else None,
+            columns_to_types=(
+                ctx.columns_to_types if ctx.dialect in ["spark", "databricks"] else None
+            ),
         )
         results = ctx.get_metadata_results()
         assert len(results.views) == 0
@@ -1491,9 +1491,7 @@ def test_sushi(ctx: TestContext):
         ) -> None:
             layer_objects = context.engine_adapter.get_data_objects(schema_name)
             layer_models = {
-                x.name.split("__")[1]
-                if is_physical_layer
-                else x.name: {
+                x.name.split("__")[1] if is_physical_layer else x.name: {
                     "table_name": x.name,
                     "is_view": x.type == DataObjectType.VIEW,
                 }
@@ -1572,9 +1570,7 @@ def test_sushi(ctx: TestContext):
         ) -> None:
             layer_objects = context.engine_adapter.get_data_objects(schema_name)
             layer_models = {
-                x.name.split("__")[1]
-                if is_physical_layer
-                else x.name: {
+                x.name.split("__")[1] if is_physical_layer else x.name: {
                     "table_name": x.name,
                     "is_view": x.type == DataObjectType.VIEW,
                 }
diff --git a/tests/core/test_scheduler.py b/tests/core/test_scheduler.py
index 47d40e93..5b548d49 100644
--- a/tests/core/test_scheduler.py
+++ b/tests/core/test_scheduler.py
@@ -199,7 +199,10 @@ def test_incremental_time_self_reference_dag(mocker: MockerFixture, make_snapsho
                 ((to_datetime("2023-01-04"), to_datetime("2023-01-05")), 2),
             ),
         },
-        (incremental_self_snapshot.name, ((to_datetime(0), to_datetime(0)), -1),): set(
+        (
+            incremental_self_snapshot.name,
+            ((to_datetime(0), to_datetime(0)), -1),
+        ): set(
             [
                 (
                     incremental_self_snapshot.name,
diff --git a/tests/core/test_selector.py b/tests/core/test_selector.py
index 032fcca6..c1ee08ff 100644
--- a/tests/core/test_selector.py
+++ b/tests/core/test_selector.py
@@ -339,7 +339,7 @@ def test_select_models_missing_env(mocker: MockerFixture, make_snapshot):
 )
 def test_expand_model_tags(mocker: MockerFixture, make_snapshot, tags, tag_selections, output):
     models: UniqueKeyDict[str, Model] = UniqueKeyDict("models")
-    for (model_name, tag, depends_on) in tags:
+    for model_name, tag, depends_on in tags:
         model = SqlModel(
             name=model_name, query=d.parse_one("SELECT 1 AS a"), depends_on=depends_on, tags=[tag]
         )
diff --git a/tests/integrations/github/cicd/fixtures.py b/tests/integrations/github/cicd/fixtures.py
index 806bb6c4..056bbdba 100644
--- a/tests/integrations/github/cicd/fixtures.py
+++ b/tests/integrations/github/cicd/fixtures.py
@@ -83,9 +83,11 @@ def make_controller(mocker: MockerFixture) -> t.Callable:
         return GithubController(
             paths=["examples/sushi"],
             token="abc",
-            event=GithubEvent.from_path(event_path)
-            if isinstance(event_path, str)
-            else GithubEvent.from_obj(event_path),
+            event=(
+                GithubEvent.from_path(event_path)
+                if isinstance(event_path, str)
+                else GithubEvent.from_obj(event_path)
+            ),
             client=client,
         )
 
diff --git a/tests/integrations/github/cicd/test_integration.py b/tests/integrations/github/cicd/test_integration.py
index 2ce495db..db230804 100644
--- a/tests/integrations/github/cicd/test_integration.py
+++ b/tests/integrations/github/cicd/test_integration.py
@@ -2,6 +2,7 @@
 These integration tests are for testing integrating with SQLMesh and not integrating with Github.
 Therefore Github calls are still mocked but context is fully evaluated.
 """
+
 import os
 import pathlib
 import typing as t
diff --git a/tests/schedulers/airflow/test_plan.py b/tests/schedulers/airflow/test_plan.py
index ed9be085..337d476f 100644
--- a/tests/schedulers/airflow/test_plan.py
+++ b/tests/schedulers/airflow/test_plan.py
@@ -172,9 +172,11 @@ def test_create_plan_dag_spec(
         forward_only=True,
         dag_start_ts=to_timestamp("2023-01-01"),
         no_gaps_snapshot_names=expected_no_gaps_snapshot_names,
-        deployability_index_for_creation=DeployabilityIndex.all_deployable()
-        if not paused_forward_only
-        else DeployabilityIndex.none_deployable(),
+        deployability_index_for_creation=(
+            DeployabilityIndex.all_deployable()
+            if not paused_forward_only
+            else DeployabilityIndex.none_deployable()
+        ),
     )
 
     state_sync_mock.get_snapshots.assert_called_once()