Repository: sktime/sktime
Pull request #7917 (2025-03-02 14:57:21)

[ENH] start rename of `handles-missing-data` tag to `capability:missing_values`
This PR starts the deprecation cycle to rename the `handles-missing-data` tag to `capability:missing_values`, for 1.0.0.

The tag `capability:missing_values` is already used throughout the code base, except forecasters which use the name `handles-missing-data`.

This PR contains:

* update of the `TagAliaserMixin` to use the `scikit-base` copy which is identical in code
* adding the `handles-missing-data` deprecation to the `TagAliaserMixin`
* renaming all instances of `handles-missing-data` in the code base to `capability:missing_values`
* adding release manager instructions for the 1.0.0 release

fkiraly: The failures seem to be the same as in `test-all`, not related to this PR

Patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
index b2e0ff174..3da02c57e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -350,7 +350,7 @@ def _make_estimator_overview(app):
             "capability:insample",
             "capability:pred_int",
             "capability:pred_int:insample",
-            "handles-missing-data",
+            "capability:missing_values",
             "ignores-exogeneous-X",
             "scitype:y",
             "requires-fh-in-fit",
@@ -364,7 +364,7 @@ def _make_estimator_overview(app):
             "scitype:transform-output",
             "scitype:transform-labels",
             "capability:inverse_transform",
-            "handles-missing-data",
+            "capability:missing_values",
             "capability:missing_values:removes",
             "capability:unequal_length",
             "capability:unequal_length:removes",
diff --git a/extension_templates/forecasting.py b/extension_templates/forecasting.py
index 4686cfdeb..1c1c6d97c 100644
--- a/extension_templates/forecasting.py
+++ b/extension_templates/forecasting.py
@@ -130,7 +130,7 @@ class MyForecaster(BaseForecaster):
         # if not None, raises exception if X.index, y.index level -1 is not of that type
         #
         # handles-missing-data = can estimator handle missing data?
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         # valid values: boolean True (yes), False (no)
         # if False, raises exception if y or X passed contain missing data (nans)
         #
@@ -225,7 +225,7 @@ class MyForecaster(BaseForecaster):
         # if est.foo == 42:
         #   self.set_tags(handles-missing-data=True)
         # example 2: cloning tags from component
-        #   self.clone_tags(est2, ["enforce_index_type", "handles-missing-data"])
+        #   self.clone_tags(est2, ["enforce_index_type", "capability:missing_values"])
 
     # todo: implement this, mandatory
     def _fit(self, y, X, fh):
diff --git a/extension_templates/transformer.py b/extension_templates/transformer.py
index 13ca7dc9a..2ae257c86 100644
--- a/extension_templates/transformer.py
+++ b/extension_templates/transformer.py
@@ -233,7 +233,7 @@ class MyTransformer(BaseTransformer):
         # used for search index and validity checking, does not raise direct exception
         #
         # handles-missing-data = can the transformer handle missing data (np or pd.NA)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         # valid values: boolean True (yes), False (no)
         # if False, may raise exception when passed time series with missing values
         #
@@ -319,7 +319,7 @@ class MyTransformer(BaseTransformer):
         # if est.foo == 42:
         #   self.set_tags(handles-missing-data=True)
         # example 2: cloning tags from component
-        #   self.clone_tags(est2, ["enforce_index_type", "handles-missing-data"])
+        #   self.clone_tags(est2, ["enforce_index_type", "capability:missing_values"])
 
     # todo: implement this, mandatory (except in special case below)
     def _fit(self, X, y=None):
diff --git a/extension_templates/transformer_simple.py b/extension_templates/transformer_simple.py
index 7074b19dd..511ad8311 100644
--- a/extension_templates/transformer_simple.py
+++ b/extension_templates/transformer_simple.py
@@ -171,7 +171,7 @@ class MyTransformer(BaseTransformer):
         # if False, may raise exception when passed unequal length Panel/Hierarchical
         #
         # handles-missing-data = can the transformer handle missing data (np or pd.NA)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         # valid values: boolean True (yes), False (no)
         # if False, may raise exception when passed time series with missing values
         #
diff --git a/extension_templates/transformer_supersimple.py b/extension_templates/transformer_supersimple.py
index 400f53f95..eebad2a91 100644
--- a/extension_templates/transformer_supersimple.py
+++ b/extension_templates/transformer_supersimple.py
@@ -95,7 +95,7 @@ class MyTransformer(BaseTransformer):
         "fit_is_empty": False,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     # todo: add any hyper-parameters and components to constructor
diff --git a/sktime/base/_base.py b/sktime/base/_base.py
index 20ccdb30e..876ecac53 100644
--- a/sktime/base/_base.py
+++ b/sktime/base/_base.py
@@ -56,11 +56,9 @@ State:
 __author__ = ["mloning", "RNKuhns", "fkiraly"]
 __all__ = ["BaseEstimator", "BaseObject"]
 
-import warnings
-from copy import deepcopy
-
 from skbase.base import BaseEstimator as _BaseEstimator
 from skbase.base import BaseObject as _BaseObject
+from skbase.base._base import TagAliaserMixin as _TagAliaserMixin
 from sklearn import clone
 from sklearn.base import BaseEstimator as _SklearnBaseEstimator
 
@@ -343,16 +341,22 @@ class BaseObject(_HTMLDocumentationLinkMixin, _BaseObject):
             return pickle.loads(file.open("_obj").read())
 
 
-class TagAliaserMixin:
+class TagAliaserMixin(_TagAliaserMixin):
     """Mixin class for tag aliasing and deprecation of old tags.
 
-    To deprecate tags, add the TagAliaserMixin to BaseObject or BaseEstimator.
-    alias_dict contains the deprecated tags, and supports removal and renaming.     For
-    removal, add an entry "old_tag_name": ""     For renaming, add an entry
-    "old_tag_name": "new_tag_name" deprecate_dict contains the version number of
-    renaming or removal.     the keys in deprecate_dict should be the same as in
-    alias_dict.     values in deprecate_dict should be strings, the version of
-    removal/renaming.
+    To deprecate tags, add the ``TagAliaserMixin`` to ``BaseObject``
+    or ``BaseEstimator``.
+
+    ``alias_dict`` contains the deprecated tags, and supports removal and renaming.
+
+    * For removal, add an entry ``"old_tag_name": ""``
+    * For renaming, add an entry ``"old_tag_name": "new_tag_name"``
+
+    ``deprecate_dict`` contains the version number of renaming or removal.
+
+    * The keys in ``deprecate_dict`` should be the same as in alias_dict.
+    * Values in ``deprecate_dict`` should be strings, the version of
+    removal/renaming, in PEP 440 format, e.g., ``"1.0.0"``.
 
     The class will ensure that new tags alias old tags and vice versa, during the
     deprecation period. Informative warnings will be raised whenever the deprecated tags
@@ -360,167 +364,14 @@ class TagAliaserMixin:
 
     When removing tags, ensure to remove the removed tags from this class. If no tags
     are deprecated anymore (e.g., all deprecated tags are removed/renamed), ensure
-    toremove this class as a parent of BaseObject or BaseEstimator.
+    to remove this class as a parent of ``BaseObject`` or ``BaseEstimator``.
     """
 
-    def __init__(self):
-        super().__init__()
-
-    @classmethod
-    def get_class_tags(cls):
-        """Get class tags from estimator class and all its parent classes.
-
-        Returns
-        -------
-        collected_tags : dict
-            Dictionary of tag name : tag value pairs. Collected from _tags
-            class attribute via nested inheritance. NOT overridden by dynamic
-            tags set by set_tags or mirror_tags.
-        """
-        collected_tags = super().get_class_tags()
-        collected_tags = cls._complete_dict(collected_tags)
-        return collected_tags
-
-    @classmethod
-    def get_class_tag(cls, tag_name, tag_value_default=None):
-        """Get tag value from estimator class (only class tags).
-
-        Parameters
-        ----------
-        tag_name : str
-            Name of tag value.
-        tag_value_default : any type
-            Default/fallback value if tag is not found.
-
-        Returns
-        -------
-        tag_value :
-            Value of the ``tag_name`` tag in self. If not found, returns
-            ``tag_value_default``.
-        """
-        cls._deprecate_tag_warn([tag_name])
-        return super().get_class_tag(
-            tag_name=tag_name, tag_value_default=tag_value_default
-        )
-
-    def get_tags(self):
-        """Get tags from estimator class and dynamic tag overrides.
-
-        Returns
-        -------
-        collected_tags : dict
-            Dictionary of tag name : tag value pairs. Collected from _tags
-            class attribute via nested inheritance and then any overrides
-            and new tags from _tags_dynamic object attribute.
-        """
-        collected_tags = super().get_tags()
-        collected_tags = self._complete_dict(collected_tags)
-        return collected_tags
-
-    def get_tag(self, tag_name, tag_value_default=None, raise_error=True):
-        """Get tag value from estimator class and dynamic tag overrides.
+    alias_dict = {"handles-missing-data": "capability:missing_values"}
+    deprecate_dict = {"handles-missing-data": "1.0.0"}
 
-        Parameters
-        ----------
-        tag_name : str
-            Name of tag to be retrieved
-        tag_value_default : any type, optional; default=None
-            Default/fallback value if tag is not found
-        raise_error : bool
-            whether a ValueError is raised when the tag is not found
-
-        Returns
-        -------
-        tag_value :
-            Value of the ``tag_name`` tag in self. If not found, returns an error if
-            raise_error is True, otherwise it returns ``tag_value_default``.
-
-        Raises
-        ------
-        ValueError if raise_error is True i.e. if tag_name is not in self.get_tags(
-        ).keys()
-        """
-        self._deprecate_tag_warn([tag_name])
-        return super().get_tag(
-            tag_name=tag_name,
-            tag_value_default=tag_value_default,
-            raise_error=raise_error,
-        )
-
-    def set_tags(self, **tag_dict):
-        """Set dynamic tags to given values.
-
-        Parameters
-        ----------
-        tag_dict : dict
-            Dictionary of tag name : tag value pairs.
-
-        Returns
-        -------
-        Self :
-            Reference to self.
 
-        Notes
-        -----
-        Changes object state by setting tag values in tag_dict as dynamic tags
-        in self.
-        """
-        self._deprecate_tag_warn(tag_dict.keys())
-
-        tag_dict = self._complete_dict(tag_dict)
-        super().set_tags(**tag_dict)
-        return self
-
-    @classmethod
-    def _complete_dict(cls, tag_dict):
-        """Add all aliased and aliasing tags to the dictionary."""
-        alias_dict = cls.alias_dict
-        deprecated_tags = set(tag_dict.keys()).intersection(alias_dict.keys())
-        new_tags = set(tag_dict.keys()).intersection(alias_dict.values())
-
-        if len(deprecated_tags) > 0 or len(new_tags) > 0:
-            new_tag_dict = deepcopy(tag_dict)
-            # for all tag strings being set, write the value
-            #   to all tags that could *be aliased by* the string
-            #   and all tags that could be *aliasing* the string
-            # this way we ensure upwards and downwards compatibility
-            for old_tag, new_tag in alias_dict.items():
-                for tag in tag_dict:
-                    if tag == old_tag and new_tag != "":
-                        new_tag_dict[new_tag] = tag_dict[tag]
-                    if tag == new_tag:
-                        new_tag_dict[old_tag] = tag_dict[tag]
-            return new_tag_dict
-        else:
-            return tag_dict
-
-    @classmethod
-    def _deprecate_tag_warn(cls, tags):
-        """Print warning message for tag deprecation.
-
-        Parameters
-        ----------
-        tags : list of str
-
-        Raises
-        ------
-        DeprecationWarning for each tag in tags that is aliased by cls.alias_dict
-        """
-        for tag_name in tags:
-            if tag_name in cls.alias_dict.keys():
-                version = cls.deprecate_dict[tag_name]
-                new_tag = cls.alias_dict[tag_name]
-                msg = f"tag {tag_name!r} will be removed in sktime version {version}"
-                if new_tag != "":
-                    msg += (
-                        f" and replaced by {new_tag!r}, please use {new_tag!r} instead"
-                    )
-                else:
-                    msg += ', please remove code that access or sets "{tag_name}"'
-                warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-
-
-class BaseEstimator(_BaseEstimator, BaseObject):
+class BaseEstimator(TagAliaserMixin, _BaseEstimator, BaseObject):
     """Base class for defining estimators in sktime.
 
     Extends sktime's BaseObject to include basic functionality for fittable estimators.
diff --git a/sktime/classification/compose/_pipeline.py b/sktime/classification/compose/_pipeline.py
index 3ba8422ed..57bf39c21 100644
--- a/sktime/classification/compose/_pipeline.py
+++ b/sktime/classification/compose/_pipeline.py
@@ -126,7 +126,9 @@ class ClassifierPipeline(_HeterogenousMetaEstimator, BaseClassifier):
         # can handle missing values iff: both classifier and all transformers can,
         #   *or* transformer chain removes missing data
         missing = classifier.get_tag("capability:missing_values", False)
-        missing = missing and self.transformers_.get_tag("handles-missing-data", False)
+        missing = missing and self.transformers_.get_tag(
+            "capability:missing_values", False
+        )
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
@@ -457,7 +459,7 @@ class SklearnClassifierPipeline(_HeterogenousMetaEstimator, BaseClassifier):
         # can handle missing values iff transformer chain removes missing data
         # sklearn classifiers might be able to handle missing data (but no tag there)
         # so better set the tag liberally
-        missing = self.transformers_.get_tag("handles-missing-data", False)
+        missing = self.transformers_.get_tag("capability:missing_values", False)
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
diff --git a/sktime/clustering/compose/_as_transform.py b/sktime/clustering/compose/_as_transform.py
index 03e11dd42..01ff50549 100644
--- a/sktime/clustering/compose/_as_transform.py
+++ b/sktime/clustering/compose/_as_transform.py
@@ -67,7 +67,7 @@ class ClustererAsTransformer(BaseTransformer):
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "capability:unequal_length": True,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:missing_values:removes": True,
     }
 
@@ -91,7 +91,7 @@ class ClustererAsTransformer(BaseTransformer):
         # forward tag information
         tags_to_set = {
             "univariate-only": not multivariate,
-            "handles-missing-data": missing,
+            "capability:missing_values": missing,
             "capability:unequal_length": unequal,
         }
         self.set_tags(**tags_to_set)
diff --git a/sktime/clustering/compose/_pipeline.py b/sktime/clustering/compose/_pipeline.py
index e13d5599b..ca57e5597 100644
--- a/sktime/clustering/compose/_pipeline.py
+++ b/sktime/clustering/compose/_pipeline.py
@@ -125,7 +125,9 @@ class ClustererPipeline(_HeterogenousMetaEstimator, BaseClusterer):
         # can handle missing values iff: both clusterer and all transformers can,
         #   *or* transformer chain removes missing data
         missing = clusterer.get_tag("capability:missing_values", False)
-        missing = missing and self.transformers_.get_tag("handles-missing-data", False)
+        missing = missing and self.transformers_.get_tag(
+            "capability:missing_values", False
+        )
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
@@ -465,7 +467,7 @@ class SklearnClustererPipeline(ClustererPipeline):
         # can handle missing values iff transformer chain removes missing data
         # sklearn clusterers might be able to handle missing data (but no tag there)
         # so better set the tag liberally
-        missing = self.transformers_.get_tag("handles-missing-data", False)
+        missing = self.transformers_.get_tag("capability:missing_values", False)
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
diff --git a/sktime/detection/compose/_as_transform.py b/sktime/detection/compose/_as_transform.py
index 9ca02bc2b..0612b6fe7 100644
--- a/sktime/detection/compose/_as_transform.py
+++ b/sktime/detection/compose/_as_transform.py
@@ -68,7 +68,7 @@ class DetectorAsTransformer(BaseTransformer):
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "capability:unequal_length": True,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:missing_values:removes": True,
     }
 
diff --git a/sktime/detection/stray.py b/sktime/detection/stray.py
index c1ee5753e..04b2011c5 100644
--- a/sktime/detection/stray.py
+++ b/sktime/detection/stray.py
@@ -82,7 +82,7 @@ class STRAY(BaseTransformer):
         "maintainers": "KatieBuc",
         # estimator type
         # --------------
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "X_inner_mtype": "np.ndarray",
         "fit_is_empty": False,
         "skip-inverse-transform": True,
diff --git a/sktime/dists_kernels/compose.py b/sktime/dists_kernels/compose.py
index daf605a66..ccb4b97ca 100644
--- a/sktime/dists_kernels/compose.py
+++ b/sktime/dists_kernels/compose.py
@@ -83,7 +83,9 @@ class PwTrafoPanelPipeline(_HeterogenousMetaEstimator, BasePairwiseTransformerPa
         # can handle missing values iff: both classifier and all transformers can,
         #   *or* transformer chain removes missing data
         missing = pw_trafo.get_tag("capability:missing_values", False)
-        missing = missing and self.transformers_.get_tag("handles-missing-data", False)
+        missing = missing and self.transformers_.get_tag(
+            "capability:missing_values", False
+        )
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
diff --git a/sktime/forecasting/adapters/_hcrystalball.py b/sktime/forecasting/adapters/_hcrystalball.py
index 255db18e3..74581aa18 100644
--- a/sktime/forecasting/adapters/_hcrystalball.py
+++ b/sktime/forecasting/adapters/_hcrystalball.py
@@ -114,7 +114,7 @@ class HCrystalBallAdapter(BaseForecaster):
         # --------------
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, model):
diff --git a/sktime/forecasting/arch/_uarch.py b/sktime/forecasting/arch/_uarch.py
index e2cf21a83..ed8909a23 100644
--- a/sktime/forecasting/arch/_uarch.py
+++ b/sktime/forecasting/arch/_uarch.py
@@ -163,7 +163,7 @@ class ARCH(BaseForecaster):
         "y_inner_mtype": "pd.Series",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": True,
         "ignores-exogeneous-X": True,
     }
diff --git a/sktime/forecasting/ardl.py b/sktime/forecasting/ardl.py
index ec71868e4..d80975b07 100644
--- a/sktime/forecasting/ardl.py
+++ b/sktime/forecasting/ardl.py
@@ -204,7 +204,7 @@ class ARDL(_StatsModelsAdapter):
         # --------------
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.Series",  # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for X?
         "requires-fh-in-fit": False,  # is forecasting horizon already required in fit?
diff --git a/sktime/forecasting/arima/_pmdarima.py b/sktime/forecasting/arima/_pmdarima.py
index 4428e2662..1f1b62d4d 100644
--- a/sktime/forecasting/arima/_pmdarima.py
+++ b/sktime/forecasting/arima/_pmdarima.py
@@ -284,7 +284,7 @@ class AutoARIMA(_PmdArimaAdapter):
         # python_dependencies: "pmdarima" - inherited from _PmdArimaAdapter
         # estimator type
         # --------------
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     SARIMAX_KWARGS_KEYS = [
@@ -694,7 +694,7 @@ class ARIMA(_PmdArimaAdapter):
             "ilkersigirci",
         ],
         "maintainers": ["hyang1996"],
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     SARIMAX_KWARGS_KEYS = [
diff --git a/sktime/forecasting/base/_base.py b/sktime/forecasting/base/_base.py
index 4dd25ac36..387675e9e 100644
--- a/sktime/forecasting/base/_base.py
+++ b/sktime/forecasting/base/_base.py
@@ -99,7 +99,7 @@ class BaseForecaster(_PredictProbaMixin, BaseEstimator):
         "capability:insample": True,  # can the estimator make in-sample predictions?
         "capability:pred_int": False,  # can the estimator produce prediction intervals?
         "capability:pred_int:insample": True,  # if yes, also for in-sample horizons?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.Series",  # which types do _fit/_predict, support for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit/_predict, support for X?
         "requires-fh-in-fit": True,  # is forecasting horizon already required in fit?
@@ -1532,12 +1532,12 @@ class BaseForecaster(_PredictProbaMixin, BaseEstimator):
 
         def _check_missing(metadata, obj_name):
             """Check input metadata against self's missing capability tag."""
-            if not self.get_tag("handles-missing-data"):
+            if not self.get_tag("capability:missing_values"):
                 msg = (
                     f"{type(self).__name__} cannot handle missing data (nans), "
                     f"but {obj_name} passed contained missing data."
                 )
-                if self.get_class_tag("handles-missing-data"):
+                if self.get_class_tag("capability:missing_values"):
                     msg = msg + (
                         f" Whether instances of {type(self).__name__} can handle "
                         "missing data depends on parameters of the instance, "
@@ -1567,7 +1567,7 @@ class BaseForecaster(_PredictProbaMixin, BaseEstimator):
             y_metadata_required = ["n_features", "feature_names", "feature_kind"]
             if self.get_tag("scitype:y") != "both":
                 y_metadata_required += ["is_univariate"]
-            if not self.get_tag("handles-missing-data"):
+            if not self.get_tag("capability:missing_values"):
                 y_metadata_required += ["has_nans"]
 
             y_valid, y_msg, y_metadata = check_is_scitype(
@@ -1634,7 +1634,7 @@ class BaseForecaster(_PredictProbaMixin, BaseEstimator):
         if X is not None:
             # request only required metadata from checks
             X_metadata_required = ["feature_kind"]
-            if not self.get_tag("handles-missing-data"):
+            if not self.get_tag("capability:missing_values"):
                 X_metadata_required += ["has_nans"]
 
             X_valid, X_msg, X_metadata = check_is_scitype(
diff --git a/sktime/forecasting/base/_delegate.py b/sktime/forecasting/base/_delegate.py
index 333d9b65e..ee83865f5 100644
--- a/sktime/forecasting/base/_delegate.py
+++ b/sktime/forecasting/base/_delegate.py
@@ -67,12 +67,12 @@ class _DelegatedForecaster(BaseForecaster):
 
         TAGS_TO_DELEGATE = [
             "requires-fh-in-fit",
-            "handles-missing-data",
+            "capability:missing_values",
             "ignores-exogeneous-X",
             "capability:insample",
             "capability:pred_int",
             "capability:pred_int:insample",
-            "handles-missing-data",
+            "capability:missing_values",
             "requires-fh-in-fit",
             "X-y-must-have-same-index",
             "enforce_index_type",
diff --git a/sktime/forecasting/base/adapters/_darts.py b/sktime/forecasting/base/adapters/_darts.py
index 73c2153ac..54e4ba084 100644
--- a/sktime/forecasting/base/adapters/_darts.py
+++ b/sktime/forecasting/base/adapters/_darts.py
@@ -120,7 +120,7 @@ class _DartsRegressionAdapter(BaseForecaster):
         "y_inner_mtype": "pd.DataFrame",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:pred_int": False,
     }
@@ -470,7 +470,7 @@ class _DartsRegressionModelsAdapter(_DartsRegressionAdapter):
         "y_inner_mtype": "pd.DataFrame",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
     }
 
diff --git a/sktime/forecasting/base/adapters/_fbprophet.py b/sktime/forecasting/base/adapters/_fbprophet.py
index ae6d7f7c5..1b7b1bb5a 100644
--- a/sktime/forecasting/base/adapters/_fbprophet.py
+++ b/sktime/forecasting/base/adapters/_fbprophet.py
@@ -22,7 +22,7 @@ class _ProphetAdapter(BaseForecaster):
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "y_inner_mtype": "pd.DataFrame",
         "python_dependencies": "prophet",
     }
diff --git a/sktime/forecasting/base/adapters/_generalised_statsforecast.py b/sktime/forecasting/base/adapters/_generalised_statsforecast.py
index bf175bc44..47e184c67 100644
--- a/sktime/forecasting/base/adapters/_generalised_statsforecast.py
+++ b/sktime/forecasting/base/adapters/_generalised_statsforecast.py
@@ -31,7 +31,7 @@ class _GeneralisedStatsForecastAdapter(BaseForecaster):
         "requires-fh-in-fit": False,
         # "X-y-must-have-same-index": True,  # TODO: need to check (how?)
         # "enforce_index_type": None,  # TODO: need to check (how?)
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self):
diff --git a/sktime/forecasting/base/adapters/_neuralforecast.py b/sktime/forecasting/base/adapters/_neuralforecast.py
index 671807461..5d6ad272f 100644
--- a/sktime/forecasting/base/adapters/_neuralforecast.py
+++ b/sktime/forecasting/base/adapters/_neuralforecast.py
@@ -84,7 +84,7 @@ class _NeuralForecastAdapter(_BaseGlobalForecaster):
         "scitype:y": "univariate",
         "requires-fh-in-fit": True,
         "X-y-must-have-same-index": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:global_forecasting": True,
     }
diff --git a/sktime/forecasting/base/adapters/_pmdarima.py b/sktime/forecasting/base/adapters/_pmdarima.py
index 664da8f62..b9cf2f6af 100644
--- a/sktime/forecasting/base/adapters/_pmdarima.py
+++ b/sktime/forecasting/base/adapters/_pmdarima.py
@@ -22,7 +22,7 @@ class _PmdArimaAdapter(BaseForecaster):
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         # TODO 0.38.0: check if numpy 2 incompatiblity can be removed
         "python_dependencies": ["pmdarima", "numpy<2"],
     }
diff --git a/sktime/forecasting/base/adapters/_pytorchforecasting.py b/sktime/forecasting/base/adapters/_pytorchforecasting.py
index 8e88c8349..66b9cb3e8 100644
--- a/sktime/forecasting/base/adapters/_pytorchforecasting.py
+++ b/sktime/forecasting/base/adapters/_pytorchforecasting.py
@@ -77,7 +77,7 @@ class _PytorchForecastingAdapter(_BaseGlobalForecaster):
         "scitype:y": "univariate",
         "requires-fh-in-fit": True,
         "X-y-must-have-same-index": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:pred_int": False,
         "capability:pred_int:insample": False,
diff --git a/sktime/forecasting/base/adapters/_statsforecast.py b/sktime/forecasting/base/adapters/_statsforecast.py
index 04643e38d..0fbca15b0 100644
--- a/sktime/forecasting/base/adapters/_statsforecast.py
+++ b/sktime/forecasting/base/adapters/_statsforecast.py
@@ -19,7 +19,7 @@ class _StatsForecastAdapter(BaseForecaster):
         "maintainers": ["AzulGarza"],
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.Series",  # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for X?
         "requires-fh-in-fit": False,  # is forecasting horizon already required in fit?
diff --git a/sktime/forecasting/base/adapters/_statsmodels.py b/sktime/forecasting/base/adapters/_statsmodels.py
index 8f3abefd3..d6d3e5283 100644
--- a/sktime/forecasting/base/adapters/_statsmodels.py
+++ b/sktime/forecasting/base/adapters/_statsmodels.py
@@ -28,7 +28,7 @@ class _StatsModelsAdapter(BaseForecaster):
         # --------------
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, random_state=None):
diff --git a/sktime/forecasting/base/adapters/_tbats.py b/sktime/forecasting/base/adapters/_tbats.py
index dc24cf0fa..bedbdbb56 100644
--- a/sktime/forecasting/base/adapters/_tbats.py
+++ b/sktime/forecasting/base/adapters/_tbats.py
@@ -23,7 +23,7 @@ class _TbatsAdapter(BaseForecaster):
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "python_dependencies": ["tbats", "numpy<2"],
     }
 
diff --git a/sktime/forecasting/chronos.py b/sktime/forecasting/chronos.py
index ac63a2010..e7985cd2b 100644
--- a/sktime/forecasting/chronos.py
+++ b/sktime/forecasting/chronos.py
@@ -299,7 +299,7 @@ class ChronosForecaster(_BaseGlobalForecaster):
         "requires-fh-in-fit": False,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
         "X_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "y_inner_mtype": [
diff --git a/sktime/forecasting/compose/_bagging.py b/sktime/forecasting/compose/_bagging.py
index 32c727f4d..e0dc87c57 100644
--- a/sktime/forecasting/compose/_bagging.py
+++ b/sktime/forecasting/compose/_bagging.py
@@ -85,7 +85,7 @@ class BaggingForecaster(BaseForecaster):
         "authors": ["fkiraly", "ltsaprounis"],
         "scitype:y": "both",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "y_inner_mtype": PANDAS_MTYPES,
         # which types do _fit, _predict, assume for y?
         "X_inner_mtype": PANDAS_MTYPES,
diff --git a/sktime/forecasting/compose/_column_ensemble.py b/sktime/forecasting/compose/_column_ensemble.py
index 4bcd94540..bebf7a549 100644
--- a/sktime/forecasting/compose/_column_ensemble.py
+++ b/sktime/forecasting/compose/_column_ensemble.py
@@ -95,7 +95,7 @@ class ColumnEnsembleForecaster(_HeterogenousEnsembleForecaster, _ColumnEstimator
         "y_inner_mtype": PANDAS_MTYPES,
         "X_inner_mtype": PANDAS_MTYPES,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
     }
@@ -122,7 +122,7 @@ class ColumnEnsembleForecaster(_HeterogenousEnsembleForecaster, _ColumnEstimator
                 "requires-fh-in-fit",
                 "capability:pred_int",
                 "ignores-exogeneous-X",
-                "handles-missing-data",
+                "capability:missing_values",
             ]
             self.clone_tags(forecasters, tags_to_clone)
         else:
@@ -130,7 +130,9 @@ class ColumnEnsembleForecaster(_HeterogenousEnsembleForecaster, _ColumnEstimator
             self._anytagis_then_set("requires-fh-in-fit", True, False, l_forecasters)
             self._anytagis_then_set("capability:pred_int", False, True, l_forecasters)
             self._anytagis_then_set("ignores-exogeneous-X", False, True, l_forecasters)
-            self._anytagis_then_set("handles-missing-data", False, True, l_forecasters)
+            self._anytagis_then_set(
+                "capability:missing_values", False, True, l_forecasters
+            )
 
     @property
     def _forecasters(self):
diff --git a/sktime/forecasting/compose/_ensemble.py b/sktime/forecasting/compose/_ensemble.py
index 7b999f59b..7fd65fbcd 100644
--- a/sktime/forecasting/compose/_ensemble.py
+++ b/sktime/forecasting/compose/_ensemble.py
@@ -109,7 +109,7 @@ class AutoEnsembleForecaster(_HeterogenousEnsembleForecaster):
         "authors": ["mloning", "GuzalBulatova", "aiwalter", "RNKuhns", "AnH0ang"],
         "ignores-exogeneous-X": False,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "scitype:y": "univariate",
     }
 
@@ -322,7 +322,7 @@ class EnsembleForecaster(_HeterogenousEnsembleForecaster):
         "authors": ["mloning", "GuzalBulatova", "aiwalter", "RNKuhns", "AnH0ang"],
         "ignores-exogeneous-X": False,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "X_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "y_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "scitype:y": "both",
diff --git a/sktime/forecasting/compose/_fallback.py b/sktime/forecasting/compose/_fallback.py
index ccb87e066..8127e3001 100644
--- a/sktime/forecasting/compose/_fallback.py
+++ b/sktime/forecasting/compose/_fallback.py
@@ -110,7 +110,7 @@ class FallbackForecaster(_HeterogenousMetaEstimator, _DelegatedForecaster):
     _tags = {
         "authors": ["ninedigits", "RikStarmans"],
         "maintainers": ["ninedigits"],
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "scitype:y": "both",
         "y_inner_mtype": ALL_TIME_SERIES_MTYPES,
         "X_inner_mtype": ALL_TIME_SERIES_MTYPES,
diff --git a/sktime/forecasting/compose/_fhplex.py b/sktime/forecasting/compose/_fhplex.py
index ba660a80c..082e486ca 100644
--- a/sktime/forecasting/compose/_fhplex.py
+++ b/sktime/forecasting/compose/_fhplex.py
@@ -85,7 +85,7 @@ class FhPlexForecaster(BaseForecaster):
     _tags = {
         "authors": "fkiraly",
         "requires-fh-in-fit": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "scitype:y": "both",
         "y_inner_mtype": PANDAS_TS_MTYPES,
         "X_inner_mtype": PANDAS_TS_MTYPES,
diff --git a/sktime/forecasting/compose/_grouped.py b/sktime/forecasting/compose/_grouped.py
index 4b3f2a8a6..fed6eacbf 100644
--- a/sktime/forecasting/compose/_grouped.py
+++ b/sktime/forecasting/compose/_grouped.py
@@ -64,7 +64,7 @@ class ForecastByLevel(_DelegatedForecaster):
     _tags = {
         "authors": ["fkiraly"],
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "scitype:y": "both",
         "y_inner_mtype": ALL_TIME_SERIES_MTYPES,
         "X_inner_mtype": ALL_TIME_SERIES_MTYPES,
@@ -270,7 +270,7 @@ class GroupbyCategoryForecaster(BaseForecaster, _HeterogenousMetaEstimator):
             "ignores-exogeneous-X": True,
             "X-y-must-have-same-index": True,
             "enforce_index_type": True,
-            "handles-missing-data": True,
+            "capability:missing_values": True,
             "capability:insample": True,
             "capability:pred_int": True,
             "capability:pred_int:insample": True,
diff --git a/sktime/forecasting/compose/_hierarchy_ensemble.py b/sktime/forecasting/compose/_hierarchy_ensemble.py
index a0efda3f6..77d0cf34e 100644
--- a/sktime/forecasting/compose/_hierarchy_ensemble.py
+++ b/sktime/forecasting/compose/_hierarchy_ensemble.py
@@ -147,7 +147,7 @@ class HierarchyEnsembleForecaster(_HeterogenousEnsembleForecaster):
         "y_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "X_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     BY_LIST = ["level", "node"]
@@ -169,14 +169,16 @@ class HierarchyEnsembleForecaster(_HeterogenousEnsembleForecaster):
             tags_to_clone = [
                 "requires-fh-in-fit",
                 "ignores-exogeneous-X",
-                "handles-missing-data",
+                "capability:missing_values",
             ]
             self.clone_tags(forecasters, tags_to_clone)
         else:
             l_forecasters = [(x[0], x[1]) for x in forecasters]
             self._anytagis_then_set("requires-fh-in-fit", True, False, l_forecasters)
             self._anytagis_then_set("ignores-exogeneous-X", False, True, l_forecasters)
-            self._anytagis_then_set("handles-missing-data", False, True, l_forecasters)
+            self._anytagis_then_set(
+                "capability:missing_values", False, True, l_forecasters
+            )
 
     @property
     def _forecasters(self):
diff --git a/sktime/forecasting/compose/_multiplexer.py b/sktime/forecasting/compose/_multiplexer.py
index 41f02cd73..406b17a3d 100644
--- a/sktime/forecasting/compose/_multiplexer.py
+++ b/sktime/forecasting/compose/_multiplexer.py
@@ -80,7 +80,7 @@ class MultiplexForecaster(_HeterogenousMetaEstimator, _DelegatedForecaster):
     _tags = {
         "authors": ["kkoralturk", "aiwalter", "fkiraly", "miraep8"],
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "scitype:y": "both",
         "y_inner_mtype": ALL_TIME_SERIES_MTYPES,
         "X_inner_mtype": ALL_TIME_SERIES_MTYPES,
diff --git a/sktime/forecasting/compose/_pipeline.py b/sktime/forecasting/compose/_pipeline.py
index bef480ea5..f92816832 100644
--- a/sktime/forecasting/compose/_pipeline.py
+++ b/sktime/forecasting/compose/_pipeline.py
@@ -423,7 +423,7 @@ class ForecastingPipeline(_Pipeline):
         "X_inner_mtype": SUPPORTED_MTYPES,
         "ignores-exogeneous-X": False,
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:pred_int": True,
         "X-y-must-have-same-index": False,
         "capability:categorical_in_X": True,
@@ -872,7 +872,7 @@ class TransformedTargetForecaster(_Pipeline):
         "X_inner_mtype": SUPPORTED_MTYPES,
         "ignores-exogeneous-X": False,
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:pred_int": True,
         "X-y-must-have-same-index": False,
     }
@@ -1372,7 +1372,7 @@ class ForecastX(BaseForecaster):
         "ignores-exogeneous-X": False,
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     def __init__(
@@ -1432,7 +1432,7 @@ class ForecastX(BaseForecaster):
         self.clone_tags(forecaster_y, tags_to_clone_from_forecaster_y)
 
         # tag_translate_dict = {
-        #    "handles-missing-data": forecaster.get_tag("handles-missing-data")
+        #   "capability:missing_values": forecaster.get_tag("capability:missing_values")
         # }
         # self.set_tags(**tag_translate_dict)
 
@@ -1928,7 +1928,7 @@ class Permute(_DelegatedForecaster, BaseForecaster, _HeterogenousMetaEstimator):
         "X_inner_mtype": ALL_TIME_SERIES_MTYPES,
         "ignores-exogeneous-X": False,
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
         "X-y-must-have-same-index": False,
diff --git a/sktime/forecasting/compose/_reduce.py b/sktime/forecasting/compose/_reduce.py
index 845343d50..99608c27c 100644
--- a/sktime/forecasting/compose/_reduce.py
+++ b/sktime/forecasting/compose/_reduce.py
@@ -231,7 +231,7 @@ class _Reducer(_BaseWindowForecaster):
             "benheid",
         ],
         "ignores-exogeneous-X": False,  # reduction uses X in non-trivial way
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:insample": False,
         "capability:pred_int": True,
         "capability:pred_int:insample": False,
@@ -254,7 +254,9 @@ class _Reducer(_BaseWindowForecaster):
         # it seems that the sklearn tags are not fully reliable
         # see discussion in PR #3405 and issue #3402
         # therefore this is commented out until sktime and sklearn are better aligned
-        # self.set_tags(**{"handles-missing-data": estimator._get_tags()["allow_nan"]})
+        # self.set_tags(
+        #     **{"capability:missing_values": estimator._get_tags()["allow_nan"]}
+        # )
 
         # for dealing with probabilistic regressors:
         # self._est_type encodes information what type of estimator is passed
@@ -1992,7 +1994,9 @@ class DirectReductionForecaster(BaseForecaster, _ReducerMixin):
         # it seems that the sklearn tags are not fully reliable
         # see discussion in PR #3405 and issue #3402
         # therefore this is commented out until sktime and sklearn are better aligned
-        # self.set_tags(**{"handles-missing-data": estimator._get_tags()["allow_nan"]})
+        # self.set_tags(
+        #     **{"capability:missing_values": estimator._get_tags()["allow_nan"]}
+        # )
 
     def _fit(self, y, X, fh):
         """Fit dispatcher based on X_treatment and windows_identical."""
@@ -2762,7 +2766,7 @@ class YfromX(BaseForecaster, _ReducerMixin):
     _tags = {
         "requires-fh-in-fit": False,  # is the forecasting horizon required in fit?
         "ignores-exogeneous-X": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "X_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "y_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "capability:pred_int": True,
diff --git a/sktime/forecasting/compose/_skforecast_reduce.py b/sktime/forecasting/compose/_skforecast_reduce.py
index 46b5f4140..0de7831de 100644
--- a/sktime/forecasting/compose/_skforecast_reduce.py
+++ b/sktime/forecasting/compose/_skforecast_reduce.py
@@ -126,7 +126,7 @@ class SkforecastAutoreg(BaseForecaster):
         "y_inner_mtype": "pd.Series",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:pred_int": True,
         "capability:pred_int:insample": False,
@@ -590,7 +590,7 @@ class SkforecastRecursive(BaseForecaster):
         "y_inner_mtype": "pd.Series",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:pred_int": True,
         "capability:pred_int:insample": False,
diff --git a/sktime/forecasting/compose/_stack.py b/sktime/forecasting/compose/_stack.py
index e5cc68a35..6156460e6 100644
--- a/sktime/forecasting/compose/_stack.py
+++ b/sktime/forecasting/compose/_stack.py
@@ -63,7 +63,7 @@ class StackingForecaster(_HeterogenousEnsembleForecaster):
         "authors": ["mloning", "fkiraly", "indinewton"],
         "ignores-exogeneous-X": False,
         "requires-fh-in-fit": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "scitype:y": "univariate",
         "X-y-must-have-same-index": True,
     }
@@ -75,7 +75,7 @@ class StackingForecaster(_HeterogenousEnsembleForecaster):
         super().__init__(forecasters=forecasters, n_jobs=n_jobs)
 
         self._anytagis_then_set("ignores-exogeneous-X", False, True, forecasters)
-        self._anytagis_then_set("handles-missing-data", False, True, forecasters)
+        self._anytagis_then_set("capability:missing_values", False, True, forecasters)
         self._anytagis_then_set("fit_is_empty", False, True, forecasters)
 
     def _fit(self, y, X, fh):
diff --git a/sktime/forecasting/compose/_transform_select_forecaster.py b/sktime/forecasting/compose/_transform_select_forecaster.py
index fa3d3629b..e627448bb 100644
--- a/sktime/forecasting/compose/_transform_select_forecaster.py
+++ b/sktime/forecasting/compose/_transform_select_forecaster.py
@@ -144,7 +144,7 @@ class TransformSelectForecaster(BaseForecaster, _HeterogenousMetaEstimator):
             "ignores-exogeneous-X": True,
             "X-y-must-have-same-index": True,
             "enforce_index_type": True,
-            "handles-missing-data": True,
+            "capability:missing_values": True,
             "capability:insample": True,
             "capability:pred_int": True,
             "capability:pred_int:insample": True,
diff --git a/sktime/forecasting/conditional_invertible_neural_network.py b/sktime/forecasting/conditional_invertible_neural_network.py
index 6b5157301..115ac1b6d 100644
--- a/sktime/forecasting/conditional_invertible_neural_network.py
+++ b/sktime/forecasting/conditional_invertible_neural_network.py
@@ -128,7 +128,7 @@ class CINNForecaster(BaseDeepNetworkPyTorch):
         "requires-fh-in-fit": False,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
     }
 
diff --git a/sktime/forecasting/conformal.py b/sktime/forecasting/conformal.py
index 5d21e5306..51c600b52 100644
--- a/sktime/forecasting/conformal.py
+++ b/sktime/forecasting/conformal.py
@@ -127,7 +127,7 @@ class ConformalIntervals(BaseForecaster):
         # --------------
         "scitype:y": "univariate",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "ignores-exogeneous-X": False,
         "capability:pred_int": True,
         "capability:pred_int:insample": False,
@@ -172,7 +172,7 @@ class ConformalIntervals(BaseForecaster):
         tags_to_clone = [
             "requires-fh-in-fit",
             "ignores-exogeneous-X",
-            "handles-missing-data",
+            "capability:missing_values",
             "X-y-must-have-same-index",
             "enforce_index_type",
         ]
diff --git a/sktime/forecasting/dynamic_factor.py b/sktime/forecasting/dynamic_factor.py
index 2d15c8892..559ec8dd2 100644
--- a/sktime/forecasting/dynamic_factor.py
+++ b/sktime/forecasting/dynamic_factor.py
@@ -132,7 +132,7 @@ class DynamicFactor(_StatsModelsAdapter):
         # --------------
         "scitype:y": "multivariate",
         "ignores-exogeneous-X": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "y_inner_mtype": "pd.DataFrame",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
diff --git a/sktime/forecasting/enbpi.py b/sktime/forecasting/enbpi.py
index 35cc09624..194ac7b97 100644
--- a/sktime/forecasting/enbpi.py
+++ b/sktime/forecasting/enbpi.py
@@ -92,7 +92,7 @@ class EnbPIForecaster(BaseForecaster):
         "python_dependencies": ["tsbootstrap>=0.1.0"],
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.DataFrame",
         # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",
diff --git a/sktime/forecasting/ets.py b/sktime/forecasting/ets.py
index dfa23bcaf..8e8d7b05c 100644
--- a/sktime/forecasting/ets.py
+++ b/sktime/forecasting/ets.py
@@ -186,7 +186,7 @@ class AutoETS(_StatsModelsAdapter):
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     def __init__(
diff --git a/sktime/forecasting/hf_transformers_forecaster.py b/sktime/forecasting/hf_transformers_forecaster.py
index feeed2d42..c7a9b64cc 100644
--- a/sktime/forecasting/hf_transformers_forecaster.py
+++ b/sktime/forecasting/hf_transformers_forecaster.py
@@ -180,7 +180,7 @@ class HFTransformersForecaster(BaseForecaster):
         "requires-fh-in-fit": False,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
         "python_dependencies": ["transformers", "torch"],
         "X_inner_mtype": "pd.DataFrame",
diff --git a/sktime/forecasting/model_selection/_tune.py b/sktime/forecasting/model_selection/_tune.py
index f9133ff8f..657d7b5b9 100644
--- a/sktime/forecasting/model_selection/_tune.py
+++ b/sktime/forecasting/model_selection/_tune.py
@@ -32,7 +32,7 @@ class BaseGridSearch(_DelegatedForecaster):
         "authors": ["mloning", "fkiraly", "aiwalter"],
         "scitype:y": "both",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "ignores-exogeneous-X": True,
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
@@ -1177,7 +1177,7 @@ class ForecastingSkoptSearchCV(BaseGridSearch):
         "maintainers": ["HazrulAkmal"],
         "scitype:y": "both",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "ignores-exogeneous-X": True,
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
@@ -1760,7 +1760,7 @@ class ForecastingOptunaSearchCV(BaseGridSearch):
         "maintainers": ["gareth-brown-86", "mk406"],
         "scitype:y": "both",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "ignores-exogeneous-X": True,
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
diff --git a/sktime/forecasting/moirai_forecaster.py b/sktime/forecasting/moirai_forecaster.py
index ba9feac60..a71bd2c5f 100644
--- a/sktime/forecasting/moirai_forecaster.py
+++ b/sktime/forecasting/moirai_forecaster.py
@@ -86,7 +86,7 @@ class MOIRAIForecaster(_BaseGlobalForecaster):
         "requires-fh-in-fit": False,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
         "python_dependencies": [
             "gluonts",
diff --git a/sktime/forecasting/naive/_naive.py b/sktime/forecasting/naive/_naive.py
index b5fe6c641..d18be1fd3 100644
--- a/sktime/forecasting/naive/_naive.py
+++ b/sktime/forecasting/naive/_naive.py
@@ -124,7 +124,7 @@ class NaiveForecaster(_BaseWindowForecaster):
         # --------------
         "y_inner_mtype": "pd.Series",
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "ignores-exogeneous-X": True,
         "scitype:y": "univariate",
         "capability:pred_var": True,
@@ -140,7 +140,7 @@ class NaiveForecaster(_BaseWindowForecaster):
         # Override tag for handling missing data
         # todo: remove if GH1367 is fixed
         if self.strategy in ("last", "mean"):
-            self.set_tags(**{"handles-missing-data": True})
+            self.set_tags(**{"capability:missing_values": True})
 
     def _fit(self, y, X, fh):
         """Fit to training data.
@@ -677,7 +677,7 @@ class NaiveVariance(BaseForecaster):
         # --------------
         "scitype:y": "univariate",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "ignores-exogeneous-X": False,
         "capability:pred_int": True,
         "capability:pred_var": True,
@@ -692,7 +692,7 @@ class NaiveVariance(BaseForecaster):
         tags_to_clone = [
             "requires-fh-in-fit",
             "ignores-exogeneous-X",
-            "handles-missing-data",
+            "capability:missing_values",
             "y_inner_mtype",
             "X_inner_mtype",
             "X-y-must-have-same-index",
diff --git a/sktime/forecasting/online_learning/_online_ensemble.py b/sktime/forecasting/online_learning/_online_ensemble.py
index 9e8721f3e..2c75f2214 100644
--- a/sktime/forecasting/online_learning/_online_ensemble.py
+++ b/sktime/forecasting/online_learning/_online_ensemble.py
@@ -39,7 +39,7 @@ class OnlineEnsembleForecaster(EnsembleForecaster):
         # --------------
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "y_inner_mtype": ["pd.Series"],
         "scitype:y": "univariate",
     }
diff --git a/sktime/forecasting/online_learning/_prediction_weighted_ensembler.py b/sktime/forecasting/online_learning/_prediction_weighted_ensembler.py
index b3ab085e4..408c03590 100644
--- a/sktime/forecasting/online_learning/_prediction_weighted_ensembler.py
+++ b/sktime/forecasting/online_learning/_prediction_weighted_ensembler.py
@@ -29,7 +29,7 @@ class _PredictionWeightedEnsembler:
         # --------------
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, n_estimators=10, loss_func=None):
@@ -114,7 +114,7 @@ class HedgeExpertEnsemble(_PredictionWeightedEnsembler):
     _tags = {
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, n_estimators=10, T=10, a=1, loss_func=None):
@@ -147,7 +147,7 @@ class NormalHedgeEnsemble(HedgeExpertEnsemble):
     _tags = {
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, n_estimators=10, a=1, loss_func=None):
@@ -259,7 +259,7 @@ class NNLSEnsemble(_PredictionWeightedEnsembler):
     _tags = {
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, n_estimators=10, loss_func=None):
diff --git a/sktime/forecasting/prophetverse.py b/sktime/forecasting/prophetverse.py
index eed16324d..b87765a70 100644
--- a/sktime/forecasting/prophetverse.py
+++ b/sktime/forecasting/prophetverse.py
@@ -327,7 +327,7 @@ class HierarchicalProphet(_DelegatedForecaster):
         # estimator type
         "scitype:y": "univariate",
         "ignores-exogeneous-X": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "y_inner_mtype": [
             "pd.DataFrame",
             "pd-multiindex",
diff --git a/sktime/forecasting/pykan_forecaster.py b/sktime/forecasting/pykan_forecaster.py
index 98de05c36..fe2717e3f 100644
--- a/sktime/forecasting/pykan_forecaster.py
+++ b/sktime/forecasting/pykan_forecaster.py
@@ -69,7 +69,7 @@ class PyKANForecaster(BaseForecaster):
         "requires-fh-in-fit": True,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
         "capability:pred_int:insample": False,
         "capability:insample": False,
diff --git a/sktime/forecasting/rbf_forecaster.py b/sktime/forecasting/rbf_forecaster.py
index fa3f2e40e..5fe427df7 100644
--- a/sktime/forecasting/rbf_forecaster.py
+++ b/sktime/forecasting/rbf_forecaster.py
@@ -96,7 +96,7 @@ class RBFForecaster(BaseDeepNetworkPyTorch):
         "y_inner_mtype": "pd.Series",
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
     }
 
diff --git a/sktime/forecasting/reconcile.py b/sktime/forecasting/reconcile.py
index 050ecc397..ee2695f8c 100644
--- a/sktime/forecasting/reconcile.py
+++ b/sktime/forecasting/reconcile.py
@@ -97,7 +97,7 @@ class ReconcilerForecaster(BaseForecaster):
         # --------------
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": [
             "pd.DataFrame",
             "pd.Series",
diff --git a/sktime/forecasting/squaring_residuals.py b/sktime/forecasting/squaring_residuals.py
index affc8f017..bbbd0e193 100644
--- a/sktime/forecasting/squaring_residuals.py
+++ b/sktime/forecasting/squaring_residuals.py
@@ -90,7 +90,7 @@ class SquaringResiduals(BaseForecaster):
         # --------------
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": True,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.Series",  # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for X?
         "requires-fh-in-fit": True,  # is forecasting horizon already required in fit?
diff --git a/sktime/forecasting/structural.py b/sktime/forecasting/structural.py
index a430680f6..96e6bdb09 100644
--- a/sktime/forecasting/structural.py
+++ b/sktime/forecasting/structural.py
@@ -211,7 +211,7 @@ class UnobservedComponents(_StatsModelsAdapter):
         # --------------
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "ignores-exogeneous-X": False,
     }
 
diff --git a/sktime/forecasting/theta.py b/sktime/forecasting/theta.py
index 4902f79de..9f7925d07 100644
--- a/sktime/forecasting/theta.py
+++ b/sktime/forecasting/theta.py
@@ -100,7 +100,7 @@ class ThetaForecaster(ExponentialSmoothing):
         "capability:pred_int": True,
         "capability:pred_int:insample": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, initial_level=None, deseasonalize=True, sp=1):
@@ -423,7 +423,7 @@ class ThetaModularForecaster(BaseForecaster):
         "scitype:y": "univariate",
         "y_inner_mtype": "pd.Series",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "python_version": ">3.7",
     }
 
diff --git a/sktime/forecasting/timemoe.py b/sktime/forecasting/timemoe.py
index e25204bc2..c88e475e1 100644
--- a/sktime/forecasting/timemoe.py
+++ b/sktime/forecasting/timemoe.py
@@ -115,7 +115,7 @@ class TimeMoEForecaster(_BaseGlobalForecaster):
         "requires-fh-in-fit": False,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
         "X_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "y_inner_mtype": [
diff --git a/sktime/forecasting/timesfm_forecaster.py b/sktime/forecasting/timesfm_forecaster.py
index 05e644d8c..1ee6fe172 100644
--- a/sktime/forecasting/timesfm_forecaster.py
+++ b/sktime/forecasting/timesfm_forecaster.py
@@ -177,7 +177,7 @@ class TimesFMForecaster(_BaseGlobalForecaster):
         "requires-fh-in-fit": False,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:pred_int": False,
         "capability:pred_int:insample": False,
diff --git a/sktime/forecasting/trend/_polynomial_trend_forecaster.py b/sktime/forecasting/trend/_polynomial_trend_forecaster.py
index b00b7a4f8..658156447 100644
--- a/sktime/forecasting/trend/_polynomial_trend_forecaster.py
+++ b/sktime/forecasting/trend/_polynomial_trend_forecaster.py
@@ -94,7 +94,7 @@ class PolynomialTrendForecaster(BaseForecaster):
         "maintainers": ["tensorflow-as-tf"],
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": True,
     }
 
diff --git a/sktime/forecasting/trend/_spline_trend_forecaster.py b/sktime/forecasting/trend/_spline_trend_forecaster.py
index cfc9fb78d..55d10fa15 100644
--- a/sktime/forecasting/trend/_spline_trend_forecaster.py
+++ b/sktime/forecasting/trend/_spline_trend_forecaster.py
@@ -90,7 +90,7 @@ class SplineTrendForecaster(_DelegatedForecaster):
         "maintainers": ["jgyasu"],
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:pred_int": False,
     }
 
diff --git a/sktime/forecasting/trend/_stl_forecaster.py b/sktime/forecasting/trend/_stl_forecaster.py
index e383cbfd9..a72af6521 100644
--- a/sktime/forecasting/trend/_stl_forecaster.py
+++ b/sktime/forecasting/trend/_stl_forecaster.py
@@ -143,7 +143,7 @@ class STLForecaster(BaseForecaster):
         "maintainers": ["tensorflow-as-tf"],
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.Series",  # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for X?
         "requires-fh-in-fit": False,  # is forecasting horizon already required in fit?
diff --git a/sktime/forecasting/trend/_trend_forecaster.py b/sktime/forecasting/trend/_trend_forecaster.py
index 2a9ccdb22..c22199d24 100644
--- a/sktime/forecasting/trend/_trend_forecaster.py
+++ b/sktime/forecasting/trend/_trend_forecaster.py
@@ -63,7 +63,7 @@ class TrendForecaster(BaseForecaster):
         "maintainers": ["tensorflow-as-tf"],
         "ignores-exogeneous-X": True,
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, regressor=None):
diff --git a/sktime/forecasting/ttm.py b/sktime/forecasting/ttm.py
index 234a2eb14..294518fa2 100644
--- a/sktime/forecasting/ttm.py
+++ b/sktime/forecasting/ttm.py
@@ -225,7 +225,7 @@ class TinyTimeMixerForecaster(_BaseGlobalForecaster):
         "requires-fh-in-fit": True,
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:insample": False,
         "capability:pred_int": False,
         "capability:pred_int:insample": False,
diff --git a/sktime/forecasting/varmax.py b/sktime/forecasting/varmax.py
index 9863e1d94..683d8fa59 100644
--- a/sktime/forecasting/varmax.py
+++ b/sktime/forecasting/varmax.py
@@ -218,7 +218,7 @@ class VARMAX(_StatsModelsAdapter):
         # --------------
         "scitype:y": "multivariate",
         "ignores-exogeneous-X": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "y_inner_mtype": "pd.DataFrame",
         "X_inner_mtype": "pd.DataFrame",
         "requires-fh-in-fit": False,
diff --git a/sktime/param_est/compose/_pipeline.py b/sktime/param_est/compose/_pipeline.py
index 364bfb45d..9c1fcf3ae 100644
--- a/sktime/param_est/compose/_pipeline.py
+++ b/sktime/param_est/compose/_pipeline.py
@@ -118,7 +118,9 @@ class ParamFitterPipeline(_HeterogenousMetaEstimator, BaseParamFitter):
         # can handle missing values iff: both estimator and all transformers can,
         #   *or* transformer chain removes missing data
         missing = param_est.get_tag("capability:missing_values", False)
-        missing = missing and self.transformers_.get_tag("handles-missing-data", False)
+        missing = missing and self.transformers_.get_tag(
+            "capability:missing_values", False
+        )
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
diff --git a/sktime/param_est/plugin/_forecaster.py b/sktime/param_est/plugin/_forecaster.py
index ce95bf1ef..a267bd2c4 100644
--- a/sktime/param_est/plugin/_forecaster.py
+++ b/sktime/param_est/plugin/_forecaster.py
@@ -108,7 +108,7 @@ class PluginParamsForecaster(_DelegatedForecaster):
     _tags = {
         "authors": "fkiraly",
         "requires-fh-in-fit": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "scitype:y": "both",
         "y_inner_mtype": ["pd.DataFrame", "pd.Series"],
         "fit_is_empty": False,
diff --git a/sktime/param_est/plugin/_transformer.py b/sktime/param_est/plugin/_transformer.py
index 1f4875aec..0430e35eb 100644
--- a/sktime/param_est/plugin/_transformer.py
+++ b/sktime/param_est/plugin/_transformer.py
@@ -136,7 +136,7 @@ class PluginParamsTransformer(_DelegatedTransformer):
             "skip-inverse-transform",
             "capability:unequal_length",
             "capability:unequal_length:removes",
-            "handles-missing-data",
+            "capability:missing_values",
             "capability:missing_values:removes",
         ]
 
diff --git a/sktime/registry/_lookup.py b/sktime/registry/_lookup.py
index 315df1b86..ddf1b5bad 100644
--- a/sktime/registry/_lookup.py
+++ b/sktime/registry/_lookup.py
@@ -158,7 +158,7 @@ def all_estimators(
     >>> # return all forecasters by filtering for estimator type
     >>> all_estimators("forecaster")  # doctest: +SKIP
     >>> # return all forecasters which handle missing data in the input by tag filtering
-    >>> all_estimators("forecaster", filter_tags={"handles-missing-data": True})  # doctest: +SKIP
+    >>> all_estimators("forecaster", filter_tags={"capability:missing_values": True})  # doctest: +SKIP
 
     References
     ----------
diff --git a/sktime/regression/compose/_pipeline.py b/sktime/regression/compose/_pipeline.py
index 504cacd49..f2bd38c41 100644
--- a/sktime/regression/compose/_pipeline.py
+++ b/sktime/regression/compose/_pipeline.py
@@ -118,7 +118,9 @@ class RegressorPipeline(_HeterogenousMetaEstimator, BaseRegressor):
         # can handle missing values iff: both regressor and all transformers can,
         #   *or* transformer chain removes missing data
         missing = regressor.get_tag("capability:missing_values", False)
-        missing = missing and self.transformers_.get_tag("handles-missing-data", False)
+        missing = missing and self.transformers_.get_tag(
+            "capability:missing_values", False
+        )
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
@@ -417,7 +419,7 @@ class SklearnRegressorPipeline(_HeterogenousMetaEstimator, BaseRegressor):
         # can handle missing values iff transformer chain removes missing data
         # sklearn regressors might be able to handle missing data (but no tag there)
         # so better set the tag liberally
-        missing = self.transformers_.get_tag("handles-missing-data", False)
+        missing = self.transformers_.get_tag("capability:missing_values", False)
         missing = missing or self.transformers_.get_tag(
             "capability:missing_values:removes", False
         )
diff --git a/sktime/transformations/base.py b/sktime/transformations/base.py
index 2e0d17335..699b7d417 100644
--- a/sktime/transformations/base.py
+++ b/sktime/transformations/base.py
@@ -131,7 +131,7 @@ class BaseTransformer(BaseEstimator):
         # can the transformer handle unequal length time series (if passed Panel)?
         "capability:unequal_length:removes": False,
         # is transform result always guaranteed to be equal length (and series)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         # todo: rename to capability:missing_values
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
diff --git a/sktime/transformations/bootstrap/_mbb.py b/sktime/transformations/bootstrap/_mbb.py
index b615d217f..357473097 100644
--- a/sktime/transformations/bootstrap/_mbb.py
+++ b/sktime/transformations/bootstrap/_mbb.py
@@ -184,7 +184,7 @@ class STLBootstrapTransformer(BaseTransformer):
         "capability:inverse_transform": False,
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": True,  # can the transformer handle multivariate X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "enforce_index_type": None,  # index type that needs to be enforced in X/y
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
@@ -498,7 +498,7 @@ class MovingBlockBootstrapTransformer(BaseTransformer):
         "capability:inverse_transform": False,
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": True,  # can the transformer handle multivariate X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "enforce_index_type": None,  # index type that needs to be enforced in X/y
         "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
diff --git a/sktime/transformations/bootstrap/_repeat.py b/sktime/transformations/bootstrap/_repeat.py
index 365ba191f..422bc340b 100644
--- a/sktime/transformations/bootstrap/_repeat.py
+++ b/sktime/transformations/bootstrap/_repeat.py
@@ -34,7 +34,7 @@ class RepeatBootstrapTransformer(BaseTransformer):
         "capability:inverse_transform": False,
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": False,  # can the transformer handle multivariate X?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "enforce_index_type": None,  # index type that needs to be enforced in X/y
         "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
diff --git a/sktime/transformations/bootstrap/_splitterboot.py b/sktime/transformations/bootstrap/_splitterboot.py
index c2c49e9b1..87f5ff8be 100644
--- a/sktime/transformations/bootstrap/_splitterboot.py
+++ b/sktime/transformations/bootstrap/_splitterboot.py
@@ -104,7 +104,7 @@ class SplitterBootstrapTransformer(BaseTransformer):
         "capability:inverse_transform": False,
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": False,  # can the transformer handle multivariate X?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "enforce_index_type": None,  # index type that needs to be enforced in X/y
         "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
diff --git a/sktime/transformations/bootstrap/_tsbootstrap.py b/sktime/transformations/bootstrap/_tsbootstrap.py
index 93894043a..d9b2bf000 100644
--- a/sktime/transformations/bootstrap/_tsbootstrap.py
+++ b/sktime/transformations/bootstrap/_tsbootstrap.py
@@ -54,7 +54,7 @@ class TSBootstrapAdapter(BaseTransformer):
         "capability:inverse_transform": False,
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": False,  # can the transformer handle multivariate X?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "enforce_index_type": None,  # index type that needs to be enforced in X/y
         "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
diff --git a/sktime/transformations/compose/_column.py b/sktime/transformations/compose/_column.py
index f53b7d37f..8aa9cccf3 100644
--- a/sktime/transformations/compose/_column.py
+++ b/sktime/transformations/compose/_column.py
@@ -127,7 +127,7 @@ class ColumnEnsembleTransformer(
         "y_inner_mtype": PANDAS_MTYPES,
         "fit_is_empty": False,
         "capability:unequal_length": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "visual_block_kind": "parallel",
     }
 
@@ -167,7 +167,7 @@ class ColumnEnsembleTransformer(
                 "transform-returns-same-time-index",
                 "capability:unequal_length",
                 "capability:unequal_length:removes",
-                "handles-missing-data",
+                "capability:missing_values",
                 "capability:missing_values:removes",
                 "scitype:transform-output",
                 "scitype:transform-labels",
@@ -191,7 +191,9 @@ class ColumnEnsembleTransformer(
             self._anytagis_then_set(
                 "capability:unequal_length:removes", False, True, l_transformers
             )
-            self._anytagis_then_set("handles-missing-data", False, True, l_transformers)
+            self._anytagis_then_set(
+                "capability:missing_values", False, True, l_transformers
+            )
             self._anytagis_then_set(
                 "capability:missing_values:removes", False, True, l_transformers
             )
@@ -439,7 +441,7 @@ class ColumnwiseTransformer(BaseTransformer):
         tags_to_clone = [
             "y_inner_mtype",
             "capability:inverse_transform",
-            "handles-missing-data",
+            "capability:missing_values",
             "X-y-must-have-same-index",
             "transform-returns-same-time-index",
             "skip-inverse-transform",
diff --git a/sktime/transformations/compose/_featureunion.py b/sktime/transformations/compose/_featureunion.py
index 02cf16f93..8e1fc268e 100644
--- a/sktime/transformations/compose/_featureunion.py
+++ b/sktime/transformations/compose/_featureunion.py
@@ -49,7 +49,7 @@ class FeatureUnion(_HeterogenousMetaEstimator, BaseTransformer):
         "scitype:transform-labels": "None",
         "scitype:instancewise": False,  # depends on components
         "univariate-only": False,  # depends on components
-        "handles-missing-data": False,  # depends on components
+        "capability:missing_values": False,  # depends on components
         "X_inner_mtype": ["pd.DataFrame", "pd-multiindex", "pd_multiindex_hier"],
         "y_inner_mtype": "None",
         "X-y-must-have-same-index": False,
@@ -111,7 +111,7 @@ class FeatureUnion(_HeterogenousMetaEstimator, BaseTransformer):
         self._anytagis_then_set("transform-returns-same-time-index", False, True, ests)
         self._anytagis_then_set("skip-inverse-transform", True, False, ests)
         # self._anytagis_then_set("capability:inverse_transform", False, True, ests)
-        self._anytagis_then_set("handles-missing-data", False, True, ests)
+        self._anytagis_then_set("capability:missing_values", False, True, ests)
         self._anytagis_then_set("univariate-only", True, False, ests)
 
         # if any of the components require_X or require_y, set it for the composite
diff --git a/sktime/transformations/compose/_grouped.py b/sktime/transformations/compose/_grouped.py
index fb5ab7b36..a253bfbf9 100644
--- a/sktime/transformations/compose/_grouped.py
+++ b/sktime/transformations/compose/_grouped.py
@@ -62,7 +62,7 @@ class TransformByLevel(_DelegatedTransformer):
     _tags = {
         "authors": ["fkiraly"],
         "requires-fh-in-fit": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "X_inner_mtype": ALL_TIME_SERIES_MTYPES,
         "y_inner_mtype": ALL_TIME_SERIES_MTYPES,
         "fit_is_empty": False,
diff --git a/sktime/transformations/compose/_id.py b/sktime/transformations/compose/_id.py
index 54cbbf905..9a1366e61 100644
--- a/sktime/transformations/compose/_id.py
+++ b/sktime/transformations/compose/_id.py
@@ -26,7 +26,7 @@ class Id(BaseTransformer):
         "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
         "transform-returns-same-time-index": True,
         # does transform return have the same time index as input X
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
     }
 
     def _transform(self, X, y=None):
diff --git a/sktime/transformations/compose/_invert.py b/sktime/transformations/compose/_invert.py
index 28074215e..ff1cec80e 100644
--- a/sktime/transformations/compose/_invert.py
+++ b/sktime/transformations/compose/_invert.py
@@ -67,7 +67,7 @@ class InvertTransform(_DelegatedTransformer):
             "scitype:instancewise",
             "X_inner_mtype",
             "y_inner_mtype",
-            "handles-missing-data",
+            "capability:missing_values",
             "X-y-must-have-same-index",
             "transform-returns-same-time-index",
             "skip-inverse-transform",
diff --git a/sktime/transformations/compose/_logger.py b/sktime/transformations/compose/_logger.py
index 4041d5705..d6667d925 100644
--- a/sktime/transformations/compose/_logger.py
+++ b/sktime/transformations/compose/_logger.py
@@ -80,7 +80,7 @@ class Logger(BaseTransformer):
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
         "transform-returns-same-time-index": True,
         # does transform return have the same time index as input X
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
     }
 
     def __init__(
diff --git a/sktime/transformations/compose/_optional.py b/sktime/transformations/compose/_optional.py
index 985cdc63e..e39c8a8f5 100644
--- a/sktime/transformations/compose/_optional.py
+++ b/sktime/transformations/compose/_optional.py
@@ -101,7 +101,7 @@ class OptionalPassthrough(_DelegatedTransformer):
             "scitype:instancewise",
             "y_inner_mtype",
             "capability:inverse_transform",
-            "handles-missing-data",
+            "capability:missing_values",
             "X-y-must-have-same-index",
             "transform-returns-same-time-index",
             "skip-inverse-transform",
diff --git a/sktime/transformations/compose/_pipeline.py b/sktime/transformations/compose/_pipeline.py
index ec50ff0ac..d871c6a90 100644
--- a/sktime/transformations/compose/_pipeline.py
+++ b/sktime/transformations/compose/_pipeline.py
@@ -189,7 +189,7 @@ class TransformerPipeline(_HeterogenousMetaEstimator, BaseTransformer):
         # removes missing data iff can handle missing data,
         #   and there is an estimator in the chain that removes it
         self._tagchain_is_linked_set(
-            "handles-missing-data", "capability:missing_values:removes", ests
+            "capability:missing_values", "capability:missing_values:removes", ests
         )
         # can handle unequal length iff all estimators can handle unequal length
         #   up to a potential estimator which turns the series equal length
diff --git a/sktime/transformations/hierarchical/aggregate.py b/sktime/transformations/hierarchical/aggregate.py
index bfa853bba..aa7097075 100644
--- a/sktime/transformations/hierarchical/aggregate.py
+++ b/sktime/transformations/hierarchical/aggregate.py
@@ -70,7 +70,7 @@ class Aggregator(BaseTransformer):
         "capability:inverse_transform": False,  # does transformer have inverse
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": False,  # can the transformer handle multivariate X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
         "transform-returns-same-time-index": False,
diff --git a/sktime/transformations/hierarchical/reconcile.py b/sktime/transformations/hierarchical/reconcile.py
index aabd55457..b4dbf72ad 100644
--- a/sktime/transformations/hierarchical/reconcile.py
+++ b/sktime/transformations/hierarchical/reconcile.py
@@ -97,7 +97,7 @@ class Reconciler(BaseTransformer):
         "capability:inverse_transform": False,
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": True,  # can the transformer handle multivariate X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
         "transform-returns-same-time-index": True,
diff --git a/sktime/transformations/panel/compose_distance.py b/sktime/transformations/panel/compose_distance.py
index 576f82f4f..b9e391d62 100644
--- a/sktime/transformations/panel/compose_distance.py
+++ b/sktime/transformations/panel/compose_distance.py
@@ -62,7 +62,7 @@ class DistanceFeatures(BaseTransformer):
         "skip-inverse-transform": False,
         "capability:unequal_length": True,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:missing_values:removes": False,
         # we leave remember_data as False, since updating self._X in update
         # would increase the number of columns in the transform return
diff --git a/sktime/transformations/series/adi_cv.py b/sktime/transformations/series/adi_cv.py
index 487d37397..311f77de6 100644
--- a/sktime/transformations/series/adi_cv.py
+++ b/sktime/transformations/series/adi_cv.py
@@ -101,7 +101,7 @@ class ADICVTransformer(BaseTransformer):
         "fit_is_empty": True,
         "capability:inverse_transform": False,
         "capability:unequal_length": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(
diff --git a/sktime/transformations/series/augmenter.py b/sktime/transformations/series/augmenter.py
index d18526375..d18b63af2 100755
--- a/sktime/transformations/series/augmenter.py
+++ b/sktime/transformations/series/augmenter.py
@@ -29,7 +29,7 @@ class _AugmenterTags:
         "scitype:transform-output": "Series",
         "scitype:transform-labels": "None",
         "scitype:instancewise": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "y_inner_mtype": "pd.Series",
         "X_inner_mtype": "pd.DataFrame",
         "X-y-must-have-same-index": False,
diff --git a/sktime/transformations/series/basisfunction.py b/sktime/transformations/series/basisfunction.py
index d8c71ce06..482a3db2f 100644
--- a/sktime/transformations/series/basisfunction.py
+++ b/sktime/transformations/series/basisfunction.py
@@ -111,7 +111,7 @@ class RBFTransformer(BaseTransformer):
         "fit_is_empty": False,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(
diff --git a/sktime/transformations/series/binning.py b/sktime/transformations/series/binning.py
index 995a5ed98..ce9be1baf 100644
--- a/sktime/transformations/series/binning.py
+++ b/sktime/transformations/series/binning.py
@@ -67,7 +67,7 @@ class TimeBinAggregate(BaseTransformer):
         "X_inner_mtype": ["pd.DataFrame"],
         # which mtypes do _fit/_predict support for X?
         "y_inner_mtype": "None",  # and for y?
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:unequal_length": True,
         "capability:unequal_length:removes": True,
         "transform-returns-same-time-index": False,
diff --git a/sktime/transformations/series/bkfilter.py b/sktime/transformations/series/bkfilter.py
index a90c81a0e..22b56703f 100644
--- a/sktime/transformations/series/bkfilter.py
+++ b/sktime/transformations/series/bkfilter.py
@@ -89,7 +89,7 @@ class BKFilter(BaseTransformer):
         # does transform return have the same time index as input X
         "capability:unequal_length": True,
         # can the transformer handle unequal length time series (if passed Panel)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "remember_data": False,  # whether all data seen is remembered as self._X
     }
 
diff --git a/sktime/transformations/series/cffilter.py b/sktime/transformations/series/cffilter.py
index 195da31d1..39966b998 100644
--- a/sktime/transformations/series/cffilter.py
+++ b/sktime/transformations/series/cffilter.py
@@ -77,7 +77,7 @@ class CFFilter(BaseTransformer):
         # does transform return have the same time index as input X
         "capability:unequal_length": True,
         # can the transformer handle unequal length time series (if passed Panel)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "remember_data": False,  # whether all data seen is remembered as self._X
     }
 
diff --git a/sktime/transformations/series/clear_sky.py b/sktime/transformations/series/clear_sky.py
index 1cdb09bb0..a47f0b220 100644
--- a/sktime/transformations/series/clear_sky.py
+++ b/sktime/transformations/series/clear_sky.py
@@ -100,7 +100,7 @@ class ClearSky(BaseTransformer):
         "skip-inverse-transform": False,  # is inverse-transform skipped when called?
         "capability:unequal_length": False,
         "capability:unequal_length:removes": True,  # ?
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:missing_values:removes": True,
     }
 
diff --git a/sktime/transformations/series/dilation_mapping.py b/sktime/transformations/series/dilation_mapping.py
index 4ba62c894..d52e05734 100644
--- a/sktime/transformations/series/dilation_mapping.py
+++ b/sktime/transformations/series/dilation_mapping.py
@@ -74,7 +74,7 @@ class DilationMappingTransformer(BaseTransformer):
         "fit_is_empty": True,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     def __init__(self, dilation=2):
diff --git a/sktime/transformations/series/dropna.py b/sktime/transformations/series/dropna.py
index 237218ab7..5b05b7b18 100644
--- a/sktime/transformations/series/dropna.py
+++ b/sktime/transformations/series/dropna.py
@@ -67,7 +67,7 @@ class DropNA(BaseTransformer):
         "fit_is_empty": False,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     VALID_AXIS_VALUES = [0, "index", 1, "columns"]
diff --git a/sktime/transformations/series/dummies.py b/sktime/transformations/series/dummies.py
index b30917cf2..95b0545ef 100644
--- a/sktime/transformations/series/dummies.py
+++ b/sktime/transformations/series/dummies.py
@@ -94,7 +94,7 @@ class SeasonalDummiesOneHot(BaseTransformer):
         "capability:unequal_length:removes": False,
         # is transform result always guaranteed to be equal length (and series)?
         #   not relevant for transformers that return Primitives in transform-output
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         # todo: rename to capability:missing_values
         "capability:missing_values:removes": True,
         # is transform result always guaranteed to contain no missing values?
diff --git a/sktime/transformations/series/fourier.py b/sktime/transformations/series/fourier.py
index 5b9274d6b..1b1c9aba8 100644
--- a/sktime/transformations/series/fourier.py
+++ b/sktime/transformations/series/fourier.py
@@ -129,7 +129,7 @@ class FourierFeatures(BaseTransformer):
         "capability:unequal_length:removes": False,
         # is transform result always guaranteed to be equal length (and series)?
         #   not relevant for transformers that return Primitives in transform-output
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         # todo: rename to capability:missing_values
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
@@ -385,7 +385,7 @@ class FourierTransform(BaseTransformer):
         "fit_is_empty": True,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self):
diff --git a/sktime/transformations/series/func_transform.py b/sktime/transformations/series/func_transform.py
index fd5a84df0..a5e96cee5 100644
--- a/sktime/transformations/series/func_transform.py
+++ b/sktime/transformations/series/func_transform.py
@@ -84,7 +84,7 @@ class FunctionTransformer(BaseTransformer):
         # which mtypes do _fit/_predict support for X?
         "y_inner_mtype": "None",  # which mtypes do _fit/_predict support for y?
         "fit_is_empty": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "capability:inverse_transform": True,
     }
 
diff --git a/sktime/transformations/series/holiday/_holidayfeats.py b/sktime/transformations/series/holiday/_holidayfeats.py
index 8ad2abcde..b68dec0b9 100644
--- a/sktime/transformations/series/holiday/_holidayfeats.py
+++ b/sktime/transformations/series/holiday/_holidayfeats.py
@@ -102,7 +102,7 @@ class HolidayFeatures(BaseTransformer):
         "scitype:transform-labels": "None",
         "scitype:instancewise": True,
         "univariate-only": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "X_inner_mtype": "pd.DataFrame",
         "y_inner_mtype": "None",
         "X-y-must-have-same-index": False,
diff --git a/sktime/transformations/series/holiday/country_holidays.py b/sktime/transformations/series/holiday/country_holidays.py
index 290fdeec8..a1d7192b6 100644
--- a/sktime/transformations/series/holiday/country_holidays.py
+++ b/sktime/transformations/series/holiday/country_holidays.py
@@ -87,7 +87,7 @@ class CountryHolidaysTransformer(BaseTransformer):
         "enforce_index_type": [pandas.DatetimeIndex, pandas.PeriodIndex],
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     def __init__(
diff --git a/sktime/transformations/series/holiday/financial_holidays.py b/sktime/transformations/series/holiday/financial_holidays.py
index 2f1718dc6..dfe449376 100644
--- a/sktime/transformations/series/holiday/financial_holidays.py
+++ b/sktime/transformations/series/holiday/financial_holidays.py
@@ -87,7 +87,7 @@ class FinancialHolidaysTransformer(BaseTransformer):
         "enforce_index_type": [pandas.DatetimeIndex, pandas.PeriodIndex],
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
     }
 
     def __init__(self, market, years=None, expand=True, observed=True, name=None):
diff --git a/sktime/transformations/series/hpfilter.py b/sktime/transformations/series/hpfilter.py
index a07ffe51e..36bb50901 100644
--- a/sktime/transformations/series/hpfilter.py
+++ b/sktime/transformations/series/hpfilter.py
@@ -75,7 +75,7 @@ class HPFilter(BaseTransformer):
         # does transform return have the same time index as input X
         "capability:unequal_length": True,
         # can the transformer handle unequal length time series (if passed Panel)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "remember_data": False,  # whether all data seen is remembered as self._X
         "python_dependencies": "statsmodels",
     }
diff --git a/sktime/transformations/series/hurst.py b/sktime/transformations/series/hurst.py
index 66fed787f..a5d0b8792 100644
--- a/sktime/transformations/series/hurst.py
+++ b/sktime/transformations/series/hurst.py
@@ -62,7 +62,7 @@ class HurstExponentTransformer(BaseTransformer):
         "fit_is_empty": False,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "authors": ["phoeenniixx"],
     }
 
diff --git a/sktime/transformations/series/impute.py b/sktime/transformations/series/impute.py
index 0bcc559ad..31305c3e9 100644
--- a/sktime/transformations/series/impute.py
+++ b/sktime/transformations/series/impute.py
@@ -105,7 +105,7 @@ class Imputer(BaseTransformer):
         # which mtypes do _fit/_predict support for X?
         "y_inner_mtype": "None",  # which mtypes do _fit/_predict support for y?
         "fit_is_empty": False,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "skip-inverse-transform": True,
         "capability:inverse_transform": True,
         "univariate-only": False,
diff --git a/sktime/transformations/series/kalman_filter/_kalman_filter.py b/sktime/transformations/series/kalman_filter/_kalman_filter.py
index 3f3b95f80..0638d2cbb 100644
--- a/sktime/transformations/series/kalman_filter/_kalman_filter.py
+++ b/sktime/transformations/series/kalman_filter/_kalman_filter.py
@@ -295,7 +295,7 @@ class KalmanFilterTransformerPK(BaseKalmanFilter, BaseTransformer):
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
         "capability:unequal_length": False,
         # can the transformer handle unequal length time series (if passed Panel)?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
         "scitype:instancewise": True,  # is this an instance-wise transform?
@@ -786,7 +786,7 @@ class KalmanFilterTransformerFP(BaseKalmanFilter, BaseTransformer):
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
         "capability:unequal_length": False,
         # can the transformer handle unequal length time series (if passed Panel)?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
         "scitype:instancewise": True,  # is this an instance-wise transform?
diff --git a/sktime/transformations/series/kalman_filter/_simdkalman.py b/sktime/transformations/series/kalman_filter/_simdkalman.py
index f460660d9..3d161ce29 100644
--- a/sktime/transformations/series/kalman_filter/_simdkalman.py
+++ b/sktime/transformations/series/kalman_filter/_simdkalman.py
@@ -149,7 +149,7 @@ class KalmanFilterTransformerSIMD(BaseKalmanFilter, BaseTransformer):
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
         "capability:unequal_length": False,
         # can the transformer handle unequal length time series (if passed Panel)?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
         "scitype:instancewise": True,  # is this an instance-wise transform?
diff --git a/sktime/transformations/series/kinematic.py b/sktime/transformations/series/kinematic.py
index e26a4271a..2f0929ba5 100644
--- a/sktime/transformations/series/kinematic.py
+++ b/sktime/transformations/series/kinematic.py
@@ -77,7 +77,7 @@ class KinematicFeatures(BaseTransformer):
         "fit_is_empty": True,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     # todo: add any hyper-parameters and components to constructor
diff --git a/sktime/transformations/series/lag.py b/sktime/transformations/series/lag.py
index 215a696c3..fb95d32db 100644
--- a/sktime/transformations/series/lag.py
+++ b/sktime/transformations/series/lag.py
@@ -136,7 +136,7 @@ class Lag(BaseTransformer):
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "capability:unequal_length": True,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
         "remember_data": True,  # remember all data seen as _X
     }
@@ -458,7 +458,7 @@ class ReducerTransform(BaseTransformer):
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "capability:unequal_length": True,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
     }
 
diff --git a/sktime/transformations/series/outlier_detection.py b/sktime/transformations/series/outlier_detection.py
index e51ec4fbb..d7b5cddf6 100644
--- a/sktime/transformations/series/outlier_detection.py
+++ b/sktime/transformations/series/outlier_detection.py
@@ -63,7 +63,7 @@ class HampelFilter(BaseTransformer):
         # which mtypes do _fit/_predict support for X?
         "y_inner_mtype": "None",  # which mtypes do _fit/_predict support for y?
         "fit_is_empty": True,
-        "handles-missing-data": True,
+        "capability:missing_values": True,
         "skip-inverse-transform": True,
         "univariate-only": False,
     }
diff --git a/sktime/transformations/series/paa.py b/sktime/transformations/series/paa.py
index e125f36ea..46171e436 100644
--- a/sktime/transformations/series/paa.py
+++ b/sktime/transformations/series/paa.py
@@ -60,7 +60,7 @@ class PAA(BaseTransformer):
         "requires_y": False,
         "fit_is_empty": True,
         "capability:inverse_transform": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, frames=8, frame_size=0):
diff --git a/sktime/transformations/series/sax.py b/sktime/transformations/series/sax.py
index c9518d922..d97b514fc 100644
--- a/sktime/transformations/series/sax.py
+++ b/sktime/transformations/series/sax.py
@@ -70,7 +70,7 @@ class SAX(BaseTransformer):
         "requires_y": False,
         "fit_is_empty": True,
         "capability:inverse_transform": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, word_size=8, alphabet_size=5, frame_size=0):
diff --git a/sktime/transformations/series/subsequence_extraction.py b/sktime/transformations/series/subsequence_extraction.py
index 7ee16aa88..33a985551 100644
--- a/sktime/transformations/series/subsequence_extraction.py
+++ b/sktime/transformations/series/subsequence_extraction.py
@@ -85,7 +85,7 @@ class SubsequenceExtractionTransformer(BaseTransformer):
         "fit_is_empty": False,
         "capability:inverse_transform": False,
         "capability:unequal_length:removes": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, aggregate_fn, subseq_len, kwargs=None, selector="max"):
diff --git a/sktime/transformations/series/summarize.py b/sktime/transformations/series/summarize.py
index 15d808f7a..314467ab9 100644
--- a/sktime/transformations/series/summarize.py
+++ b/sktime/transformations/series/summarize.py
@@ -209,7 +209,7 @@ class WindowSummarizer(BaseTransformer):
         ],  # which mtypes do _fit/_predict support for X?
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": False,  # can the transformer handle multivariate X?
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "X-y-must-have-same-index": False,  # can estimator handle different X/y index?
         "enforce_index_type": None,  # index type that needs to be enforced in X/y
         "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
diff --git a/sktime/transformations/series/temporian.py b/sktime/transformations/series/temporian.py
index 01ddf472a..163b73c0c 100644
--- a/sktime/transformations/series/temporian.py
+++ b/sktime/transformations/series/temporian.py
@@ -64,7 +64,7 @@ class TemporianTransformer(BaseTransformer):
         "fit_is_empty": True,
         "capability:inverse_transform": False,
         "capability:unequal_length": True,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
     }
 
     def __init__(self, function, compile=False):
diff --git a/sktime/transformations/series/time_since.py b/sktime/transformations/series/time_since.py
index 7d89e5a19..67f522d55 100644
--- a/sktime/transformations/series/time_since.py
+++ b/sktime/transformations/series/time_since.py
@@ -100,7 +100,7 @@ class TimeSince(BaseTransformer):
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "capability:unequal_length": True,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": True,  # can estimator handle missing data?
+        "capability:missing_values": True,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
     }
 
diff --git a/sktime/transformations/series/vmd.py b/sktime/transformations/series/vmd.py
index ed95a66c8..07dba35aa 100644
--- a/sktime/transformations/series/vmd.py
+++ b/sktime/transformations/series/vmd.py
@@ -131,7 +131,7 @@ class VmdTransformer(BaseTransformer):
         "skip-inverse-transform": False,
         "capability:unequal_length": False,
         "capability:unequal_length:removes": False,
-        "handles-missing-data": False,
+        "capability:missing_values": False,
         "capability:missing_values:removes": False,
     }
 
diff --git a/sktime/utils/estimators/_forecasters.py b/sktime/utils/estimators/_forecasters.py
index 8f331311a..58983b5fa 100644
--- a/sktime/utils/estimators/_forecasters.py
+++ b/sktime/utils/estimators/_forecasters.py
@@ -30,7 +30,7 @@ class MockUnivariateForecasterLogger(BaseForecaster, _MockEstimatorMixin):
     _tags = {
         "scitype:y": "univariate",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.Series",  # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for X?
         "requires-fh-in-fit": False,  # is forecasting horizon already required in fit?
@@ -212,7 +212,7 @@ class MockForecaster(BaseForecaster):
     _tags = {
         "scitype:y": "both",  # which y are fine? univariate/multivariate/both
         "ignores-exogeneous-X": False,  # does estimator ignore the exogeneous X?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         "y_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for y?
         "X_inner_mtype": "pd.DataFrame",  # which types do _fit, _predict, assume for X?
         "requires-fh-in-fit": False,  # is forecasting horizon already required in fit?


Test patch:
diff --git a/sktime/forecasting/compose/tests/test_pipeline.py b/sktime/forecasting/compose/tests/test_pipeline.py
index d9db79eb8..28f1e4223 100644
--- a/sktime/forecasting/compose/tests/test_pipeline.py
+++ b/sktime/forecasting/compose/tests/test_pipeline.py
@@ -416,7 +416,7 @@ def test_tag_handles_missing_data():
     """
     forecaster = MockForecaster()
     # make sure that test forecaster can't handle missing data
-    forecaster.set_tags(**{"handles-missing-data": False})
+    forecaster.set_tags(**{"capability:missing_values": False})
 
     y = _make_series()
     y.iloc[10] = np.nan
diff --git a/sktime/forecasting/tests/test_naive.py b/sktime/forecasting/tests/test_naive.py
index 243820037..498491dbd 100644
--- a/sktime/forecasting/tests/test_naive.py
+++ b/sktime/forecasting/tests/test_naive.py
@@ -282,7 +282,7 @@ def test_strategy_mean_and_last_seasonal_additional_combinations(
     forecast_data = model.predict(fh)
 
     # Make sure that the model (object) reports that it handles missing data
-    assert model.get_tag("handles-missing-data")
+    assert model.get_tag("capability:missing_values")
 
     if sp < window_length:
         # We expect a perfect forecast given our perfectly cyclic data
diff --git a/sktime/registry/tests/test_lookup.py b/sktime/registry/tests/test_lookup.py
index 9b20de9a9..7e8c07606 100644
--- a/sktime/registry/tests/test_lookup.py
+++ b/sktime/registry/tests/test_lookup.py
@@ -216,7 +216,9 @@ def test_all_estimators_return_tags_bad_arg(return_tags):
         _ = all_estimators(return_tags=return_tags)
 
 
-@pytest.mark.parametrize("tag_name", ["capability:pred_int", "handles-missing-data"])
+@pytest.mark.parametrize(
+    "tag_name", ["capability:pred_int", "capability:missing_values"]
+)
 @pytest.mark.parametrize("tag_value", [True, False])
 def test_all_estimators_tag_filter(tag_value, tag_name):
     """Test that tag filtering returns estimators as expected."""
diff --git a/sktime/transformations/tests/test_check_estimator.py b/sktime/transformations/tests/test_check_estimator.py
index 9e6c61bb6..750197a7c 100644
--- a/sktime/transformations/tests/test_check_estimator.py
+++ b/sktime/transformations/tests/test_check_estimator.py
@@ -41,7 +41,7 @@ class _TransformChangeNInstances(BaseTransformer):
         # can the transformer handle unequal length time series (if passed Panel)?
         "capability:unequal_length:removes": False,
         # is transform result always guaranteed to be equal length (and series)?
-        "handles-missing-data": False,  # can estimator handle missing data?
+        "capability:missing_values": False,  # can estimator handle missing data?
         # todo: rename to capability:missing_values
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
diff --git a/sktime/transformations/tests/test_compose.py b/sktime/transformations/tests/test_compose.py
index 17530c23e..8eb392b77 100644
--- a/sktime/transformations/tests/test_compose.py
+++ b/sktime/transformations/tests/test_compose.py
@@ -157,9 +157,9 @@ def test_missing_unequal_tag_inference():
     assert t1.get_tag("capability:unequal_length")
     assert t1.get_tag("capability:unequal_length:removes")
     assert not t2.get_tag("capability:unequal_length:removes")
-    assert t3.get_tag("handles-missing-data")
+    assert t3.get_tag("capability:missing_values")
     assert t3.get_tag("capability:missing_values:removes")
-    assert not t4.get_tag("handles-missing-data")
+    assert not t4.get_tag("capability:missing_values")
     assert not t4.get_tag("capability:missing_values:removes")