Repository: aeon-toolkit/aeon
Pull request #831 (2023-10-13 20:00:17)

[MNT] Add workflow to update `pre-commit` hooks and make a PR
This workflow will automatically update the `pre-commit` hooks in .pre-commit-config.yaml and create a PR with the changes (I hope).

It is difficult to test this actually works, but seems low risk even if it does not initially.

sweep-ai[bot]: ## Apply [Sweep Rules](https://docs.sweep.dev/usage/config#rules) to your PR?
- [ ] **Apply:**  There should not be large chunks of code that are just commented out. Docstrings and explanations in code are okay though.
- [ ] **Apply:**  Update the relevant API page in 'docs/api_reference/' when new public functions and classes are added and not included in the API documentation. Only add functions and classes which are not already in the relevant API documentation and avoid duplicate entries.

Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8faace28..4584d7bc 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,15 +1,18 @@
 <!--
-Thanks for contributing a pull request! Please ensure you have taken a look at our contribution guide: https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTING.md
+Thanks for contributing a pull request! Please ensure you have taken a look at our
+contribution guide: https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTING.md
 
-Feel free to delete sections of this template if they do not apply to your PR, avoid submitting a blank template or empty sections.
+Feel free to delete sections of this template if they do not apply to your PR,
+avoid submitting a blank template or empty sections.
 -->
 
 #### Reference Issues/PRs
 
 <!--
 Example: Fixes #1234. See also #3456.
-
-Please use keywords (e.g., Fixes) to create link to the issues or pull requests you resolved, so that they will automatically be closed when your pull request is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests
+Please use keywords (e.g., Fixes) to create link to the issues or pull requests
+you resolved, so that they will automatically be closed when your pull request
+is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests
 -->
 
 #### What does this implement/fix? Explain your changes.
@@ -21,25 +24,20 @@ A clear and concise description of what you have implemented.
 #### Does your contribution introduce a new dependency? If yes, which one?
 
 <!--
-If your contribution does add a dependency, we may suggest adding it as an optional/soft dependency to keep external dependencies of the core aeon package to a minimum.
--->
-
-#### What should a reviewer concentrate their feedback on?
-
-<!--
-This section is particularly useful if you have a pull request that is still in development. You can guide the reviews to focus on the parts that are ready for their comments. We suggest using bullets (indicated by * or -) and filled checkboxes [x] here
--->
-
-#### Did you add any tests for the change?
-
-<!--
-This section is useful if you have added a test in addition to the existing ones. This will ensure that further changes to these files won't introduce the same kind of bug. It is considered good practice to add tests with newly added code to enforce the fact that the code actually works. This will reduce the chance of introducing logical bugs.
+If your contribution does add a dependency, we may suggest adding it as an
+optional/soft dependency to keep external dependencies of the core aeon package
+to a minimum.
 -->
 
 #### Any other comments?
 
 <!--
-Please be aware that we are a loose team of volunteers so patience is necessary; assistance handling other issues is very welcome. We value all user contributions, no matter how minor they are. If we are slow to review, either the pull request needs some benchmarking, tinkering, convincing, etc. or more likely the reviewers are simply busy. In either case, we ask for your understanding during the review process.
+Please be aware that we are a loose team of volunteers so patience is
+necessary; assistance handling other issues is very welcome. We value all
+user contributions, no matter how minor they are. If we are slow to
+review, either the pull request needs some benchmarking, tinkering,
+convincing, etc. or more likely the reviewers are simply busy. In either
+case, we ask for your understanding during the review process.
 -->
 
 ### PR checklist
@@ -50,9 +48,18 @@ Please go through the checklist below. Please feel free to remove points if they
 
 ##### For all contributions
 - [ ] I've added myself to the [list of contributors](https://github.com/aeon-toolkit/aeon/blob/main/.all-contributorsrc).
-- [ ] Optionally, I've updated aeon's [CODEOWNERS](https://github.com/aeon-toolkit/aeon/blob/main/CODEOWNERS) to receive notifications about future changes to these files.
-- [ ] The PR title starts with either [ENH], [MNT], [DOC], or [BUG] indicating whether the PR topic is related to enhancement, maintenance, documentation, or bug.
-- [ ] For new estimators, I've added the estimator to the online documentation.
+Alternatively, you can use the [@all-contributors](https://allcontributors.org/docs/en/bot/usage)
+bot to do this for you.
+- [ ] The PR title starts with either [ENH], [MNT], [DOC], [BUG] or [GOV] indicating
+whether the PR topic is related to enhancement, maintenance, documentation, bugs or
+governance.
+
+##### For new estimators and functions
+- [ ] I've added the estimator to the online [API documentation](https://www.aeon-toolkit.org/en/latest/api_reference.html).
+
+##### For developers with write access
+- [ ] Optionally, I've updated aeon's [CODEOWNERS](https://github.com/aeon-toolkit/aeon/blob/main/CODEOWNERS)
+to receive notifications about future changes to these files.
 
 
 <!--
diff --git a/.github/workflows/pr_examples.yml b/.github/workflows/pr_examples.yml
index 0d888ab9..e36538fa 100644
--- a/.github/workflows/pr_examples.yml
+++ b/.github/workflows/pr_examples.yml
@@ -1,4 +1,4 @@
-name: PR examples
+name: PR Examples
 
 on:
   push:
diff --git a/.github/workflows/pr_opened.yml b/.github/workflows/pr_opened.yml
new file mode 100644
index 00000000..be07f7b6
--- /dev/null
+++ b/.github/workflows/pr_opened.yml
@@ -0,0 +1,37 @@
+name: PR Opened
+on:
+  pull_request_target:
+    types: [opened]
+
+permissions:
+  contents: read
+  pull-requests: write
+  issues: write
+
+jobs:
+  # based on the scikit-learn 1.3.1 PR labelers
+  labeler:
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          sparse-checkout: build_tools
+
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.10"
+
+      - name: Install PyGithub
+        run: pip install -Uq PyGithub
+
+      - name: Label pull request
+        id: label-pr
+        run: python build_tools/pr_labeler.py
+        env:
+          CONTEXT_GITHUB: ${{ toJson(github) }}
+
+      - name: Write pull request comment
+        run: python build_tools/pr_open_commenter.py ${{ steps.label-pr.outputs.title-labels }} ${{ steps.label-pr.outputs.title-labels-new }} ${{ steps.label-pr.outputs.content-labels }} ${{ steps.label-pr.outputs.content-labels-status }}
+        env:
+          CONTEXT_GITHUB: ${{ toJson(github) }}
diff --git a/.github/workflows/pr_pre_commit.yml b/.github/workflows/pr_precommit.yml
similarity index 66%
rename from .github/workflows/pr_pre_commit.yml
rename to .github/workflows/pr_precommit.yml
index 8aadc4bb..4902bb84 100644
--- a/.github/workflows/pr_pre_commit.yml
+++ b/.github/workflows/pr_precommit.yml
@@ -29,7 +29,14 @@ jobs:
       - name: List changed files
         run: echo '${{ steps.changed-files.outputs.all_changed_files }}'
 
-      - uses: pre-commit/action@v3.0.0
+      - if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'full pre-commit') }}
+        name: Full pre-commit
+        uses: pre-commit/action@v3.0.0
+        with:
+          extra_args: --all-files
+      - if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'full pre-commit') }}
+        name: Local pre-commit
+        uses: pre-commit/action@v3.0.0
         with:
           extra_args: --files ${{ steps.changed-files.outputs.all_changed_files }}
 
diff --git a/.github/workflows/precommit_autoupdate.yml b/.github/workflows/precommit_autoupdate.yml
new file mode 100644
index 00000000..4d1a74a3
--- /dev/null
+++ b/.github/workflows/precommit_autoupdate.yml
@@ -0,0 +1,27 @@
+name: Update pre-commit Hooks
+
+on:
+  schedule:
+    # every Monday at 12:30 AM UTC
+    - cron:  "30 0 * * 1"
+
+jobs:
+  pre-commit-auto-update:
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.10"
+
+      - uses: browniebroke/pre-commit-autoupdate-action@v1.0.0
+
+      - uses: peter-evans/create-pull-request@v5
+        with:
+          commit-message: "Automated `pre-commit` hook update"
+          branch: pre-commit-hooks-update
+          title: "[MNT] Automated `pre-commit` hook update"
+          body: "Automated weekly update to `.pre-commit-config.yaml` hook versions."
+          labels: maintenance, no changelog
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1ab62906..97781414 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,7 +6,7 @@ on:
       - published
 
 jobs:
-  check_manifest:
+  check-manifest:
     runs-on: ubuntu-22.04
 
     steps:
@@ -20,7 +20,7 @@ jobs:
         with:
           extra_args: check-manifest --hook-stage manual
 
-  build_project:
+  build-project:
     needs: check_manifest
     runs-on: ubuntu-22.04
 
@@ -41,8 +41,9 @@ jobs:
         with:
           name: dist
           path: dist/*
+          retention-days: 5
 
-  test_wheels:
+  test-wheels:
     needs: build_project
     runs-on: ${{ matrix.os }}
 
@@ -81,7 +82,7 @@ jobs:
       - name: Tests
         run: python -m pytest
 
-  upload_wheels:
+  upload-wheels:
     needs: test_wheels
     runs-on: ubuntu-22.04
 
diff --git a/.github/workflows/update_contributors.yml b/.github/workflows/update_contributors.yml
index 7af9db04..9fe5fa53 100644
--- a/.github/workflows/update_contributors.yml
+++ b/.github/workflows/update_contributors.yml
@@ -9,7 +9,8 @@ on:
 
 jobs:
   generate-markdown-and-commit:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-22.04
+
     steps:
       - uses: actions/checkout@v3
 
@@ -26,8 +27,8 @@ jobs:
 
       - uses: peter-evans/create-pull-request@v5
         with:
-          commit-message: "Automated CONTRIBUTORS.md update"
+          commit-message: "Automated `CONTRIBUTORS.md` update"
           branch: update_contributors
-          title: "[MNT] Automated CONTRIBUTORS.md update"
-          body: "Automated update to CONTRIBUTORS.md caused by an update to the .all-contributorsrc file."
+          title: "[MNT] Automated `CONTRIBUTORS.md` update"
+          body: "Automated update to CONTRIBUTORS.md caused by an update to the `.all-contributorsrc` file."
           labels: maintenance, no changelog
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b3c3741b..7cb189a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
 # Changelog
 
 You can find the aeon changelog on our [website](https://www.aeon-toolkit.org/en/latest/changelog.html).
+
+`aeon` adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html>) and the source code for
+all [releases](https://github.com/aeon-toolkit/aeon/releases>) is available on GitHub.
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 35dc5f63..8072b037 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,3 +1,12 @@
 # Code of Conduct
 
-You can find our Code of Conduct on our [website](https://www.aeon-toolkit.org/en/latest/get_involved/code_of_conduct.html).
+You can find our full Code of Conduct (CoC) on our [website](https://www.aeon-toolkit.org/en/latest/code_of_conduct.html).
+
+The code of conduct aims to ensure a positive and respectful experience for all
+members of the community. Community members should follow the principles set out in
+the [Python Software Foundation Code of Conduct](https://www.python.org/psf/codeofconduct/).
+
+If you want to make a CoC report, contact the general `aeon` email
+[contact@aeon-toolkit.org](mailto:contact@aeon-toolkit.org) or message a member of the
+[Code of Conduct Workgroup (COCW)](https://www.aeon-toolkit.org/en/latest/governance.html#code-of-conduct-workgroup)
+directly on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA).
diff --git a/GOVERNANCE.md b/GOVERNANCE.md
index 597baa49..f961b1ac 100644
--- a/GOVERNANCE.md
+++ b/GOVERNANCE.md
@@ -1,3 +1,16 @@
 # Governance
 
-You can find our governance guidelines on our [website](https://www.aeon-toolkit.org/en/latest/get_involved/governance.html).
+You can find our full governance guidelines on our [website](https://www.aeon-toolkit.org/en/latest/governance.html).
+
+`aeon` welcomes all contributors, and anyone can involve themselves in the community
+and contribute code, documentation, and ideas. The project is maintained by a group
+of core developers, who are responsible for reviewing and merging contributions,
+engaging with the community, and steering the project. Tasks are delegated to
+workgroups, which are groups of core developers and contributors assigned by
+core developers to fulfil a specific role.
+
+aeon's decision-making process is transparent and consensus-based.
+Most decisions involve seeking consensus among contributors and core developers.
+If consensus is not reached, a vote can be called, requiring a two-thirds majority
+of core developers for approval. The process emphasises transparency and open
+discussion, with some sensitive topics discussed privately.
diff --git a/README.md b/README.md
index 31885044..94e8d060 100644
--- a/README.md
+++ b/README.md
@@ -16,11 +16,6 @@ time series packages to provide a single framework for algorithm comparison.
 The latest `aeon` release is `v0.5.0`. You can view the full changelog
 [here](https://www.aeon-toolkit.org/en/latest/changelog.html).
 
-```diff
-- The deprecation policy is currently suspended, be careful with the version bounds used when including aeon as a dependency.
-- The policy will return in version v0.6.0, but in the mean time the suspension allows us to quickly develop and maintain the package in the forking transition period.
-```
-
 Our webpage and documentation is available at https://aeon-toolkit.org.
 
 | Overview      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
diff --git a/aeon/annotation/eagglo.py b/aeon/annotation/eagglo.py
index 626bfe3a..1ab2ccde 100644
--- a/aeon/annotation/eagglo.py
+++ b/aeon/annotation/eagglo.py
@@ -471,13 +471,13 @@ def get_distance_matrix(X, Y, alpha) -> float:
     return np.power(dist, alpha).mean()
 
 
-@njit(nopython=True, fastmath=True)
+@njit(fastmath=True, cache=True)
 def get_distance_single(X, Y, alpha) -> float:
     dist = euclidean(X, Y)
     return np.power(dist, alpha)  # .mean()
 
 
-@njit(nopython=True, fastmath=True)
+@njit(fastmath=True, cache=True)
 def euclidean_matrix_to_matrix(a, b):
     """Compute the Euclidean distances between the rows of two matrices."""
     n, m = a.shape[0], b.shape[0]
diff --git a/aeon/classification/deep_learning/inception_time.py b/aeon/classification/deep_learning/inception_time.py
index c4196d8d..56d30bf9 100644
--- a/aeon/classification/deep_learning/inception_time.py
+++ b/aeon/classification/deep_learning/inception_time.py
@@ -24,103 +24,101 @@ class InceptionTimeClassifier(BaseClassifier):
 
     Parameters
     ----------
-        n_classifiers       : int, default = 5,
-            the number of Inception models used for the
-            Ensemble in order to create
-            InceptionTime.
-        depth               : int, default = 6,
+    n_classifiers       : int, default = 5,
+        the number of Inception models used for the
+        Ensemble in order to create
+        InceptionTime.
+    depth               : int, default = 6,
             the number of inception modules used
-        nb_filters          : int or list of int32, default = 32,
-            the number of filters used in one inception
-            module, if not a list,
-            the same number of filters is used in
-            all inception modules
-        nb_conv_per_layer   : int or list of int, default = 3,
-            the number of convolution layers in each inception
-            module, if not a list,
-            the same number of convolution layers is used
-            in all inception modules
-        kernel_size         : int or list of int, default = 40,
-            the head kernel size used for each inception
-            module, if not a list,
-            the same is used in all inception modules
-        use_max_pooling     : bool or list of bool, default = True,
-            conditioning whether or not to use max pooling layer
-            in inception modules,if not a list,
-            the same is used in all inception modules
-        max_pool_size       : int or list of int, default = 3,
-            the size of the max pooling layer, if not a list,
-            the same is used in all inception modules
-        strides             : int or list of int, default = 1,
-            the strides of kernels in convolution layers for each
-            inception module, if not a list,
-            the same is used in all inception modules
-        dilation_rate       : int or list of int, default = 1,
-            the dilation rate of convolutions in each inception
-            module, if not a list,
-            the same is used in all inception modules
-        padding             : str or list of str, default = "same",
-            the type of padding used for convoltuon for each
-            inception module, if not a list,
-            the same is used in all inception modules
-        activation          : str or list of str, default = "relu",
-            the activation function used in each inception
-            module, if not a list,
-            the same is used in all inception modules
-        use_bias            : bool or list of bool, default = False,
-            conditioning whether or not convolutions should
-            use bias values in each inception
-            module, if not a list,
-            the same is used in all inception modules
-        use_residual : bool, default = True,
-            condition whether or not to use residual
-            connections all over Inception
-        use_bottleneck : bool, default = True,
-            condition whether or not to use bottlenecks
-            all over Inception
-        bottleneck_size : int, default = 32,
-            the bottleneck size in case use_bottleneck = True
-        use_custom_filters : bool, default = True,
-            condition on whether or not to use custom
-            filters in the first inception module
-        batch_size : int, default = 64
-            the number of samples per gradient update.
-        use_mini_batch_size : bool, default = False
-            condition on using the mini batch size
-            formula Wang et al.
-        n_epochs : int, default = 1500
-            the number of epochs to train the model.
-        callbacks : callable or None, default
-        ReduceOnPlateau and ModelCheckpoint
-            list of tf.keras.callbacks.Callback objects.
-        file_path : str, default = "./"
-            file_path when saving model_Checkpoint callback
-        save_best_model : bool, default = False
-            Whether or not to save the best model, if the
-            modelcheckpoint callback is used by default,
-            this condition, if True, will prevent the
-            automatic deletion of the best saved model from
-            file and the user can choose the file name
-        save_last_model     : bool, default = False
-            Whether or not to save the last model, last
-            epoch trained, using the base class method
-            save_last_model_to_file
-        best_file_name      : str, default = "best_model"
-            The name of the file of the best model, if
-            save_best_model is set to False, this parameter
-            is discarded
-        last_file_name      : str, default = "last_model"
-            The name of the file of the last model, if
-            save_last_model is set to False, this parameter
-            is discarded
-        random_state        : int, default = 0
-            seed to any needed random actions.
-        verbose             : boolean, default = False
-            whether to output extra information
-        optimizer           : keras optimizer, default = Adam
-        loss                : keras loss,
-                              default = categorical_crossentropy
-        metrics             : keras metrics, default = None,
+    nb_filters          : int or list of int32, default = 32,
+        the number of filters used in one inception
+        module, if not a list,
+        the same number of filters is used in
+        all inception modules
+    nb_conv_per_layer   : int or list of int, default = 3,
+        the number of convolution layers in each inception
+        module, if not a list,
+        the same number of convolution layers is used
+        in all inception modules
+    kernel_size         : int or list of int, default = 40,
+        the head kernel size used for each inception
+        module, if not a list,
+        the same is used in all inception modules
+    use_max_pooling     : bool or list of bool, default = True,
+        conditioning whether or not to use max pooling layer
+        in inception modules,if not a list,
+        the same is used in all inception modules
+    max_pool_size       : int or list of int, default = 3,
+        the size of the max pooling layer, if not a list,
+        the same is used in all inception modules
+    strides             : int or list of int, default = 1,
+        the strides of kernels in convolution layers for each
+        inception module, if not a list,
+        the same is used in all inception modules
+    dilation_rate       : int or list of int, default = 1,
+        the dilation rate of convolutions in each inception
+        module, if not a list,
+        the same is used in all inception modules
+    padding             : str or list of str, default = "same",
+        the type of padding used for convoltuon for each
+        inception module, if not a list,
+        the same is used in all inception modules
+    activation          : str or list of str, default = "relu",
+        the activation function used in each inception
+        module, if not a list,
+        the same is used in all inception modules
+    use_bias            : bool or list of bool, default = False,
+        conditioning whether or not convolutions should
+        use bias values in each inception
+        module, if not a list,
+        the same is used in all inception modules
+    use_residual : bool, default = True,
+        condition whether or not to use residual
+        connections all over Inception
+    use_bottleneck : bool, default = True,
+        condition whether or not to use bottlenecks
+        all over Inception
+    bottleneck_size : int, default = 32,
+        the bottleneck size in case use_bottleneck = True
+    use_custom_filters : bool, default = True,
+        condition on whether or not to use custom
+        filters in the first inception module
+    batch_size : int, default = 64
+        the number of samples per gradient update.
+    use_mini_batch_size : bool, default = False
+        condition on using the mini batch size
+        formula Wang et al.
+    n_epochs : int, default = 1500
+        the number of epochs to train the model.
+    callbacks : callable or None, default = ReduceOnPlateau and ModelCheckpoint
+        list of tf.keras.callbacks.Callback objects.
+    file_path : str, default = "./"
+        file_path when saving model_Checkpoint callback
+    save_best_model : bool, default = False
+        Whether or not to save the best model, if the
+        modelcheckpoint callback is used by default,
+        this condition, if True, will prevent the
+        automatic deletion of the best saved model from
+        file and the user can choose the file name
+    save_last_model     : bool, default = False
+        Whether or not to save the last model, last
+        epoch trained, using the base class method
+        save_last_model_to_file
+    best_file_name      : str, default = "best_model"
+        The name of the file of the best model, if
+        save_best_model is set to False, this parameter
+        is discarded
+    last_file_name      : str, default = "last_model"
+        The name of the file of the last model, if
+        save_last_model is set to False, this parameter
+        is discarded
+    random_state        : int, default = 0
+        seed to any needed random actions.
+    verbose             : boolean, default = False
+        whether to output extra information
+    optimizer           : keras optimizer, default = Adam
+    loss                : keras loss, default = categorical_crossentropy
+    metrics             : keras metrics, default = None,
         will be set to accuracy as default if None
 
     Notes
diff --git a/aeon/classification/dictionary_based/_weasel_v2.py b/aeon/classification/dictionary_based/_weasel_v2.py
index 72e0b4c3..6f8e73fa 100644
--- a/aeon/classification/dictionary_based/_weasel_v2.py
+++ b/aeon/classification/dictionary_based/_weasel_v2.py
@@ -45,7 +45,8 @@ class WEASEL_V2(BaseClassifier):
     (2) Maximal window length: Typically chosen from
         24, 44 or 84 depending on the time series length.
     (3) Ensemble size: Typically chosen from 50, 100, 150, to derive
-        a feature vector of roughly 20𝑘 up to 70𝑘 features (distinct words).
+        a feature vector of roughly 20k up to 70k features
+        (distinct words).
 
     From the other parameters passed, WEASEL chosen random values for each set
     of configurations. E.g. for each of 150 configurations, a random value is chosen
@@ -274,7 +275,7 @@ class WEASELTransformerV2:
     (2) Maximal window length: Typically chosen from
         24, 44 or 84 depending on the time series length.
     (3) Ensemble size: Typically chosen from 50, 100, 150, to derive
-        a feature vector of roughly 20𝑘 up to 70𝑘 features (distinct words).
+        a feature vector of roughly 20k up to 70k features (distinct words).
 
     From the other parameters passed, WEASEL chosen random values for each set
     of configurations. E.g. for each of 150 configurations, a random value is chosen
diff --git a/aeon/classification/shapelet_based/_rdst.py b/aeon/classification/shapelet_based/_rdst.py
index 05629f16..f57ab249 100644
--- a/aeon/classification/shapelet_based/_rdst.py
+++ b/aeon/classification/shapelet_based/_rdst.py
@@ -15,7 +15,9 @@ from sklearn.preprocessing import StandardScaler
 
 from aeon.base._base import _clone_estimator
 from aeon.classification.base import BaseClassifier
-from aeon.transformations.collection import RandomDilatedShapeletTransform
+from aeon.transformations.collection.shapelet_based import (
+    RandomDilatedShapeletTransform,
+)
 
 
 class RDSTClassifier(BaseClassifier):
diff --git a/aeon/distances/__init__.py b/aeon/distances/__init__.py
index 38b2f56b..a917d870 100644
--- a/aeon/distances/__init__.py
+++ b/aeon/distances/__init__.py
@@ -1,4 +1,5 @@
 """Distance computation."""
+
 __author__ = ["chrisholder", "TonyBagnall", "baraline"]
 
 __all__ = [
diff --git a/aeon/forecasting/arima.py b/aeon/forecasting/arima.py
index 0da29044..e04645cb 100644
--- a/aeon/forecasting/arima.py
+++ b/aeon/forecasting/arima.py
@@ -29,10 +29,10 @@ class AutoARIMA(_PmdArimaAdapter):
     Canova-Hansen to determine the optimal order of seasonal differencing, D.
 
     In order to find the best model, auto-ARIMA optimizes for a given
-    information_criterion, one of (‘aic’, ‘aicc’, ‘bic’, ‘hqic’, ‘oob’)
+    information_criterion, one of ('aic', 'aicc', 'bic', 'hqic', 'oob')
     (Akaike Information Criterion, Corrected Akaike Information Criterion,
     Bayesian Information Criterion, Hannan-Quinn Information Criterion, or
-    “out of bag”–for validation scoring–respectively) and returns the ARIMA
+    "out of bag"–for validation scoring–respectively) and returns the ARIMA
     which minimizes the value.
 
     Note that due to stationarity issues, auto-ARIMA might not find a suitable
@@ -46,7 +46,7 @@ class AutoARIMA(_PmdArimaAdapter):
     ----------
     start_p : int, optional (default=2)
         The starting value of p, the order (or number of time lags)
-        of the auto-regressive (“AR”) model. Must be a positive integer.
+        of the auto-regressive ("AR") model. Must be a positive integer.
     d : int, optional (default=None)
         The order of first-differencing. If None (by default), the value will
         automatically be selected based on the results of the test (i.e.,
@@ -55,7 +55,7 @@ class AutoARIMA(_PmdArimaAdapter):
         value). Must be a positive integer or None. Note that if d is None,
         the runtime could be significantly longer.
     start_q : int, optional (default=2)
-        The starting value of q, the order of the moving-average (“MA”) model.
+        The starting value of q, the order of the moving-average ("MA") model.
         Must be a positive integer.
     max_p : int, optional (default=5)
         The maximum value of p, inclusive. Must be a positive integer greater
@@ -104,8 +104,8 @@ class AutoARIMA(_PmdArimaAdapter):
         Whether the time-series is stationary and d should be set to zero.
     information_criterion : str, optional (default='aic')
         The information criterion used to select the best ARIMA model. One of
-        pmdarima.arima.auto_arima.VALID_CRITERIA, (‘aic’, ‘bic’, ‘hqic’,
-        ‘oob’).
+        pmdarima.arima.auto_arima.VALID_CRITERIA, ('aic', 'bic', 'hqic',
+        'oob').
     alpha : float, optional (default=0.05)
         Level of the test for testing significance.
     test : str, optional (default='kpss')
@@ -122,8 +122,8 @@ class AutoARIMA(_PmdArimaAdapter):
         over-fit the model.
     n_jobs : int, optional (default=1)
         The number of models to fit in parallel in the case of a grid search
-        (stepwise=False). Default is 1, but -1 can be used to designate “as
-        many as possible”.
+        (stepwise=False). Default is 1, but -1 can be used to designate "as
+        many as possible".
     start_params : array-like, optional (default=None)
         Starting parameters for ARMA(p,q). If None, the default is given by
         ARMA._fit_start_params.
@@ -162,7 +162,7 @@ class AutoARIMA(_PmdArimaAdapter):
         be squelched.
     error_action : str, optional (default='warn')
         If unable to fit an ARIMA due to stationarity issues, whether to warn
-        (‘warn’), raise the ValueError (‘raise’) or ignore (‘ignore’). Note
+        ('warn'), raise the ValueError ('raise') or ignore ('ignore'). Note
         that the default behavior is to warn, and fits that fail will be
         returned as None. This is the recommended behavior, as statsmodels
         ARIMA and SARIMAX models hit bugs periodically that can cause an
@@ -175,14 +175,14 @@ class AutoARIMA(_PmdArimaAdapter):
         fit.
     random : bool, optional (default='False')
         Similar to grid searches, auto_arima provides the capability to
-        perform a “random search” over a hyper-parameter space. If random is
+        perform a "random search" over a hyper-parameter space. If random is
         True, rather than perform an exhaustive search or stepwise search,
         only n_fits ARIMA models will be fit (stepwise must be False for this
         option to do anything).
     random_state : int, long or numpy RandomState, optional (default=None)
         The PRNG for when random=True. Ensures replicable testing and results.
     n_fits : int, optional (default=10)
-        If random is True and a “random search” is going to be performed,
+        If random is True and a "random search" is going to be performed,
         n_iter is the number of ARIMA models to be fit.
     out_of_sample_size : int, optional (default=0)
         The number of examples from the tail of the time series to hold out
@@ -197,7 +197,7 @@ class AutoARIMA(_PmdArimaAdapter):
             > Append [5, 6] to end of self.arima_res_.data.endog values
     scoring : str, optional (default='mse')
         If performing validation (i.e., if out_of_sample_size > 0), the metric
-        to use for scoring the out-of-sample data. One of (‘mse’, ‘mae’)
+        to use for scoring the out-of-sample data. One of ('mse', 'mae')
     scoring_args : dict, optional (default=None)
         A dictionary of key-word arguments to be passed to the scoring metric.
     with_intercept : bool, optional (default=True)
diff --git a/aeon/forecasting/dynamic_factor.py b/aeon/forecasting/dynamic_factor.py
index 2fbf0e8d..b4ab8ed3 100644
--- a/aeon/forecasting/dynamic_factor.py
+++ b/aeon/forecasting/dynamic_factor.py
@@ -24,8 +24,8 @@ class DynamicFactor(_StatsModelsAdapter):
         The order of vector autoregression followed by factors.
     error_cov_type : {'scalar','diagonal','unstructured'} ,default = 'diagonal'
         The structure of the covariance matrix of the observation error term, where
-        “unstructured” puts no restrictions on the matrix, “diagonal” requires it
-        to be any diagonal matrix (uncorrelated errors), and “scalar” requires it
+        "unstructured" puts no restrictions on the matrix, "diagonal" requires it
+        to be any diagonal matrix (uncorrelated errors), and "scalar" requires it
         to be a scalar times the identity matrix.
     error_order : int , default = 0
         The order of the vector autoregression followed by the observation error
@@ -45,37 +45,37 @@ class DynamicFactor(_StatsModelsAdapter):
          describes whether or not start_params also includes the fixed parameters,
           in addition to the free parameters.
     cov_type : {'opg','oim','approx','robust','robust_approx','none'},default = 'opg'
-        ‘opg’ for the outer product of gradient estimator
-        ‘oim’ for the observed information matrix estimator, calculated
+        'opg' for the outer product of gradient estimator
+        'oim' for the observed information matrix estimator, calculated
         using the method of Harvey (1989)
-        ‘approx’ for the observed information matrix estimator, calculated using
+        'approx' for the observed information matrix estimator, calculated using
          a numerical approximation of the Hessian matrix.
-        ‘robust’ for an approximate (quasi-maximum likelihood) covariance matrix
+        'robust' for an approximate (quasi-maximum likelihood) covariance matrix
          that may be valid even in the presence of some misspecifications.
-          Intermediate calculations use the ‘oim’ method.
-        ‘robust_approx’ is the same as ‘robust’ except that the intermediate
-        calculations use the ‘approx’ method.
-        ‘none’ for no covariance matrix calculation
+          Intermediate calculations use the 'oim' method.
+        'robust_approx' is the same as 'robust' except that the intermediate
+        calculations use the 'approx' method.
+        'none' for no covariance matrix calculation
     cov_kwds :dict or None , default = None
-        ‘approx_complex_step’ : bool, optional - If True, numerical approximations are
+        'approx_complex_step' : bool, optional - If True, numerical approximations are
          computed using complex-step methods. If False, numerical approximations are
          computed using finite difference methods. Default is True.
-        ‘approx_centered’ : bool, optional - If True, numerical approximations computed
+        'approx_centered' : bool, optional - If True, numerical approximations computed
         using finite difference methods use a centered approximation. Default is False.
     method : str , 'lbfgs'
-        ‘newton’ for Newton-Raphson
-        ‘nm’ for Nelder-Mead
-        ‘bfgs’ for Broyden-Fletcher-Goldfarb-Shanno (BFGS)
-        ‘lbfgs’ for limited-memory BFGS with optional box constraints
-        ‘powell’ for modified Powell’s method
-        ‘cg’ for conjugate gradient
-        ‘ncg’ for Newton-conjugate gradient
-        ‘basinhopping’ for global basin-hopping solver
+        'newton' for Newton-Raphson
+        'nm' for Nelder-Mead
+        'bfgs' for Broyden-Fletcher-Goldfarb-Shanno (BFGS)
+        'lbfgs' for limited-memory BFGS with optional box constraints
+        'powell' for modified Powell's method
+        'cg' for conjugate gradient
+        'ncg' for Newton-conjugate gradient
+        'basinhopping' for global basin-hopping solver
     maxiter : int , optional ,default = 50
         The maximum number of iterations to perform.
     full_output : bool , default = 1
         Set to True to have all available output in the
-        Results object’s mle_retvals attribute.
+        Results object's mle_retvals attribute.
         The output is dependent on the solver.
     disp : bool ,   default = 5
         Set to True to print convergence messages.
@@ -85,16 +85,16 @@ class DynamicFactor(_StatsModelsAdapter):
     return_params : bool ,default = False
         Whether or not to return only the array of maximizing parameters.
     optim_score : {'harvey','approx'} , default = None
-        The method by which the score vector is calculated. ‘harvey’ uses the method
-        from Harvey (1989), ‘approx’ uses either finite difference or
+        The method by which the score vector is calculated. 'harvey' uses the method
+        from Harvey (1989), 'approx' uses either finite difference or
         complex step differentiation depending upon the value of optim_complex_step,
         and None uses the built-in gradient approximation of the optimizer.
     optim_complex_step : bool , default = True
         Whether or not to use complex step differentiation
         when approximating the score; if False, finite difference approximation is used.
     optim_hessian : {'opg','oim','approx'} , default = None
-        ‘opg’ uses outer product of gradients, ‘oim’ uses the information
-        matrix formula from Harvey (1989), and ‘approx’ uses numerical approximation.
+        'opg' uses outer product of gradients, 'oim' uses the information
+        matrix formula from Harvey (1989), and 'approx' uses numerical approximation.
     low_memory : bool , default = False
         If set to True, techniques are applied to substantially reduce memory usage.
         If used, some features of the results object will not be available
@@ -429,15 +429,15 @@ class DynamicFactor(_StatsModelsAdapter):
             If unspecified, but the model has been initialized,
             then that initialization is used.
             This must be specified if anchor is anything
-            other than “start” or 0.
+            other than "start" or 0.
         anchor : int,str,or datetime , optional
             Starting point from which to begin the simulations; type depends
             on the index of the given endog model.
-            Two special cases are the strings ‘start’ and ‘end’,
+            Two special cases are the strings 'start' and 'end',
             which refer to starting at the beginning and end of the sample,
             respectively. If a date/time index was provided to the model,
             then this argument can be a date string to parse or a datetime type.
-            Otherwise, an integer index should be given. Default is ‘start’.
+            Otherwise, an integer index should be given. Default is 'start'.
         repetitions : int , optional
             Number of simulated paths to generate. Default is 1 simulated path
 
@@ -512,7 +512,7 @@ class DynamicFactor(_StatsModelsAdapter):
             the last significant autocorrelation.
             In this case, a moving average model is assumed for the data
             and the standard errors for the confidence intervals should be generated
-             using Bartlett’s formula.
+             using Bartlett's formula.
         acf_kwargs : dict , optional
             Optional dictionary of keyword arguments that are directly
             passed on to the correlogram Matplotlib plot produced by plot_acf().
diff --git a/aeon/forecasting/structural.py b/aeon/forecasting/structural.py
index 5490be43..d61b80f6 100644
--- a/aeon/forecasting/structural.py
+++ b/aeon/forecasting/structural.py
@@ -176,8 +176,8 @@ class UnobservedComponents(_StatsModelsAdapter):
 
     References
     ----------
-    .. [1] Seabold, Skipper, and Josef Perktold. “statsmodels: Econometric
-       and statistical modeling with python.” Proceedings of the 9th Python
+    .. [1] Seabold, Skipper, and Josef Perktold. "statsmodels: Econometric
+       and statistical modeling with python." Proceedings of the 9th Python
        in Science Conference. 2010.
 
     .. [2] Durbin, James, and Siem Jan Koopman. 2012.
diff --git a/aeon/registry/_tags.py b/aeon/registry/_tags.py
index 0f3e6254..df05eaf9 100644
--- a/aeon/registry/_tags.py
+++ b/aeon/registry/_tags.py
@@ -153,7 +153,7 @@ ESTIMATOR_TAG_REGISTER = [
                 "pd-multiindex",
                 "numpy3D",
                 "df-list",
-                "numpy-list",
+                "np-list",
             ],
         ),
         "which data structure is the internal _fit/_predict able to deal with?",
@@ -161,14 +161,20 @@ ESTIMATOR_TAG_REGISTER = [
     (
         "input_data_type",
         "transformer",
-        ("list", ["Series", "Panel", "Collection"]),
-        "The abstract data type of the transformer input X",
+        ("list", ["Series", "Collection", "Panel"]),
+        "The input abstract data type of the transformer, input X. Series "
+        "indicates a single series input, Collection indicates a collection of "
+        "time series. Panel is a legacy term and equivalent to Collection.",
     ),
     (
         "output_data_type",
         "transformer",
-        ("list", ["Series", "Primitives", "Panel"]),
-        "The input abstract data type of the transformer output, the transformed X",
+        ("list", ["Tabular", "Series", "Collection", "Primitives", "Panel"]),
+        "The output abstract data type of the transformer output, the transformed X. "
+        "Tabular indicates 2D output where rows are cases and unordered attributes are "
+        "columns. Series indicates a single series output and collection indicates "
+        "output is a collection of time series.  Primitives is a legacy term for "
+        "Tabular and Panel for Collection.",
     ),
     (
         "instancewise",
diff --git a/aeon/transformations/base.py b/aeon/transformations/base.py
index b36703ae..868f5c2f 100644
--- a/aeon/transformations/base.py
+++ b/aeon/transformations/base.py
@@ -96,13 +96,9 @@ class BaseTransformer(BaseEstimator):
     # default tag values - these typically make the "safest" assumption
     _tags = {
         "input_data_type": "Series",
-        # The type of X: Series, or Panel
         "output_data_type": "Series",
-        # The type is returned: Primitives, Series, Panel
         "transform_labels": "None",
-        # what is the scitype of y: None (not needed), Primitives, Series, Panel
-        "instancewise": True,  # is this an instance-wise transform?
-        "capability:inverse_transform": False,  # can the transformer inverse transform?
+        "instancewise": True,
         "univariate-only": False,  # can the transformer handle multivariate X?
         "X_inner_mtype": "pd.DataFrame",  # which mtypes do _fit/_predict support for X?
         # this can be a Panel mtype even if transform-input is Series, vectorized
@@ -114,10 +110,9 @@ class BaseTransformer(BaseEstimator):
         "transform-returns-same-time-index": False,
         # does transform return have the same time index as input X
         "skip-inverse-transform": False,  # is inverse-transform skipped when called?
+        "capability:inverse_transform": False,  # can the transformer inverse transform?
         "capability:unequal_length": True,
-        # 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)?
         "capability:missing_values": False,  # can estimator handle missing data?
         "capability:missing_values:removes": False,
         # is transform result always guaranteed to contain no missing values?
diff --git a/aeon/transformations/bootstrap/_mbb.py b/aeon/transformations/bootstrap/_mbb.py
index 772a51c4..2190a539 100644
--- a/aeon/transformations/bootstrap/_mbb.py
+++ b/aeon/transformations/bootstrap/_mbb.py
@@ -164,11 +164,8 @@ class STLBootstrapTransformer(BaseTransformer):
     """
 
     _tags = {
-        # todo: what is the scitype of X: Series, or Panel
         "input_data_type": "Series",
-        # todo: what scitype is returned: Primitives, Series, Panel
         "output_data_type": "Panel",
-        # todo: what is the scitype of y: None (not needed), Primitives, Series, Panel
         "transform_labels": "None",
         "instancewise": True,  # is this an instance-wise transform?
         "X_inner_mtype": "pd.DataFrame",  # which mtypes do _fit/_predict support for X?
@@ -478,11 +475,8 @@ class MovingBlockBootstrapTransformer(BaseTransformer):
     """
 
     _tags = {
-        # todo: what is the scitype of X: Series, or Panel
         "input_data_type": "Series",
-        # todo: what scitype is returned: Primitives, Series, Panel
         "output_data_type": "Panel",
-        # todo: what is the scitype of y: None (not needed), Primitives, Series, Panel
         "transform_labels": "None",
         "instancewise": True,  # is this an instance-wise transform?
         "X_inner_mtype": "pd.DataFrame",  # which mtypes do _fit/_predict support for X?
diff --git a/aeon/transformations/collection/__init__.py b/aeon/transformations/collection/__init__.py
index 5aeef919..89d87aed 100644
--- a/aeon/transformations/collection/__init__.py
+++ b/aeon/transformations/collection/__init__.py
@@ -20,10 +20,8 @@ __all__ = [
     "Tabularizer",
     "TimeBinner",
     "IntervalSegmenter",
-    "SlidingWindowSegmenter",
     "RandomIntervalSegmenter",
-    "RandomShapeletTransform",
-    "RandomDilatedShapeletTransform",
+    "SlidingWindowSegmenter",
     "SlopeTransformer",
     "SevenNumberSummaryTransformer",
     "SupervisedIntervals",
@@ -36,7 +34,6 @@ __all__ = [
 from aeon.transformations.collection._collection_wrapper import (
     CollectionToSeriesWrapper,
 )
-from aeon.transformations.collection._scaler import TimeSeriesScaler
 from aeon.transformations.collection.acf import AutocorrelationFunctionTransformer
 from aeon.transformations.collection.ar_coefficient import ARCoefficientTransformer
 from aeon.transformations.collection.base import BaseCollectionTransformer
@@ -53,15 +50,12 @@ from aeon.transformations.collection.pad import PaddingTransformer
 from aeon.transformations.collection.periodogram import PeriodogramTransformer
 from aeon.transformations.collection.random_intervals import RandomIntervals
 from aeon.transformations.collection.reduce import Tabularizer, TimeBinner
+from aeon.transformations.collection.scaler import TimeSeriesScaler
 from aeon.transformations.collection.segment import (
     IntervalSegmenter,
     RandomIntervalSegmenter,
     SlidingWindowSegmenter,
 )
-from aeon.transformations.collection.shapelet_based import (
-    RandomDilatedShapeletTransform,
-    RandomShapeletTransform,
-)
 from aeon.transformations.collection.slope import SlopeTransformer
 from aeon.transformations.collection.summary import SevenNumberSummaryTransformer
 from aeon.transformations.collection.supervised_intervals import SupervisedIntervals
diff --git a/aeon/transformations/collection/acf.py b/aeon/transformations/collection/acf.py
index 76c5f0db..bbf41a13 100644
--- a/aeon/transformations/collection/acf.py
+++ b/aeon/transformations/collection/acf.py
@@ -48,6 +48,7 @@ class AutocorrelationFunctionTransformer(BaseCollectionTransformer):
     """
 
     _tags = {
+        "fit_is_empty": True,
         "capability:multivariate": True,
     }
 
diff --git a/aeon/transformations/collection/base.py b/aeon/transformations/collection/base.py
index 4b050517..2180047c 100644
--- a/aeon/transformations/collection/base.py
+++ b/aeon/transformations/collection/base.py
@@ -1,7 +1,7 @@
 """
-Base class template for panel transformers.
+Base class template for Collection transformers.
 
-    class name: BasePanelTransformer
+    class name: BaseCollectionTransformer
 
 Defining methods:
     fitting         - fit(self, X, y=None)
@@ -25,6 +25,7 @@ __all__ = [
 ]
 
 from abc import ABCMeta, abstractmethod
+from typing import final
 
 import numpy as np
 import pandas as pd
@@ -37,18 +38,21 @@ from aeon.transformations.base import BaseTransformer
 class BaseCollectionTransformer(
     BaseCollectionEstimator, BaseTransformer, metaclass=ABCMeta
 ):
-    """Transformer base class."""
+    """Transformer base class for collections."""
 
-    # Relevant tag values inherited from BaseTransformer
+    # tag values specific to CollectionTransformers
     _tags = {
-        "input_data_type": "Panel",
-        "output_data_type": "Panel",
+        "input_data_type": "Collection",
+        "output_data_type": "Collection",
         "fit_is_empty": False,
+        "requires_y": False,
+        "capability:inverse_transform": False,
     }
 
     def __init__(self):
         super(BaseCollectionTransformer, self).__init__()
 
+    @final
     def fit(self, X, y=None):
         """Fit transformer to X, optionally using y if supervised.
 
@@ -70,6 +74,9 @@ class BaseCollectionTransformer(
         -------
         self : a fitted instance of the estimator
         """
+        if self.get_tag("requires_y"):
+            if y is None:
+                raise ValueError("Tag requires_y is true, but fit called with y=None")
         # skip the rest if fit_is_empty is True
         if self.get_tag("fit_is_empty"):
             self._is_fitted = True
@@ -85,6 +92,7 @@ class BaseCollectionTransformer(
 
         return self
 
+    @final
     def transform(self, X, y=None):
         """Transform X and return a transformed version.
 
@@ -118,6 +126,7 @@ class BaseCollectionTransformer(
 
         return Xt
 
+    @final
     def fit_transform(self, X, y=None):
         """
         Fit to data, then transform it.
@@ -156,27 +165,28 @@ class BaseCollectionTransformer(
 
         return Xt
 
+    @final
     def inverse_transform(self, X, y=None):
         """Inverse transform X and return an inverse transformed version.
 
         Currently it is assumed that only transformers with tags
-            "input_data_type"="Series", "output_data_type"="Series",
-        have an inverse_transform.
+             "input_data_type"="Series", "output_data_type"="Series",
+        can have an inverse_transform.
 
         State required:
-            Requires state to be "fitted".
+             Requires state to be "fitted".
 
         Accesses in self:
-        _is_fitted : must be True
-        _X : optionally accessed, only available if remember_data tag is True
-        fitted model attributes (ending in "_") : accessed by _inverse_transform
+         _is_fitted : must be True
+         _X : optionally accessed, only available if remember_data tag is True
+         fitted model attributes (ending in "_") : accessed by _inverse_transform
 
         Parameters
         ----------
         X : Input data
             Data to fit transform to, of valid collection type.
         y : Target variable, default=None
-            Additional data, e.g., labels for transformation
+             Additional data, e.g., labels for transformation
 
         Returns
         -------
@@ -202,6 +212,7 @@ class BaseCollectionTransformer(
 
         return Xt
 
+    @final
     def update(self, X, y=None, update_params=True):
         """Update transformer with X, optionally y.
 
@@ -243,7 +254,7 @@ class BaseCollectionTransformer(
             raise ValueError(f"{self.__class__.__name__} requires `y` in `update`.")
 
         # check and convert X/y
-        X_inner = self.preprocess_collection(X)
+        X_inner = self._preprocess_collection(X)
         y_inner = y
 
         # update memory of X, if remember_data tag is set to True
diff --git a/aeon/transformations/collection/catch22.py b/aeon/transformations/collection/catch22.py
index 3505e2e0..d74dda3e 100644
--- a/aeon/transformations/collection/catch22.py
+++ b/aeon/transformations/collection/catch22.py
@@ -46,7 +46,7 @@ feature_names = [
 class Catch22(BaseCollectionTransformer):
     """Canonical Time-series Characteristics (Catch22).
 
-    Overview: Input n series with d dimensions of length m
+    Overview: Input n series with d dimensions of length m.
     Transforms series into the 22 Catch22 [1]_ features extracted from the hctsa [2]_
     toolbox.
 
@@ -131,7 +131,7 @@ class Catch22(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "X_inner_mtype": ["np-list", "numpy3D"],
         "capability:unequal_length": True,
         "capability:multivariate": True,
diff --git a/aeon/transformations/collection/channel_selection.py b/aeon/transformations/collection/channel_selection.py
index f6647ddf..d8b6ebb7 100644
--- a/aeon/transformations/collection/channel_selection.py
+++ b/aeon/transformations/collection/channel_selection.py
@@ -122,9 +122,9 @@ class ClassPrototype:
 
     References
     ----------
-    ..[1]: Bhaskar Dhariyal et al. “Fast Channel Selection for Scalable Multivariate
-    Time Series Classification.” AALTD, ECML-PKDD, Springer, 2021
-    ..[2]: Bhaskar Dhariyal et al. “Scalable Classifier-Agnostic Channel Selection
+    ..[1]: Bhaskar Dhariyal et al. "Fast Channel Selection for Scalable Multivariate
+    Time Series Classification." AALTD, ECML-PKDD, Springer, 2021
+    ..[2]: Bhaskar Dhariyal et al. "Scalable Classifier-Agnostic Channel Selection
     for Multivariate Time Series Classification", DAMI, ECML, Springer, 2023
 
     """
@@ -270,9 +270,9 @@ class ElbowClassSum(BaseCollectionTransformer):
 
     References
     ----------
-    ..[1]: Bhaskar Dhariyal et al. “Fast Channel Selection for Scalable Multivariate
-    Time Series Classification.” AALTD, ECML-PKDD, Springer, 2021
-    ..[2]: Bhaskar Dhariyal et al. “Scalable Classifier-Agnostic Channel Selection
+    ..[1]: Bhaskar Dhariyal et al. "Fast Channel Selection for Scalable Multivariate
+    Time Series Classification." AALTD, ECML-PKDD, Springer, 2021
+    ..[2]: Bhaskar Dhariyal et al. "Scalable Classifier-Agnostic Channel Selection
     for Multivariate Time Series Classification", DAMI, ECML, Springer, 2023
 
     Examples
@@ -404,9 +404,9 @@ class ElbowClassPairwise(BaseCollectionTransformer):
 
     References
     ----------
-    ..[1]: Bhaskar Dhariyal et al. “Fast Channel Selection for Scalable Multivariate
-    Time Series Classification.” AALTD, ECML-PKDD, Springer, 2021
-    ..[2]: Bhaskar Dhariyal et al. “Scalable Classifier-Agnostic Channel Selection
+    ..[1]: Bhaskar Dhariyal et al. "Fast Channel Selection for Scalable Multivariate
+    Time Series Classification." AALTD, ECML-PKDD, Springer, 2021
+    ..[2]: Bhaskar Dhariyal et al. "Scalable Classifier-Agnostic Channel Selection
     for Multivariate Time Series Classification", DAMI, ECML, Springer, 2023
 
     Examples
@@ -422,12 +422,8 @@ class ElbowClassPairwise(BaseCollectionTransformer):
     """
 
     _tags = {
-        "requires_y": True,  # does y need to be passed in fit?
-        "fit_is_empty": False,  # is fit empty and can be skipped? Yes = True
-        "skip-inverse-transform": True,  # is inverse-transform skipped when called?
-        "capability:unequal_length": False,
+        "requires_y": True,
         "capability:multivariate": True,
-        "y_inner_type": "numpy1D",
     }
 
     def __init__(
@@ -449,7 +445,7 @@ class ElbowClassPairwise(BaseCollectionTransformer):
 
         Parameters
         ----------
-        X: pandas DataFrame or np.ndarray
+        X: np.ndarray
             The training input samples.
         y: array-like or list
             The class values for X.
diff --git a/aeon/transformations/collection/convolution_based/_minirocket.py b/aeon/transformations/collection/convolution_based/_minirocket.py
index 039db9a6..2eeb7ecf 100644
--- a/aeon/transformations/collection/convolution_based/_minirocket.py
+++ b/aeon/transformations/collection/convolution_based/_minirocket.py
@@ -58,7 +58,7 @@ class MiniRocket(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
     }
 
     def __init__(
diff --git a/aeon/transformations/collection/convolution_based/_minirocket_multivariate.py b/aeon/transformations/collection/convolution_based/_minirocket_multivariate.py
index 7513d002..744008d0 100644
--- a/aeon/transformations/collection/convolution_based/_minirocket_multivariate.py
+++ b/aeon/transformations/collection/convolution_based/_minirocket_multivariate.py
@@ -59,7 +59,7 @@ class MiniRocketMultivariate(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
     }
 
diff --git a/aeon/transformations/collection/convolution_based/_minirocket_multivariate_variable.py b/aeon/transformations/collection/convolution_based/_minirocket_multivariate_variable.py
index 85bc5e52..0d9bd97c 100644
--- a/aeon/transformations/collection/convolution_based/_minirocket_multivariate_variable.py
+++ b/aeon/transformations/collection/convolution_based/_minirocket_multivariate_variable.py
@@ -82,7 +82,7 @@ class MiniRocketMultivariateVariable(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
         "capability:unequal_length": True,
         "X_inner_mtype": "df-list",
@@ -129,12 +129,12 @@ class MiniRocketMultivariateVariable(BaseCollectionTransformer):
 
         super(MiniRocketMultivariateVariable, self).__init__()
 
-    def _fit(self, X: List[np.ndarray], y=None):
+    def _fit(self, X, y=None):
         """Fits dilations and biases to input time series.
 
         Parameters
         ----------
-        X : List of 2D np.ndarray
+        X : List of 2D dataframes
         y : ignored argument for interface compatibility
 
         Returns
@@ -275,14 +275,14 @@ def _nested_dataframe_to_transposed2D_array_and_len_list(
         )
 
     vec = []
-    lenghts = []
+    lengths = []
 
     for _x in X:
         _x_shape = _x.shape
         if _x_shape[0] < 9:
             if pad is not None:
                 # emergency: pad with zeros up to 9.
-                lenghts.append(9)
+                lengths.append(9)
                 vec.append(
                     np.vstack(
                         [_x.values, np.full([9 - _x_shape[0], _x_shape[1]], float(pad))]
@@ -295,11 +295,11 @@ def _nested_dataframe_to_transposed2D_array_and_len_list(
                     " padding, discard, or setting a pad_value_short_series value"
                 )
         else:
-            lenghts.append(_x_shape[0])
+            lengths.append(_x_shape[0])
             vec.append(_x.values)
 
     X_2d_t = np.vstack(vec).T.astype(dtype=np.float32)
-    lengths = np.array(lenghts, dtype=np.int32)
+    lengths = np.array(lengths, dtype=np.int32)
 
     if not lengths.sum() == X_2d_t.shape[1]:
         raise ValueError("X_new and lengths do not match. check input dimension")
diff --git a/aeon/transformations/collection/convolution_based/_multirocket.py b/aeon/transformations/collection/convolution_based/_multirocket.py
index bbcde9dd..d025d050 100644
--- a/aeon/transformations/collection/convolution_based/_multirocket.py
+++ b/aeon/transformations/collection/convolution_based/_multirocket.py
@@ -4,7 +4,6 @@ import numpy as np
 import pandas as pd
 from numba import get_num_threads, njit, prange, set_num_threads
 
-from aeon.datatypes import convert
 from aeon.transformations.collection import BaseCollectionTransformer
 
 
@@ -72,7 +71,7 @@ class MultiRocket(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
     }
 
     def __init__(
@@ -112,11 +111,11 @@ class MultiRocket(BaseCollectionTransformer):
         self
         """
         X = X.astype(np.float64)
-        X = convert(X, from_type="numpy3D", to_type="numpyflat", as_scitype="Panel")
         if self.normalise:
             X = (X - X.mean(axis=-1, keepdims=True)) / (
                 X.std(axis=-1, keepdims=True) + 1e-8
             )
+        X = X.reshape((X.shape[0], X.shape[1] * X.shape[2]))
 
         self.parameter = self._get_parameter(X)
 
@@ -139,7 +138,7 @@ class MultiRocket(BaseCollectionTransformer):
         pandas DataFrame, transformed features
         """
         X = X.astype(np.float64)
-        X = convert(X, from_type="numpy3D", to_type="numpyflat", as_scitype="Panel")
+        X = X.reshape((X.shape[0], X.shape[1] * X.shape[2]))
         if self.normalise:
             X = (X - X.mean(axis=-1, keepdims=True)) / (
                 X.std(axis=-1, keepdims=True) + 1e-8
diff --git a/aeon/transformations/collection/convolution_based/_multirocket_multivariate.py b/aeon/transformations/collection/convolution_based/_multirocket_multivariate.py
index 6512409a..c51067dc 100644
--- a/aeon/transformations/collection/convolution_based/_multirocket_multivariate.py
+++ b/aeon/transformations/collection/convolution_based/_multirocket_multivariate.py
@@ -71,7 +71,7 @@ class MultiRocketMultivariate(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
     }
 
diff --git a/aeon/transformations/collection/convolution_based/_rocket.py b/aeon/transformations/collection/convolution_based/_rocket.py
index add46a44..eaf7d0d4 100644
--- a/aeon/transformations/collection/convolution_based/_rocket.py
+++ b/aeon/transformations/collection/convolution_based/_rocket.py
@@ -62,7 +62,7 @@ class Rocket(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
     }
 
@@ -81,7 +81,7 @@ class Rocket(BaseCollectionTransformer):
 
         Parameters
         ----------
-        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = (n_instances, n_channels, n_timepoints)
             collection of time series to transform
         y : ignored argument for interface compatibility
 
diff --git a/aeon/transformations/collection/dictionary_based/_sfa.py b/aeon/transformations/collection/dictionary_based/_sfa.py
index 26eb7c7d..7f8bd476 100644
--- a/aeon/transformations/collection/dictionary_based/_sfa.py
+++ b/aeon/transformations/collection/dictionary_based/_sfa.py
@@ -102,11 +102,7 @@ class SFA(BaseCollectionTransformer):
     """
 
     _tags = {
-        "univariate-only": True,
-        "instancewise": False,
-        "fit_is_empty": False,
         "requires_y": True,
-        "y_inner_type": "numpy1D",
     }
 
     def __init__(
diff --git a/aeon/transformations/collection/dictionary_based/_sfa_fast.py b/aeon/transformations/collection/dictionary_based/_sfa_fast.py
index c675b5cb..65abc4c5 100644
--- a/aeon/transformations/collection/dictionary_based/_sfa_fast.py
+++ b/aeon/transformations/collection/dictionary_based/_sfa_fast.py
@@ -151,11 +151,7 @@ class SFAFast(BaseCollectionTransformer):
     """
 
     _tags = {
-        "univariate-only": True,
-        "instancewise": False,
-        "fit_is_empty": False,
         "requires_y": True,
-        "y_inner_type": "numpy1D",
     }
 
     def __init__(
diff --git a/aeon/transformations/collection/dwt.py b/aeon/transformations/collection/dwt.py
index 29bf1d3d..41a9bec2 100644
--- a/aeon/transformations/collection/dwt.py
+++ b/aeon/transformations/collection/dwt.py
@@ -1,14 +1,15 @@
 """Discrete wavelet transform."""
+
+__author__ = "Vincent Nicholson"
+
 import math
 
 import numpy as np
 
-from aeon.transformations.base import BaseTransformer
-
-__author__ = "Vincent Nicholson"
+from aeon.transformations.collection import BaseCollectionTransformer
 
 
-class DWTTransformer(BaseTransformer):
+class DWTTransformer(BaseCollectionTransformer):
     """Discrete Wavelet Transform Transformer.
 
     Performs the Haar wavelet transformation on a time series.
@@ -29,11 +30,8 @@ class DWTTransformer(BaseTransformer):
     """
 
     _tags = {
-        "output_data_type": "Series",
-        "instancewise": False,
-        "X_inner_mtype": "numpy3D",
-        "y_inner_type": "None",
         "fit_is_empty": True,
+        "capability:multivariate": True,
     }
 
     def __init__(self, n_levels=3):
diff --git a/aeon/transformations/collection/hog1d.py b/aeon/transformations/collection/hog1d.py
index e1c9322e..9e8d172b 100644
--- a/aeon/transformations/collection/hog1d.py
+++ b/aeon/transformations/collection/hog1d.py
@@ -1,18 +1,19 @@
 """HOG1D transform."""
+
 import math
 import numbers
 
 import numpy as np
 
 from aeon.transformations._split import SplitsTimeSeries
-from aeon.transformations.base import BaseTransformer
+from aeon.transformations.collection import BaseCollectionTransformer
 
 
-class HOG1DTransformer(BaseTransformer, SplitsTimeSeries):
+class HOG1DTransformer(BaseCollectionTransformer, SplitsTimeSeries):
     """HOG1D transform.
 
-    This transformer calculates the HOG1D transform [1] of a collection of time seriess.
-    HOG1D splits each time series num_intervals times, and finds a histogram of
+    This transformer calculates the HOG1D transform [1] of a collection of time series.
+    HOG1D splits each time series n_intervals times, and finds a histogram of
     gradients within each interval.
 
     Parameters
@@ -33,19 +34,14 @@ class HOG1DTransformer(BaseTransformer, SplitsTimeSeries):
     """
 
     _tags = {
-        "output_data_type": "Series",
-        "instancewise": True,
-        "X_inner_mtype": "numpy3D",
-        "y_inner_type": "None",
         "fit_is_empty": True,
-        "univariate-only": True,
     }
 
     def __init__(self, n_intervals=2, n_bins=8, scaling_factor=0.1):
         self.n_intervals = n_intervals
         self.n_bins = n_bins
         self.scaling_factor = scaling_factor
-        super(HOG1DTransformer, self).__init__(_output_convert=False)
+        super(HOG1DTransformer, self).__init__()
 
     def _transform(self, X, y=None):
         """Transform X and return a transformed version.
diff --git a/aeon/transformations/collection/interpolate.py b/aeon/transformations/collection/interpolate.py
index 58ed511b..10e61dd2 100644
--- a/aeon/transformations/collection/interpolate.py
+++ b/aeon/transformations/collection/interpolate.py
@@ -1,13 +1,14 @@
 """Time series interpolator/re-sampler."""
-import numpy as np
-
-from aeon.transformations.base import BaseTransformer
 
 __all__ = ["TSInterpolator"]
 __author__ = ["mloning", "TonyBagnall"]
 
+import numpy as np
+
+from aeon.transformations.collection import BaseCollectionTransformer
+
 
-class TSInterpolator(BaseTransformer):
+class TSInterpolator(BaseCollectionTransformer):
     """Time series interpolator/re-sampler.
 
     Transformer that rescales series for another number of points.
@@ -37,10 +38,7 @@ class TSInterpolator(BaseTransformer):
     """
 
     _tags = {
-        "output_data_type": "Series",
-        "instancewise": False,
         "X_inner_mtype": ["np-list", "numpy3D"],
-        "y_inner_type": "None",
         "capability:multivariate": True,
         "capability:unequal_length": True,
         "fit_is_empty": True,
@@ -57,7 +55,7 @@ class TSInterpolator(BaseTransformer):
             raise ValueError("resizing length must be integer and > 0")
 
         self.length = length
-        super(TSInterpolator, self).__init__(_output_convert=False)
+        super(TSInterpolator, self).__init__()
 
     def _transform(self, X, y=None):
         """Take series in each cell, train linear interpolation and samples n.
diff --git a/aeon/transformations/collection/matrix_profile.py b/aeon/transformations/collection/matrix_profile.py
index c5e5892f..884331a8 100644
--- a/aeon/transformations/collection/matrix_profile.py
+++ b/aeon/transformations/collection/matrix_profile.py
@@ -3,9 +3,8 @@
 __author__ = ["Claudia Rincon Sanchez"]
 
 import numpy as np
-import pandas as pd
 
-from aeon.transformations.base import BaseTransformer
+from aeon.transformations.collection import BaseCollectionTransformer
 
 
 def _sliding_dot_products(q, t, q_len, t_len):
@@ -193,7 +192,7 @@ def _stomp_self(ts, m):
     return mp
 
 
-class MatrixProfile(BaseTransformer):
+class MatrixProfile(BaseCollectionTransformer):
     """Return the matrix profile and index profile for each time series of a dataset.
 
     Example of use:
@@ -206,15 +205,7 @@ class MatrixProfile(BaseTransformer):
     """
 
     _tags = {
-        "univariate-only": True,
         "fit_is_empty": True,
-        "input_data_type": "Series",
-        # what is the scitype of X: Series, or Panel
-        "output_data_type": "Primitives",
-        # what is the scitype of y: None (not needed), Primitives, Series, Panel
-        "instancewise": False,  # is this an instance-wise transform?
-        "X_inner_mtype": "numpy3D",  # which mtypes do _fit/_predict support for X?
-        "y_inner_type": "None",  # which mtypes do _fit/_predict support for X?
     }
 
     def __init__(self, m=10):
@@ -226,18 +217,17 @@ class MatrixProfile(BaseTransformer):
 
         Parameters
         ----------
-        X : 3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             panel of time series to transform
         y : ignored argument for interface compatibility
 
         Returns
         -------
-        Xt : pandas.DataFrame in nested_univ mtype format
-            Dataframe with the n_instances rows as the input.
-            The number of columns equals the number of subsequences
-            of the desired length in each time series.
+        Xt : Matrix profile series
         """
         # Input checks
-        n_instances = X.shape[0]
+        import pandas as pd
+
+        n_instances = len(X)
         Xt = pd.DataFrame([_stomp_self(X[i], self.m) for i in range(n_instances)])
         return Xt
diff --git a/aeon/transformations/collection/pad.py b/aeon/transformations/collection/pad.py
index 0790e088..0ce9ddae 100644
--- a/aeon/transformations/collection/pad.py
+++ b/aeon/transformations/collection/pad.py
@@ -1,11 +1,12 @@
 """Padding transformer, pad unequal length time series to max length or fixed length."""
-import numpy as np
-
-from aeon.transformations.base import BaseTransformer
 
 __all__ = ["PaddingTransformer"]
 __author__ = ["abostrom", "TonyBagnall"]
 
+import numpy as np
+
+from aeon.transformations.collection import BaseCollectionTransformer
+
 
 def _get_max_length(X):
     max_length = X[0].shape[1]
@@ -16,7 +17,7 @@ def _get_max_length(X):
     return max_length
 
 
-class PaddingTransformer(BaseTransformer):
+class PaddingTransformer(BaseCollectionTransformer):
     """Pad unequal length time series to equal, fixed length.
 
     Pads the input dataset to either fixed length (at least as long as the longest
@@ -46,11 +47,9 @@ class PaddingTransformer(BaseTransformer):
     """
 
     _tags = {
-        "output_data_type": "Series",
-        "instancewise": False,
         "X_inner_mtype": ["np-list", "numpy3D"],
-        "y_inner_type": "None",
         "fit_is_empty": False,
+        "capability:multivariate": True,
         "capability:unequal_length": True,
         "capability:unequal_length:removes": True,
     }
@@ -58,7 +57,7 @@ class PaddingTransformer(BaseTransformer):
     def __init__(self, pad_length=None, fill_value=0):
         self.pad_length = pad_length
         self.fill_value = fill_value
-        super(PaddingTransformer, self).__init__(_output_convert=False)
+        super(PaddingTransformer, self).__init__()
 
     def _fit(self, X, y=None):
         """Fit padding transformer to X and y.
diff --git a/aeon/transformations/collection/random_intervals.py b/aeon/transformations/collection/random_intervals.py
index 55be07eb..1ab632f8 100644
--- a/aeon/transformations/collection/random_intervals.py
+++ b/aeon/transformations/collection/random_intervals.py
@@ -108,7 +108,7 @@ class RandomIntervals(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
         "fit_is_empty": False,
     }
diff --git a/aeon/transformations/collection/reduce.py b/aeon/transformations/collection/reduce.py
index aa0a232a..8480853f 100644
--- a/aeon/transformations/collection/reduce.py
+++ b/aeon/transformations/collection/reduce.py
@@ -1,7 +1,7 @@
 """Tabularizer transform, for pipelining."""
 
 __author__ = ["mloning", "fkiraly", "kcc-lion"]
-__all__ = ["Tabularizer"]
+__all__ = ["Tabularizer", "TimeBinner"]
 
 import warnings
 
@@ -9,32 +9,25 @@ import numpy as np
 import pandas as pd
 
 from aeon.datatypes import convert, convert_to
-from aeon.transformations.base import BaseTransformer
+from aeon.transformations.collection import BaseCollectionTransformer
 
 
-class Tabularizer(BaseTransformer):
+class Tabularizer(BaseCollectionTransformer):
     """
     A transformer that turns time series/panel data into tabular data.
 
     This estimator converts nested pandas dataframe containing
     time-series/panel data with numpy arrays or pandas Series in
-    dataframe cells into a tabular pandas dataframe with only primitives in
-    cells. This is useful for transforming
+    dataframe cells into a tabular numpy array. This is useful for transforming
     time-series/panel data into a format that is accepted by standard
     validation learning algorithms (as in sklearn).
     """
 
     _tags = {
         "fit_is_empty": True,
-        "univariate-only": False,
-        "input_data_type": "Series",
-        # what is the scitype of X: Series, or Panel
-        "output_data_type": "Primitives",
-        # what is the scitype of y: None (not needed), Primitives, Series, Panel
-        "instancewise": True,  # is this an instance-wise transform?
+        "output_data_type": "Tabular",
         "X_inner_mtype": ["nested_univ", "numpy3D"],
-        # which mtypes do _fit/_predict support for X?
-        "y_inner_type": "None",  # and for y?
+        "capability:multivariate": True,
     }
 
     def _transform(self, X, y=None):
@@ -72,14 +65,14 @@ class Tabularizer(BaseTransformer):
         return Xt
 
 
-class TimeBinner(BaseTransformer):
+class TimeBinner(BaseCollectionTransformer):
     """
     Turns time series/panel data into tabular data based on intervals.
 
     This estimator converts nested pandas dataframe containing
     time-series/panel data with numpy arrays or pandas Series in
-    dataframe cells into a tabular pandas dataframe with only primitives in
-    cells. The primitives are calculated based on Intervals defined
+    dataframe cells into a tabular numpy array.
+    The primitives are calculated based on Intervals defined
     by the IntervalIndex and aggregated by aggfunc.
 
     This is useful for transforming time-series/panel data
@@ -98,15 +91,11 @@ class TimeBinner(BaseTransformer):
 
     _tags = {
         "fit_is_empty": True,
-        "univariate-only": False,
-        "input_data_type": "Series",
-        # what is the scitype of X: Series, or Panel
-        "output_data_type": "Primitives",
-        # what is the scitype of y: None (not needed), Primitives, Series, Panel
-        "instancewise": True,  # is this an instance-wise transform?
+        "output_data_type": "Tabular",
+        "instancewise": True,
         "X_inner_mtype": ["nested_univ"],
-        # which mtypes do _fit/_predict support for X?
-        "y_inner_type": "None",  # and for y?
+        "y_inner_type": "None",
+        "capability:multivariate": True,
     }
 
     def __init__(self, idx, aggfunc=None):
diff --git a/aeon/transformations/collection/_scaler.py b/aeon/transformations/collection/scaler.py
similarity index 100%
rename from aeon/transformations/collection/_scaler.py
rename to aeon/transformations/collection/scaler.py
diff --git a/aeon/transformations/collection/segment.py b/aeon/transformations/collection/segment.py
index 66b18226..1b9ab4e8 100644
--- a/aeon/transformations/collection/segment.py
+++ b/aeon/transformations/collection/segment.py
@@ -6,11 +6,11 @@ import pandas as pd
 from sklearn.utils import check_random_state
 
 from aeon.datatypes._panel._convert import _concat_nested_arrays, _get_time_index
-from aeon.transformations.base import BaseTransformer
+from aeon.transformations.collection import BaseCollectionTransformer
 from aeon.utils.validation import check_window_length
 
 
-class IntervalSegmenter(BaseTransformer):
+class IntervalSegmenter(BaseCollectionTransformer):
     """Interval segmentation transformer.
 
     Parameters
@@ -25,22 +25,14 @@ class IntervalSegmenter(BaseTransformer):
     """
 
     _tags = {
-        "univariate-only": True,
-        "input_data_type": "Series",
-        "output_data_type": "Series",
-        "instancewise": True,
-        "X_inner_mtype": "numpy3D",
-        "y_inner_type": "None",
-        "fit_is_empty": False,
         "capability:unequal_length:removes": True,
-        "capability:multivariate": False,
     }
 
     def __init__(self, intervals=10):
         self.intervals = intervals
         self._time_index = []
         self.input_shape_ = ()
-        super(IntervalSegmenter, self).__init__(_output_convert=False)
+        super(IntervalSegmenter, self).__init__()
 
     def _fit(self, X, y=None):
         """
@@ -183,9 +175,7 @@ class RandomIntervalSegmenter(IntervalSegmenter):
     """
 
     _tags = {
-        "X_inner_mtype": "numpy3D",  # which mtypes do _fit/_predict support for X?
-        "y_inner_type": "pd_Series_Table",
-        # which mtypes do _fit/_predict support for y?
+        "X_inner_mtype": "numpy3D",
     }
 
     def __init__(
@@ -347,7 +337,7 @@ def _rand_intervals_fixed_n(
     return np.column_stack([starts, ends])
 
 
-class SlidingWindowSegmenter(BaseTransformer):
+class SlidingWindowSegmenter(BaseCollectionTransformer):
     """Sliding window segmenter transformer.
 
     This class is to transform a univariate series into a multivariate one by
@@ -388,19 +378,14 @@ class SlidingWindowSegmenter(BaseTransformer):
     """
 
     _tags = {
-        "univariate-only": True,
         "fit_is_empty": True,
-        "input_data_type": "Series",
-        # what is the scitype of X: Series, or Panel
-        "output_data_type": "Series",
         "instancewise": False,
-        "X_inner_mtype": "numpy3D",
         "y_inner_type": "None",
     }
 
     def __init__(self, window_length=5):
         self.window_length = window_length
-        super(SlidingWindowSegmenter, self).__init__(_output_convert=False)
+        super(SlidingWindowSegmenter, self).__init__()
 
     def _transform(self, X, y=None):
         """Transform time series.
diff --git a/aeon/transformations/collection/shapelet_based/__init__.py b/aeon/transformations/collection/shapelet_based/__init__.py
index a82a5cdc..ae84b1c0 100644
--- a/aeon/transformations/collection/shapelet_based/__init__.py
+++ b/aeon/transformations/collection/shapelet_based/__init__.py
@@ -1,5 +1,9 @@
 """Shapelet based transformers."""
-__all__ = ["RandomShapeletTransform", "RandomDilatedShapeletTransform"]
+
+__all__ = [
+    "RandomShapeletTransform",
+    "RandomDilatedShapeletTransform",
+]
 
 from aeon.transformations.collection.shapelet_based._dilated_shapelet_transform import (
     RandomDilatedShapeletTransform,
diff --git a/aeon/transformations/collection/shapelet_based/_dilated_shapelet_transform.py b/aeon/transformations/collection/shapelet_based/_dilated_shapelet_transform.py
index a1bc9e21..db9b3cd5 100644
--- a/aeon/transformations/collection/shapelet_based/_dilated_shapelet_transform.py
+++ b/aeon/transformations/collection/shapelet_based/_dilated_shapelet_transform.py
@@ -14,7 +14,7 @@ from numba import njit, prange, set_num_threads
 from sklearn.preprocessing import LabelEncoder
 
 from aeon.distances import manhattan_distance
-from aeon.transformations.base import BaseTransformer
+from aeon.transformations.collection import BaseCollectionTransformer
 from aeon.utils.numba.general import (
     choice_log,
     combinations_1d,
@@ -27,7 +27,7 @@ from aeon.utils.numba.stats import prime_up_to
 from aeon.utils.validation import check_n_jobs
 
 
-class RandomDilatedShapeletTransform(BaseTransformer):
+class RandomDilatedShapeletTransform(BaseCollectionTransformer):
     """Random Dilated Shapelet Transform (RDST) as described in [1]_[2]_.
 
     Overview: The input is n series with d channels of length m. First step is to
@@ -130,14 +130,8 @@ class RandomDilatedShapeletTransform(BaseTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
-        "fit_is_empty": False,
-        "univariate-only": False,
-        "X_inner_mtype": "numpy3D",
-        "y_inner_type": "numpy1D",
-        "requires_y": False,
-        "capability:inverse_transform": False,
-        "capability:missing_values": False,
+        "output_data_type": "Tabular",
+        "capability:multivariate": True,
     }
 
     def __init__(
diff --git a/aeon/transformations/collection/shapelet_based/_shapelet_transform.py b/aeon/transformations/collection/shapelet_based/_shapelet_transform.py
index dcffba80..0a012f20 100644
--- a/aeon/transformations/collection/shapelet_based/_shapelet_transform.py
+++ b/aeon/transformations/collection/shapelet_based/_shapelet_transform.py
@@ -138,10 +138,8 @@ class RandomShapeletTransform(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
-        "y_inner_type": "numpy1D",
-        "fit_is_empty": False,
         "requires_y": True,
     }
 
diff --git a/aeon/transformations/collection/signature_based/_compute.py b/aeon/transformations/collection/signature_based/_compute.py
index 9e0d4b55..621017e3 100644
--- a/aeon/transformations/collection/signature_based/_compute.py
+++ b/aeon/transformations/collection/signature_based/_compute.py
@@ -26,7 +26,7 @@ class _WindowSignatureTransform(BaseCollectionTransformer):
     # default tag values for "Series-to-Primitives"
     _tags = {
         "fit_is_empty": True,
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
         "python_dependencies": "esig",
         "python_version": "<3.11",
diff --git a/aeon/transformations/collection/signature_based/_signature_method.py b/aeon/transformations/collection/signature_based/_signature_method.py
index 82faf942..db55fc07 100644
--- a/aeon/transformations/collection/signature_based/_signature_method.py
+++ b/aeon/transformations/collection/signature_based/_signature_method.py
@@ -40,9 +40,8 @@ class SignatureTransformer(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
-        "fit_is_empty": False,
         "python_dependencies": "esig",
         "python_version": "<3.11",
     }
diff --git a/aeon/transformations/collection/slope.py b/aeon/transformations/collection/slope.py
index 895a11f4..7dd5391e 100644
--- a/aeon/transformations/collection/slope.py
+++ b/aeon/transformations/collection/slope.py
@@ -1,16 +1,17 @@
 """Slope transformer."""
+
+__all__ = ["SlopeTransformer"]
+__author__ = ["mloning"]
+
 import math
 
 import numpy as np
 
 from aeon.transformations._split import SplitsTimeSeries
-from aeon.transformations.base import BaseTransformer
-
-__all__ = ["SlopeTransformer"]
-__author__ = ["mloning"]
+from aeon.transformations.collection import BaseCollectionTransformer
 
 
-class SlopeTransformer(BaseTransformer, SplitsTimeSeries):
+class SlopeTransformer(BaseCollectionTransformer, SplitsTimeSeries):
     """Piecewise slope transformation.
 
     Class to perform a slope transformation on a collection of time series.
@@ -35,16 +36,13 @@ class SlopeTransformer(BaseTransformer, SplitsTimeSeries):
     """
 
     _tags = {
-        "output_data_type": "Series",
-        "instancewise": False,
-        "X_inner_mtype": "numpy3D",
-        "y_inner_type": "None",
+        "capability:multivariate": True,
         "fit_is_empty": True,
     }
 
     def __init__(self, n_intervals=8):
         self.n_intervals = n_intervals
-        super(SlopeTransformer, self).__init__(_output_convert=False)
+        super(SlopeTransformer, self).__init__()
 
     def _transform(self, X, y=None):
         """Transform X and return a transformed version.
diff --git a/aeon/transformations/collection/supervised_intervals.py b/aeon/transformations/collection/supervised_intervals.py
index 0b4e43ba..35f66b19 100644
--- a/aeon/transformations/collection/supervised_intervals.py
+++ b/aeon/transformations/collection/supervised_intervals.py
@@ -134,8 +134,7 @@ class SupervisedIntervals(BaseCollectionTransformer):
     """
 
     _tags = {
-        "output_data_type": "Primitives",
-        "y_inner_type": "numpy1D",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
         "requires_y": True,
     }
diff --git a/aeon/transformations/collection/truncate.py b/aeon/transformations/collection/truncate.py
index 69c3bd9b..128e4ebc 100644
--- a/aeon/transformations/collection/truncate.py
+++ b/aeon/transformations/collection/truncate.py
@@ -1,13 +1,14 @@
 """Truncation transformer - truncate unequal length panels to lower/upper bounds."""
-import numpy as np
-
-from aeon.transformations.base import BaseTransformer
 
 __all__ = ["TruncationTransformer"]
 __author__ = ["abostrom", "TonyBagnall"]
 
+import numpy as np
+
+from aeon.transformations.collection import BaseCollectionTransformer
+
 
-class TruncationTransformer(BaseTransformer):
+class TruncationTransformer(BaseCollectionTransformer):
     """Truncate unequal length time series to a lower bounds.
 
     Truncates all series in panel between lower/upper range bounds. This transformer
@@ -34,18 +35,15 @@ class TruncationTransformer(BaseTransformer):
     """
 
     _tags = {
-        "output_data_type": "Series",
-        "instancewise": False,
         "X_inner_mtype": ["np-list", "numpy3D"],
-        "y_inner_type": "None",
-        "fit_is_empty": False,
+        "capability:multivariate": True,
         "capability:unequal_length": True,
         "capability:unequal_length:removes": True,
     }
 
     def __init__(self, truncated_length=None):
         self.truncated_length = truncated_length
-        super(TruncationTransformer, self).__init__(_output_convert=False)
+        super(TruncationTransformer, self).__init__()
 
     @staticmethod
     def _get_min_length(X):
diff --git a/aeon/transformations/collection/tsfresh.py b/aeon/transformations/collection/tsfresh.py
index 57a033ae..85d6bad1 100644
--- a/aeon/transformations/collection/tsfresh.py
+++ b/aeon/transformations/collection/tsfresh.py
@@ -12,10 +12,9 @@ class _TSFreshFeatureExtractor(BaseCollectionTransformer):
     """Base adapter class for tsfresh transformations."""
 
     _tags = {
-        "output_data_type": "Primitives",
+        "output_data_type": "Tabular",
         "capability:multivariate": True,
         "fit_is_empty": True,
-        "y_inner_type": "None",
         "python_dependencies": "tsfresh",
     }
 
diff --git a/aeon/transformations/series/acf.py b/aeon/transformations/series/acf.py
index 61cafb94..a2ba21c1 100644
--- a/aeon/transformations/series/acf.py
+++ b/aeon/transformations/series/acf.py
@@ -36,10 +36,10 @@ class AutoCorrelationTransformer(BaseTransformer):
         calculations.
 
         - "none" performs no checks or handling of missing values
-        - “raise” raises an exception if NaN values are found.
-        - “drop” removes the missing observations and then estimates the
+        - "raise" raises an exception if NaN values are found.
+        - "drop" removes the missing observations and then estimates the
           autocovariances treating the non-missing as contiguous.
-        - “conservative” computes the autocovariance using nan-ops so that nans
+        - "conservative" computes the autocovariance using nan-ops so that nans
           are removed when computing the mean and cross-products that are used to
           estimate the autocovariance. "n" in calculation is set to the number of
           non-missing observations.
diff --git a/aeon/transformations/series/bkfilter.py b/aeon/transformations/series/bkfilter.py
index fe12efa0..b67ee704 100644
--- a/aeon/transformations/series/bkfilter.py
+++ b/aeon/transformations/series/bkfilter.py
@@ -25,7 +25,7 @@ class BKFilter(BaseTransformer):
     data and deals with the periodicity of the business cycle. Applying their
     band-pass filter to a series will produce a new series that does not contain
     fluctuations at a higher or lower frequency than those of the business cycle.
-    Baxter-King follow Burns and Mitchell’s work on business cycles, which suggests
+    Baxter-King follow Burns and Mitchell's work on business cycles, which suggests
     that U.S. business cycles typically last from 1.5 to 8 years.
 
     Parameters
diff --git a/aeon/utils/validation/collection.py b/aeon/utils/validation/collection.py
index d68f385d..97cc1095 100644
--- a/aeon/utils/validation/collection.py
+++ b/aeon/utils/validation/collection.py
@@ -205,7 +205,7 @@ def get_type(X):
     >>> get_type( np.zeros(shape=(10, 3, 20)))
     'numpy3D'
     """
-    if isinstance(X, np.ndarray):  # “numpy3D” or numpyflat
+    if isinstance(X, np.ndarray):  # "numpy3D" or numpyflat
         if X.ndim == 3:
             return "numpy3D"
         elif X.ndim == 2:
diff --git a/build_tools/pr_labeler.py b/build_tools/pr_labeler.py
new file mode 100644
index 00000000..b0bf57d2
--- /dev/null
+++ b/build_tools/pr_labeler.py
@@ -0,0 +1,91 @@
+"""Labels PRs based on title and change list.
+
+Must be run in a github action with the pull_request_target event.
+
+Based on the scikit-learn v1.3.1 label_title_regex.py script.
+"""
+
+import json
+import os
+import re
+
+from github import Github
+
+context_dict = json.loads(os.getenv("CONTEXT_GITHUB"))
+
+repo = context_dict["repository"]
+g = Github(context_dict["token"])
+repo = g.get_repo(repo)
+pr_number = context_dict["event"]["number"]
+pr = repo.get_pull(number=pr_number)
+labels = [label.name for label in pr.get_labels()]
+
+# title labels
+title = pr.title
+
+title_regex_to_labels = [
+    (r"\bENH\b", "enhancement"),
+    (r"\bMNT\b", "maintenance"),
+    (r"\bBUG\b", "bug"),
+    (r"\bDOC\b", "documentation"),
+    (r"\bGOV\b", "governance"),
+]
+
+title_labels = [
+    label for regex, label in title_regex_to_labels if re.search(regex, title)
+]
+title_labels_to_add = list(set(title_labels) - set(labels))
+
+# content labels
+paths = [file.filename for file in pr.get_files()]
+
+content_paths_to_labels = [
+    ("aeon/anomaly_detection/", "anomaly detection"),
+    ("aeon/benchmarking/", "benchmarking"),
+    ("aeon/classification/", "classification"),
+    ("aeon/clustering/", "clustering"),
+    ("aeon/datasets/", "datasets"),
+    ("aeon/datatypes/", "datatypes"),
+    ("aeon/distances/", "distances"),
+    ("examples/", "examples"),
+    ("aeon/forecasting/", "forecasting"),
+    ("aeon/networks/", "networks"),
+    ("aeon/regression/", "regression"),
+    ("aeon/segmentation/", "segmentation"),
+    ("aeon/similarity_search/", "similarity search"),
+    ("aeon/transformations/", "transformations"),
+]
+
+present_content_labels = [
+    label for _, label in content_paths_to_labels if label in labels
+]
+
+content_labels = [
+    label
+    for package, label in content_paths_to_labels
+    if any([package in path for path in paths])
+]
+content_labels = list(set(content_labels))
+
+content_labels_to_add = content_labels
+content_labels_status = "used"
+if len(present_content_labels) > 0:
+    content_labels_to_add = []
+    content_labels_status = "ignored"
+if len(content_labels) > 3:
+    content_labels_to_add = []
+    content_labels_status = (
+        "large" if content_labels_status != "ignored" else "ignored+large"
+    )
+
+# add to PR
+if title_labels_to_add or content_labels_to_add:
+    pr.add_to_labels(*title_labels_to_add + content_labels_to_add)
+
+with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
+    print(f"title-labels={title_labels}".replace(" ", ""), file=fh)  # noqa: T201
+    print(  # noqa: T201
+        f"title-labels-new={title_labels_to_add}".replace(" ", ""), file=fh
+    )
+    print(f"content-labels={content_labels}".replace(" ", ""), file=fh)  # noqa: T201
+    print(f"content-labels-status={content_labels_status}", file=fh)  # noqa: T201
diff --git a/build_tools/pr_open_commenter.py b/build_tools/pr_open_commenter.py
new file mode 100644
index 00000000..0a791e74
--- /dev/null
+++ b/build_tools/pr_open_commenter.py
@@ -0,0 +1,104 @@
+"""Writes a comment on PR opening.
+
+Includes output from the labeler action.
+"""
+
+import json
+import os
+import sys
+
+from github import Github
+
+context_dict = json.loads(os.getenv("CONTEXT_GITHUB"))
+
+repo = context_dict["repository"]
+g = Github(context_dict["token"])
+repo = g.get_repo(repo)
+pr_number = context_dict["event"]["number"]
+pr = repo.get_pull(number=pr_number)
+
+print(sys.argv)  # noqa
+title_labels = sys.argv[1][1:-1].split(",")
+title_labels_new = sys.argv[2][1:-1].split(",")
+content_labels = sys.argv[3][1:-1].split(",")
+content_labels_status = sys.argv[4]
+
+labels = [(label.name, label.color) for label in repo.get_labels()]
+title_labels = [
+    "$\\color{#%s}{\\textsf{%s}}$" % (color, label)
+    for label, color in labels
+    if label in title_labels
+]
+title_labels_new = [
+    "$\\color{#%s}{\\textsf{%s}}$" % (color, label)
+    for label, color in labels
+    if label in title_labels_new
+]
+content_labels = [
+    "$\\color{#%s}{\\textsf{%s}}$" % (color, label)
+    for label, color in labels
+    if label in content_labels
+]
+
+title_labels_str = ""
+if len(title_labels) == 0:
+    title_labels_str = (
+        "I did not find any labels to add based on the title. Please "
+        "add the ENH, MNT, BUG, DOC and/or GOV tag to your pull "
+        "requests titles. For now you can add the labels manually."
+    )
+elif len(title_labels_new) != 0:
+    arr_str = str(title_labels_new).strip("[]").replace("'", "")
+    title_labels_str = (
+        "I have added the following labels to this PR based on the title: "
+        f"**[ {arr_str} ]**."
+    )
+    if len(title_labels) != len(title_labels_new):
+        arr_str = (
+            str(set(title_labels) - set(title_labels_new)).strip("[]").replace("'", "")
+        )
+        title_labels_str += (
+            f" The following labels were already present: **[ {arr_str} ]**"
+        )
+
+content_labels_str = ""
+if len(content_labels) != 0:
+    if content_labels_status == "used":
+        arr_str = str(content_labels).strip("[]").replace("'", "")
+        content_labels_str = (
+            "I have added the following labels to this PR based on "
+            f"the changes made: **[ {arr_str} ]**. Feel free "
+            "to change these if they do not properly represent the PR."
+        )
+    elif content_labels_status == "ignored":
+        arr_str = str(content_labels).strip("[]").replace("'", "")
+        content_labels_str = (
+            "I would have added the following labels to this PR "
+            f"based on the changes made: **[ {arr_str} ]**, "
+            "however some package labels are already present."
+        )
+    elif content_labels_status == "large":
+        content_labels_str = (
+            "This PR changes too many different packages (>3) for "
+            "automatic addition of labels, please manually add package "
+            "labels if relevant."
+        )
+elif title_labels_str == "":
+    content_labels_str = (
+        "I did not find any labels to add that did not already "
+        "exist. If the content of your PR changes, make sure to "
+        "update the labels accordingly."
+    )
+
+pr.create_issue_comment(
+    f"""
+## Thank you for contributing to `aeon`!
+
+{title_labels_str}
+{content_labels_str}
+
+The "Checks" tab above or the panel below will show the status of our automated tests. From the panel below you can click on "Details" to the right of the check to see more information if there is a failure. If our `pre-commit` code quality check fails, any trivial fixes will automatically be pushed to your PR.
+
+Don't hesitate to ask questions on the `aeon` Slack channel if you have any!
+    """  # noqa
+)
diff --git a/docs/Makefile b/docs/Makefile
index ef141322..d1cef8bc 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,28 +1,20 @@
 # Minimal makefile for Sphinx documentation
+#
 
-# You can set these variables from the command line.
-PREPROCESS    = sphinx-apidoc
-APIDOCTEMPLATEDIR = source/_templates/apidoc
-AUTOGENDIR    = source/api_reference/auto_generated
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
 SOURCEDIR     = .
 BUILDDIR      = _build
-PROJECTDIR    = "../aeon/"
-
-# exclude unit tests from API reference
-EXCLUDEPATHS = "../*tests* ../*test* ../*setup* ../*all*"
 
 # Put it first so that "make" without argument is like "make help".
 help:
-	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -T
 
 .PHONY: help Makefile
 
 # Catch-all target: route all unknown targets to Sphinx using the new
 # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
 %: Makefile
-	rm -rf $(AUTOGENDIR)
-	@$(PREPROCESS) -eMT --templatedir $(APIDOCTEMPLATEDIR) -o $(AUTOGENDIR) $(PROJECTDIR) "$(EXCLUDEPATHS)"
-	-rm -rf $(BUILDDIR)
-	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -T
diff --git a/docs/_templates/class.rst b/docs/_templates/class.rst
index e45bca45..e129e8a7 100644
--- a/docs/_templates/class.rst
+++ b/docs/_templates/class.rst
@@ -1,12 +1,6 @@
-{{objname}}
-{{ underline }}==============
+{{ objname }}
+{{ underline }}
 
 .. currentmodule:: {{ module }}
 
 .. autoclass:: {{ objname }}
-
-.. include:: {{module}}.{{objname}}.examples
-
-.. raw:: html
-
-    <div class="clearer"></div>
diff --git a/docs/_templates/class_with_call.rst b/docs/_templates/class_with_call.rst
index 9fc4b65c..21936b6e 100644
--- a/docs/_templates/class_with_call.rst
+++ b/docs/_templates/class_with_call.rst
@@ -1,13 +1,7 @@
-{{objname}}
-{{ underline }}===============
+{{ objname }}
+{{ underline }}
 
 .. currentmodule:: {{ module }}
 
 .. autoclass:: {{ objname }}
    :special-members: __call__
-
-.. include:: {{module}}.{{objname}}.examples
-
-.. raw:: html
-
-    <div class="clearer"></div>
diff --git a/docs/_templates/function.rst b/docs/_templates/function.rst
index 61d178dc..f5676ee8 100644
--- a/docs/_templates/function.rst
+++ b/docs/_templates/function.rst
@@ -1,12 +1,6 @@
-{{objname}}
-{{ underline }}====================
+{{ objname }}
+{{ underline }}
 
 .. currentmodule:: {{ module }}
 
 .. autofunction:: {{ objname }}
-
-.. include:: {{module}}.{{objname}}.examples
-
-.. raw:: html
-
-    <div class="clearer"></div>
diff --git a/docs/about.md b/docs/about.md
index 41c6258b..99786ed0 100644
--- a/docs/about.md
+++ b/docs/about.md
@@ -2,7 +2,7 @@
 
 ## Roles
 
-The roles are described in scikit-time's {ref}`governance` document.
+The roles are described in aeon's [governance](governance.md) document.
 A list of all contributors can be found [here](contributors.md).
 
 ### Community Council
diff --git a/docs/api_reference/classification.rst b/docs/api_reference/classification.rst
index 1fd42d5e..f9aae3db 100644
--- a/docs/api_reference/classification.rst
+++ b/docs/api_reference/classification.rst
@@ -107,9 +107,9 @@ Interval-based
     :toctree: auto_generated/
     :template: class.rst
 
-    CanonicalIntervalForest
-    DrCIF
-    RandomIntervalSpectralEnsemble
+    CanonicalIntervalForestClassifier
+    DrCIFClassifier
+    RandomIntervalSpectralEnsembleClassifier
     SupervisedTimeSeriesForest
     TimeSeriesForestClassifier
     RandomIntervalClassifier
diff --git a/docs/api_reference/transformations.rst b/docs/api_reference/transformations.rst
index 1b9f90a9..bc29661d 100644
--- a/docs/api_reference/transformations.rst
+++ b/docs/api_reference/transformations.rst
@@ -116,7 +116,6 @@ These transformers extract simple summary features.
 
     SummaryTransformer
     WindowSummarizer
-    PlateauFinder
     FittedParamExtractor
 
 
@@ -142,6 +141,8 @@ Shapelets, wavelets, and convolution
     MiniRocket
     MiniRocketMultivariate
     MiniRocketMultivariateVariable
+    MultiRocket
+    MultiRocketMultivariate
 
 .. currentmodule:: aeon.transformations.collection.dwt
 
diff --git a/docs/code_of_conduct.md b/docs/code_of_conduct.md
new file mode 100644
index 00000000..31999d6b
--- /dev/null
+++ b/docs/code_of_conduct.md
@@ -0,0 +1,42 @@
+# Code of Conduct
+
+We value the participation of every member in our community and want to
+ensure that every contributor has an enjoyable experience. Accordingly, those who
+participate in the aeon project are expected to show respect and courtesy to other
+community members at all times.
+
+Everyone participating in activities associated with the aeon project (whether
+in-person or remote) is required to conform to this Code of Conduct (CoC) regardless
+of platform.
+
+It is expected that community members will abide by the principles of the [Python
+Software Foundation Code of Conduct](https://www.python.org/psf/codeofconduct/) to
+help maintain an open, considerate and respectful environment.
+
+Unacceptable behavior and code of conduct violations are determined and dealt with
+at the discretion of the aeon [Code of Conduct Workgroup (COCW)](./governance.md#code-of-conduct-workgroup).
+Possible actions taken include warnings, mediated meeting and/or apologies, and
+expulsion from the project depending on the severity of the breach. The COCW can edit
+and remove comments and other contributions that are not aligned to this Code of
+Conduct.
+
+Examples of unacceptable behaviour include (but are not limited to):
+- Personal attacks, insults and threats
+- Harassment of individuals or groups both publicly and privately
+- Publishing private information of others, such as physical or electronic
+addresses, without explicit permission
+- The use of sexualised language or imagery in conversation or talks
+- Jokes or insults based on stereotypes, slurs or other exclusionary language
+- Continued one-on-one communication after requests to cease
+- General unethical and unprofessional conduct
+
+Community members asked to stop any inappropriate behaviour are expected to comply
+immediately.
+
+If you believe someone is violating the CoC, we ask that you report it by
+contacting the COCW. All reports will be kept confidential. You can make a report by
+emailing the general aeon contact address [contact@aeon-toolkit.org](mailto:contact@aeon-toolkit.org),
+or by messaging members of the COCW directly on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA).
+
+Thank you for reading and helping make this a welcoming, friendly community for
+everyone.
diff --git a/docs/conf.py b/docs/conf.py
index ba2764cb..32af697b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -44,6 +44,7 @@ extensions = [
     "sphinx_issues",
     "sphinx_copybutton",
     "sphinx_remove_toctrees",
+    "versionwarning.extension",
     "myst_parser",
 ]
 
@@ -134,7 +135,7 @@ suppress_warnings = ["myst.mathjax"]
 # Recommended by sphinx_design when using the MyST Parser
 myst_enable_extensions = ["colon_fence", "html_image"]
 
-myst_heading_anchors = 2
+myst_heading_anchors = 4
 
 
 def linkcode_resolve(domain, info):
@@ -347,10 +348,10 @@ Generated using nbsphinx_. The Jupyter notebook can be found here_.
 
 # Example configuration for intersphinx: refer to the Python standard library.
 intersphinx_mapping = {
-    "python": ("https://docs.python.org/{.major}".format(sys.version_info), None),
-    "numpy": ("https://docs.scipy.org/doc/numpy/", None),
-    "scipy": ("https://docs.scipy.org/doc/scipy/reference", None),
-    "matplotlib": ("https://matplotlib.org/", None),
+    "python": ("https://docs.python.org/3/", None),
+    "numpy": ("https://numpy.org/doc/stable/", None),
+    "scipy": ("https://docs.scipy.org/doc/scipy/", None),
+    "matplotlib": ("https://matplotlib.org/stable/", None),
     "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
     "joblib": ("https://joblib.readthedocs.io/en/latest/", None),
     "scikit-learn": ("https://scikit-learn.org/stable/", None),
diff --git a/docs/developer_guide/deprecation.md b/docs/developer_guide/deprecation.md
new file mode 100644
index 00000000..b305d2de
--- /dev/null
+++ b/docs/developer_guide/deprecation.md
@@ -0,0 +1,246 @@
+# Deprecation Policy
+
+aeon [releases](https://github.com/aeon-toolkit/aeon/releases) follow [semantic
+versioning](https://semver.org). A release number denotes `<major>.<minor>.<patch>`
+versions.
+
+Broadly, if a change could unexpectedly cause code using aeon to crash when updating
+to the next version, then it should be deprecated to give the user a chance to prepare.
+
+When to deprecate:
+- Removal or renaming of public classes or functions
+- Removal or renaming of public class parameters or function arguments
+- Addition of positional arguments without default values
+
+Deprecation warnings should be included for at least one full minor version cycle
+before change or removal. If an item is deprecated in v0.6.0, it can be removed in
+v0.7.0. If an item is deprecated in v0.6.1, it can be removed in v0.8.0.
+
+Note that the deprecation policy does not necessarily apply to modules we class as
+still experimental. Currently experimental modules are:
+
+- `annotation`
+- `anomaly_detection`
+- `segmentation`
+- `similarity_search`
+
+When we introduce a new module, we may classify it as experimental until the API is
+stable. We will try to not make drastic changes to experimental modules, but we need to
+retain the freedom to be more agile with the design in these cases.
+
+## Deprecation Process
+
+To deprecate functions and classes, write a "TODO" comment stating the version the code
+should be removed in and raise a warning using the [deprecated package](https://deprecated.readthedocs.io/en/latest/index.html).
+This raises  a `FutureWarning` saying that the functionality has been deprecated.
+Import from `deprecated.sphinx` so the deprecation message is automatically added to
+the documentation.
+
+When renaming items, the functionality should ideally already be available with the new
+name when the deprecation warning is added. For example, including both the old and new
+name for a positional argument, or both functions/classes with the old and new names.
+This is not always possible, but it is good practice to do so.
+
+In most cases not necessary to use the `deprecated` package when renaming or removing
+function and class keyword arguments. The default value of the argument can be set to
+`"deprecated"`. If this value is changed, a `FutureWarning` can be raised.
+This isolates the deprecation warning to the argument, rather than the whole function
+or class. If renaming, the new keyword argument can be added alongside this, with the
+warning directing users to use the new keyword argument.
+
+## Examples
+
+### Functions and Methods
+
+Deprecate a function.
+
+```python
+from deprecated.sphinx import deprecated
+
+# TODO: remove in v0.7.0
+@deprecated(
+    version="0.6.0",
+    reason="my_function will be removed in v0.7.0.",
+    category=FutureWarning,
+)
+def my_function(x, y):
+    """Return x + y."""
+    return x + y
+```
+
+Deprecate a function to add a new positional argument. In certain cases, you can add
+a keyword argument with a default value, to ease the transition.
+
+```python
+from deprecated.sphinx import deprecated
+
+# TODO: remove in v0.7.0 and remove z default
+@deprecated(
+    version="0.6.0",
+    reason="my_function will be include a third positional argument 'z' in v0.7.0, used for reasons.",
+    category=FutureWarning,
+)
+def my_function(x, y, z=0):
+    """Return x + y + z."""
+    return x + y + z
+```
+
+Deprecate a class method to remove a positional argument.
+
+```python
+from deprecated.sphinx import deprecated
+
+class MyClass:
+    """Contains my_method."""
+
+    # TODO: remove in v0.7.0
+    @deprecated(
+        version="0.6.0",
+        reason="my_method first positional argument 'x' will be removed in v0.7.0.",
+        category=FutureWarning,
+    )
+    def my_method(self, x, y):
+        """Return y."""
+        return y
+```
+
+### Classes
+
+Deprecate a class.
+
+Since this example is deprecated on a patch release, it cannot be removed from the
+next minor release.
+
+```python
+from deprecated.sphinx import deprecated
+
+# TODO: remove in v0.8.0
+@deprecated(
+    version="0.6.1",
+    reason="MyClass will be removed in v0.8.0.",
+    category=FutureWarning,
+)
+class MyClass:
+    """Exists (for now)."""
+    pass
+```
+
+Deprecate a public class attribute. If we are renaming, we could add the new name and
+direct users to use that instead while updating both.
+
+```python
+from deprecated.sphinx import deprecated
+
+# TODO: remove in v0.7.0
+@deprecated(
+    version="0.6.0",
+    reason="The my_attribute attribute will be removed in v0.7.0.",
+    category=FutureWarning,
+)
+class MyClass:
+    """Contains my_attribute."""
+
+    my_attribute = 1
+```
+
+Deprecate a class parameter.
+
+```python
+import warnings
+
+class MyClass:
+    """Contains x and y.
+
+    Parameters
+    ----------
+    x : int, default=1
+        The x parameter.
+    y : int, default="deprecated"
+        The y parameter.
+
+        Deprecated and will be removed in v0.7.0.
+    """
+
+    # TODO remove 'y' in v0.7.0
+    def __init__(self, x=1, y="deprecated"):
+        self.x = x
+        if y != "deprecated":
+            warnings.warn("The 'y' parameter will be removed in v0.7.0.", FutureWarning)
+```
+
+### Renaming
+
+Deprecate a class parameter to rename it.
+
+```python
+import warnings
+
+class MyClass:
+    """Does stuff.
+
+    Parameters
+    ----------
+    x : int, default=1
+        The x parameter.
+    new_param : int, default=2
+        New identifier for old_param.
+    old_param : int, default="deprecated"
+        Old identifier for new_param. If used will set new_param.
+
+        Deprecated and will be removed in v0.7.0.
+    """
+
+    # TODO remove 'old_param' in v0.7.0
+    def __init__(self, x=1, new_param=2, old_param="deprecated"):
+        self.x = x
+        self.new_param = new_param
+        if old_param != "deprecated":
+            warnings.warn("The 'old_param' parameter will be removed in v0.7.0. Use 'new_param' instead.", FutureWarning)
+            self.new_param = old_param
+```
+
+Deprecate a class to rename it.
+
+```python
+from deprecated.sphinx import deprecated
+
+class MyNewClass:
+    """Exists."""
+
+    @staticmethod
+    def return_one():
+        """Return 1."""
+        return 1
+
+
+# TODO: remove in v0.7.0
+@deprecated(
+    version="0.6.0",
+    reason="The MyOldClass class has been renamed to MyNewClass and will be removed in v0.7.0.",
+    category=FutureWarning,
+)
+class MyOldClass(MyNewClass):
+    """Exists."""
+    pass
+```
+
+Deprecate a function to rename it.
+
+```python
+from deprecated.sphinx import deprecated
+
+def my_new_function(x, y):
+    """Return x + y."""
+    return x + y
+
+
+# TODO: remove in v0.7.0
+@deprecated(
+    version="0.6.0",
+    reason="my_old_function has been renamed to my_new_function and will be removed in v0.7.0.",
+    category=FutureWarning,
+)
+def my_old_function(x, y):
+    """Return x + y."""
+    return my_new_function(x, y)
+```
diff --git a/docs/developer_guide/deprecation.rst b/docs/developer_guide/deprecation.rst
deleted file mode 100644
index 938b43aa..00000000
--- a/docs/developer_guide/deprecation.rst
+++ /dev/null
@@ -1,117 +0,0 @@
-.. _developer_guide_deprecation:
-
-===========
-Deprecation
-===========
-
-Please note this policy is not currently in force. We aim to restore deprecation for
-V0.5 but the policy may be revised, but it will be based on the below.
-
-Deprecation policy
-==================
-
-aeon `releases <https://github.com/aeon-toolkit/aeon/releases>`_ follow `semantic versioning <https://semver.org>`_.
-A release number denotes <major>.<minor>.<patch> versions.
-
-Our previous deprecation policy is as follows:
-
-* all interface breaking (not downwards compatible) changes to public interfaces must
-be accompanied by deprecation.
-* such changes or removals happen only at MINOR or MAJOR versions, not at PATCH versions.
-* deprecation warnings should be included for at least one MINOR version cycle
-before change or removal.
-
-
-Deprecation process
-===================
-
-The deprecation process is as follows:
-
-* **Raise a warning.** For all deprecated functionality, we raise a
-:code:`FutureWarning`. The warning message should give the version number when the functionality will be changed and describe the new usage.
-* **Add a TODO comment in the code.** Add the following TODO comment to all pieces of code that can be removed: :code:`TODO: remove in <version-number>`. For example, :code:`TODO: remove in v0.11.0`. The TODO comment should describe which steps need to be taken for deprecation (e.g. removal of arguments, removal of functions or blocks of code). If changes need to be applied across multiple places, place multiple TODO comments.
-* **Remove deprecated functionality.** As part of the release process, all deprecated functionality that is due to be removed will be removed by searching for the TODO comments.
-
-
-To raise the warning, we use the `deprecated <https://deprecated.readthedocs.io/en/latest/index.html>`_ package.
-The package provides depreciation helper functions such as the :code:`deprecated` decorator.
-When importing it from :code:`deprecated.sphinx`, it automatically adds a deprecation message to the docstring.
-You can decorate functions, methods or classes.
-
-Examples
---------
-
-In the examples below, the :code:`deprecated` decorator will raise a :code:`FutureWarning` saying that the functionality has been deprecated since version 0.9.0 and will be removed in version 0.11.0.
-
-Functions
-~~~~~~~~~
-
-.. code-block::
-
-    from deprecated.sphinx import deprecated
-
-    # TODO: remove in v0.7.0
-    @deprecated(version="0.6.0", reason="my_old_function will be removed in v0.7.0",
-category=FutureWarning)
-    def my_old_function(x, y):
-        return x + y
-
-Methods
-~~~~~~~
-
-.. code-block::
-
-    from deprecated.sphinx import deprecated
-
-    class MyClass:
-
-        # TODO: remove in v0.7.0
-        @deprecated(version="0..0", reason="my_old_method will be removed in v0.7.0",
-category=FutureWarning)
-        def my_old_method(self, x, y):
-            return x + y
-
-Classes
-~~~~~~~
-
-.. code-block::
-
-    from deprecated.sphinx import deprecated
-
-    # TODO: remove in v0.7.0
-    @deprecated(version="0.6.0", reason="MyOldClass will be removed in v0.7.0",
-category=FutureWarning)
-    class MyOldClass:
-        pass
-
-
-Special deprecations
-====================
-
-This section outlines the deprecation process for cases which use of ``deprecated`` does not cover.
-
-Deprecating tags
-----------------
-
-To deprecate tags, it needs to be ensured that warnings are raised when the tag is used.
-There are two common scenarios: removing a tag, or renaming a tag.
-
-For either scenario, the helper class ``TagAliaserMixin`` (in ``aeon.base``) can be used.
-
-To deprecate tags, add the ``TagAliaserMixin`` to ``BaseEstimator``, or another ``BaseObject`` descendant.
-It is advised to select the youngest descendant that fully covers use of the deprecated tag.
-``TagAliaserMixin`` overrides the tag family of methods, and should hence be the first class to inherit from
-(or in case of multiple mixins, earlier than ``BaseObject``).
-
-``alias_dict`` in ``TagAliaserMixin`` contains a dictionary of deprecated tags:
-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, and should have the same keys as ``alias_dict``.
-
-The ``TagAliaserMixin`` 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 are being accessed.
-
-When removing/renaming tags after the deprecation period,
-ensure to remove the removed tags from the dictionaries in ``TagAliaserMixin`` class.
-If no tags are deprecated anymore (e.g., all deprecated tags are removed/renamed),
-ensure to remove this class as a parent of ``BaseObject`` or ``BaseEstimator``.
diff --git a/docs/developer_guide/documentation.rst b/docs/developer_guide/documentation.rst
index c6f39fb5..3336ad88 100644
--- a/docs/developer_guide/documentation.rst
+++ b/docs/developer_guide/documentation.rst
@@ -206,3 +206,5 @@ dependencies listed in
 
       cd docs
       make html
+
+You may need to install pandoc to build the documentation locally.
diff --git a/docs/get_involved/code_of_conduct.rst b/docs/get_involved/code_of_conduct.rst
deleted file mode 100644
index d63a909f..00000000
--- a/docs/get_involved/code_of_conduct.rst
+++ /dev/null
@@ -1,342 +0,0 @@
-.. _code_of_conduct:
-
-Code of Conduct
-===============
-
-We value the participation of every member of our community and want to
-ensure an that every contributor has an enjoyable and fulfilling
-experience. Accordingly, everyone who participates in the aeon project
-is expected to show respect and courtesy to other community members at
-all times.
-
-The CoCC and all project members, are dedicated to a **harassment-free
-experience for everyone**, regardless of gender, gender identity and
-expression, sexual orientation, disability, physical appearance, body
-size, race, age or religion. **We do not tolerate harassment by
-and/or of members of our community in any form**.
-
-We are particularly motivated to support new and/or anxious
-collaborators, people who are looking to learn and develop their skills,
-and anyone who has experienced discrimination in the past.
-
-To make clear what is expected, we ask all members of the community to
-conform to the following Code of Conduct.
-
-
-1 Introduction
---------------
-
-aeon is a community-oriented and -led project. We value the
-involvement of everyone in the community. We are committed to creating a
-friendly and respectful place for learning, teaching and contributing.
-All participants in our in-person events and online communications are
-expected to show respect and courtesy to others at all times.
-
-To make clear what is expected, everyone participating in activities
-associated with the aeon project is required to conform to this Code
-of Conduct. This Code of Conduct applies to all spaces managed by the
-aeon project including, but not limited to, in-person focus groups and
-workshops, and communications online via GitHub.
-
-The CoCC is responsible
-for enforcing the Code of Conduct. It can be reached via contact@aeon-toolkit.org.
-
-Reports may be reviewed by other members of the core development team,
-unless there is a conflict of interest, and will be kept confidential.
-
-2 Code of Conduct
------------------
-
-The aeon team are dedicated to providing a welcoming and supportive
-environment for all people, regardless of background or identity. As
-such, we do not tolerate behaviour that is disrespectful to our
-community members or that excludes, intimidates, or causes discomfort to
-others. We do not tolerate discrimination or harassment based on
-characteristics that include, but are not limited to: gender identity
-and expression, sexual orientation, disability, physical appearance,
-body size, citizenship, nationality, ethnic or social origin, pregnancy,
-familial status, veteran status, genetic information, religion or belief
-(or lack thereof), membership of a national minority, property, age,
-education, socio-economic status, technical choices, and experience
-level.
-
-Everyone who participates in the aeon project activities is required
-to conform to this Code of Conduct. This Code of Conduct applies to all
-spaces managed by the aeon project including, but not limited to, in
-person focus groups and workshops, and communications online via GitHub.
-By participating, contributors indicate their acceptance of the
-procedures by which the aeon project core development team resolves
-any Code of Conduct incidents, which may include storage and processing
-of their personal information.
-
-2.1 Expected Behaviour
-~~~~~~~~~~~~~~~~~~~~~~
-
-We are confident that our community members will together build a
-supportive and collaborative atmosphere at our events and during online
-communications. The following bullet points set out explicitly what we
-hope you will consider to be appropriate community guidelines:
-
--  **Be respectful of different viewpoints and experiences**. Do not
-   engage in homophobic, racist, transphobic, ageist, ableist, sexist,
-   or otherwise exclusionary behaviour.
--  **Use welcoming and inclusive language**. Exclusionary comments or
-   jokes, threats or violent language are not acceptable. Do not address
-   others in an angry, intimidating, or demeaning manner. Be considerate
-   of the ways the words you choose may impact others. Be patient and
-   respectful of the fact that English is a second (or third or fourth!)
-   language for some participants.
--  **Do not harass people**. Harassment includes unwanted physical
-   contact, sexual attention, or repeated social contact. Know that
-   consent is explicit, conscious and continuous—not implied. If you are
-   unsure whether your behaviour towards another person is welcome, ask
-   them. If someone tells you to stop, do so.
--  **Respect the privacy and safety of others**. Do not take photographs
-   of others without their permission. Do not share other participant’s
-   personal experiences without their express permission. Note that
-   posting (or threatening to post) personally identifying information
-   of others without their consent ("doxing") is a form of harassment.
--  **Be considerate of others’ participation**. Everyone should have an
-   opportunity to be heard. In update sessions, please keep comments
-   succinct so as to allow maximum engagement by all participants. Do
-   not interrupt others on the basis of disagreement; hold such comments
-   until they have finished speaking.
--  **Don’t be a bystander**. If you see something inappropriate
-   happening, speak up. If you don't feel comfortable intervening but
-   feel someone should, please feel free to ask a member of the Code of
-   Conduct response team for support.
--  As an overriding general rule, please **be intentional in your
-   actions and humble in your mistakes**.
-
-All interactions should be professional regardless of platform: either
-online or in-person. See `this explanation of the four social
-rules <https://www.recurse.com/manual#sub-sec-social-rules>`__ - no
-feigning surprise, no well-actually's, no back-seat driving, no subtle
--isms - for further recommendations for inclusive behaviours.
-
-2.2 Unacceptable Behaviour
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Examples of unacceptable behaviour by aeon community members at any
-project event or platform include:
-
--  written or verbal comments which have the effect of excluding people
-   on the basis of membership of any specific group
--  causing someone to fear for their safety, such as through stalking,
-   following, or intimidation
--  violent threats or language directed against another person
--  the display of sexual or violent images
--  unwelcome sexual attention
--  nonconsensual or unwelcome physical contact
--  sustained disruption of talks, events or communications
--  insults or put downs
--  sexist, racist, homophobic, transphobic, ableist, or exclusionary
-   jokes
--  excessive swearing
--  incitement to violence, suicide, or self-harm
--  continuing to initiate interaction (including photography or
-   recording) with someone after being asked to stop
--  publication of private communication without consent
-
-2.3 Consequences of Unacceptable Behaviour
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Participants who are asked to stop any inappropriate behaviour are
-expected to comply immediately. This applies to all aeon community
-events and platforms, either online or in-person. If a participant
-engages in behaviour that violates this Code of Conduct, any member of
-the core development team may warn the offender, ask them to leave the
-event or platform (without refund), or impose any other appropriate
-sanctions (see the `enforcement manual <#enforcement-manual>`__ for
-details).
-
-2.4 Feedback
-~~~~~~~~~~~~
-
-This Code of Conduct is not intended as a static set of rules by which
-everyone must abide. Rather, you are invited to make suggestions for
-updates or clarifications by contacting the CoCC via contact@aeon-toolkit.org
-or by making a pull request to this document on GitHub.
-
-3 Incident Reporting Guidelines
--------------------------------
-
-3.1 Contact points
-~~~~~~~~~~~~~~~~~~
-
-Please contact the CoCC directly by email, see 4.1.
-
-3.2 Alternate contact points
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-tbd
-
-3.3 What to do if someone is in physical danger
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you believe someone is in physical danger, please contact the
-appropriate emergency responders.
-
-3.4 Code of Conduct Enforcement
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We believe it is important to have an actionable plan before something
-happens. We therefore have a detailed enforcement policy which is
-available in the Enforcement Manual below.
-
-4 Enforcement Manual
---------------------
-
-This is the enforcement manual followed by the aeon project research
-team. It's used when we respond to an issue to make sure we're
-consistent and fair. Enforcement of the Code of Conduct should be
-respectful and not include any harassing behaviours.
-
-4.1 The Code of Conduct Committee (COCC)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The aeon Code of Conduct committee currently consists of members listed
-`here <https://github.com/aeon-toolkit/aeon/blob/main/docs/about/team.rst>`__.
-
-We encourage community members to step up to become a member of the committee.
-
-4.2 Urgent Situations: Acting Unilaterally
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If the incident involves physical danger, or involves a threat to
-anyone's safety (e.g. threats of violence), any member of the community
-may -- and should -- act unilaterally to protect the safety of any
-community member. This can include contacting law enforcement (or other
-local personnel) and speaking on behalf of the aeon team.
-
-If the act is ongoing, any community member may act immediately, before
-reaching consensus, to diffuse the situation. In ongoing situations, any
-member may at their discretion employ any of the tools available in this
-enforcement manual, including bans and blocks online, or removal from a
-physical space.
-
-In situations where an individual community member acts unilaterally,
-they must inform the CoCC as soon as possible, and report their
-actions for review within 24 hours.
-
-4.3 Less-Urgent Situations
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Upon receiving a report of an incident, the Code of Conduct committee
-will review the incident and determine, to the best of her ability:
-
--  whether this is an ongoing situation
--  whether there is a threat to anyone's physical safety
--  what happened
--  whether this event constitutes a Code of Conduct violation
--  who, if anyone, was the bad actor
-
-This information will be collected either in person or in writing. The
-Code of Conduct committee will provide a written summary of the
-information surrounding the incident. All participants will be
-anonymised in the summary report, referred to as "Community Member 1",
-"Community Member 2", or "Research Team Member 1". The "de-anonymising
-key" will be kept in a separate file and only accessed to link repeated
-reports against the same person over time.
-
-The Code of Conduct committee will aim to have a resolution agreed upon
-within one week. In the event that a resolution can't be determined in
-that time, a member of the Code of Conduct committee will respond to the
-reporter(s) with an update and projected timeline for resolution.
-
-4.4 Resolutions
-~~~~~~~~~~~~~~~
-
-The Code of Conduct committee will seek to agree on a resolution by
-consensus of all members investigating the report in question.
-
-Possible responses may include:
-
--  A mediated conversation or agreement between the impacted community
-   members.
--  A request for a verbal or written apology, public or private, from a
-   community member.
--  A public announcement clarifying community responsibilities under the
-   Code of Conduct.
--  Nothing, if the issue reported is not a violation or outside of the
-   scope of this Code of Conduct.
--  A private in-person conversation between a member of the research
-   team and the individual(s) involved. In this case, the person who has
-   the conversation will provide a written summary for record keeping.
--  A private written reprimand from a member of the research team to the
-   individual(s) involved. In this case, the research team member will
-   deliver that reprimand to the individual(s) over email.
--  A public announcement of an incident, ideally in the same venue that
-   the violation occurred (i.e. on the listserv for a listserv
-   violation; GitHub for a GitHub violation, etc.). The committee may
-   choose to publish this message elsewhere for posterity.
--  An imposed "time out" from online spaces. CoCC will
-   communicate this "time out" to the individual(s) involved.
--  A permanent or temporary ban from some or all aeon project spaces
-   (GitHub, in-person events etc). The research team will maintain
-   records of all such bans so that they may be reviewed in the future,
-   extended to a Code of Conduct safety team as it is built, or
-   otherwise maintained. If a member of the community is removed from an
-   event they will not be reimbursed for any part of the event that they
-   miss.
-
-Once a resolution is agreed upon, but before it is enacted, a member of
-the Code of Conduct committee will contact the original reporter and any
-other affected parties and explain the proposed resolution. The Code of
-Conduct committee member will ask if this resolution is acceptable, and
-must note feedback for the record. However, the Code of Conduct
-committee is not required to act on this feedback.
-
-4.5 Conflicts of Interest
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In case of conflict of interest of aeon CoC committee members, you can report to the
-Community Council, or, in case of events, to the organisation at which the event is
-taking place (e.g., a university).
-
-5 Acknowledgements
-------------------
-
-This code is adapted from the `The Turing
-Way <https://github.com/alan-turing-institute/the-turing-way>`__ project
-with Dr Kirstie Whitaker as lead investigator and based on the
-`Carpentries Code of
-Conduct <https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html>`__
-with sections from the `Alan Turing Institute Data Study Group Code of
-Conduct <https://docs.google.com/document/d/1iv2cizNPUwtEhHqaezAzjIoKkaIX02f7XbYmFMXDTGY/edit>`__.
-All are used under the creative commons attribution license.
-
-The Carpentries Code of Conduct was adapted from guidelines written by
-the `Django
-Project <https://www.djangoproject.com/conduct/enforcement-manual/>`__,
-which was itself based on the `Ada Initiative
-template <http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Responding_to_reports>`__
-and the `PyCon 2013 Procedure for Handling Harassment
-Incidents <https://us.pycon.org/2013/about/code-of-conduct/harassment-incidents/>`__.
-Contributors to the Carpentries Code of Conduct were: Adam Obeng,
-Aleksandra Pawlik, Bill Mills, Carol Willing, Erin Becker, Hilmar Lapp,
-Kara Woo, Karin Lagesen, Pauline Barmby, Sheila Miguez, Simon Waldman,
-Tracy Teal.
-
-The Turing Institute Data Study Group Code of Conduct was heavily
-adapted from the `Citizen Lab Summer Institute 2017 Code of
-Conduct <https://citizenlab.ca/summerinstitute/codeofconduct.html>`__
-and used under a CC BY 2.5 CA license. Citizen Lab based their Code of
-Conduct on the `xvzf Code of Conduct <http://xvzf.io/>`__, the
-`Contributor Covenant <http://contributor-covenant.org/>`__, the `Django
-Code of Conduct and Reporting
-Guide <https://www.djangoproject.com/conduct/>`__ and we are also
-grateful for `this guidance from Ada
-Initiative <http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Responding_to_reports>`__.
-
-We really appreciate the work that all of the communities linked above
-have put into creating such a well considered process.
-
-This Code of Conduct is licensed under a `Creative Commons Attribution
-4.0 International <https://creativecommons.org/licenses/by/4.0/>`__ (CC
-BY 4.0 CA) license which means you are free to share and adapt the work
-so long as the attribution to `The Turing
-Way <https://github.com/alan-turing-institute/the-turing-way>`__ project
-with Dr Kirstie Whitaker as the principal investigator is retained,
-along with the attribution to the Carpentries, The Alan Turing Institute
-Data Study Group organising team, Citizen Lab and the other resources.
diff --git a/docs/get_involved/governance.md b/docs/get_involved/governance.md
new file mode 100644
index 00000000..ffde585a
--- /dev/null
+++ b/docs/get_involved/governance.md
@@ -0,0 +1,165 @@
+# Governance and decision-making
+
+The purpose of this document is to formalize the governance process used by the `aeon`
+project, to clarify how decisions are made and how the various elements of our community
+interact. Our goal is to ensure a transparent, democratic, and inclusive decision-making
+process that empowers all community members to contribute to the project.
+
+`aeon` is a community-owned and community-run project. Anyone with an interest in the
+project can join the community, contribute to the project design and participate in the
+decision-making process.
+
+This document establishes the decision-making structure used by `aeon` which strives to
+find consensus from the community, while avoiding any deadlocks. Our decision-making
+process involves different roles, each with specific responsibilities.
+
+## Roles
+
+### Contributors
+
+Contributors are community members who have contributed in concrete ways to the project.
+Anyone can become a contributor, and contributions can take many forms – not only code –
+as detailed in the contributing guide. Contributors play a crucial role in shaping the
+project through participating in discussions and influencing the decision-making
+process.
+
+### Core Developers
+
+Core developers are community members that have made significant contributions and are
+trusted to continue the development of the project and engage with its community.
+Core developers are granted write access to the `aeon` repository and voting rights
+on all project decisions. They are expected to review code contributions
+and engage with topics or code they are knowledgeable about.
+
+New core developers are nominated by existing core developers and are subject to a
+two-thirds majority vote of existing core developers. Core developers are expected to
+maintain a reasonable amount of engagement with the project. Developing code,
+interacting with contributions and engaging with the broader community are all valid
+contributions for core developers.
+
+Core developers who no longer want or are able to engage with the project are expected
+to resign. Core developers may lose their role after a year of inactivity with no
+contributions (see above) and engagement with fellow developers. If this lack of
+engagement has been demonstrated, another core developer can suggest removal and create
+a pull request on the `aeon` repository. The removal will complete if this pull request
+is successfully merged.
+
+### Workgroups
+
+Extra responsibilities are delegated to workgroups, which are groups of contributors
+with a core developer lead. Workgroups are responsible for specific areas of the project
+and are expected to manage and make decisions in their area of responsibility.
+Workgroups are expected to be transparent about their activities and decisions, and to
+engage with the community where possible.
+
+Membership of workgroups and the leadership position of a workgroup is subject to a
+two-thirds majority vote of core developers. Workgroup members unable to fulfill their
+responsibilities are expected to resign from the workgroup. It is the responsibility of
+the workgroup lead to safeguard access to relevant project resources and accounts.
+
+#### Infrastructure Workgroup
+
+The infrastructure workgroup maintains the infrastructure of `aeon` to ensure the
+smooth running of the project. This includes ensuring the website remains online, that
+CI remains functional and other related tasks.
+
+The infrastructure workgroup maintains ownership of the `aeon` GitHub organisation and
+ReadTheDocs account.
+
+#### Release Management Workgroup
+
+The release management workgroup manages `aeon` releases. This includes deciding on
+release schedules, managing release candidates and publishing releases.
+
+The release management workgroup maintains ownership of the `aeon` PyPi project and
+conda feedstock.
+
+#### Finance Workgroup
+
+The finance workgroup is responsible for managing the project finances. This includes
+approving any project expenses and managing any finance related accounts.
+
+#### Communications Workgroup
+
+The role of the communications workgroups is to interact with the broader community
+through the `aeon` social network accounts and discussion forums. It is the
+responsibility of the communications workgroup to manage and maintain the `aeon`
+Twitter, LinkedIn, Slack and other relevant communications accounts.
+
+The communications team maintains access to social networking accounts and is
+responsible for managing access to the `aeon` email address. To help manage
+GitHub discussions, the communication workgroup is given triage access to the `aeon`
+GitHub repository.
+
+#### Code of Conduct Workgroup
+
+The Code of Conduct Workgroup (CoCW) consists of contributors tasked with making sure
+`aeon` remains a welcoming and inclusive community. CoCW responsibilities include
+maintaining the `aeon` Code of Conduct (CoC) and managing reports of breaking the CoC.
+CoCW members are expected to review reports of CoC violations, contact and discuss with
+involved individuals and make recommendations on actions to take.
+
+Any CoCW members involved in a CoC report or CoCW members which have a conflict of
+interest regarding the report are expected to recuse themselves from the process. The
+CoCW is given triage access to the `aeon` GitHub repository to moderate discussions if
+necessary.
+
+## Decision-Making Process
+
+Decisions about the future of the project are announced publicly to allow discussion
+with all members of the community. The whole process from proposal to implementation
+is fully visible, apart from topics considered sensitive. All non-sensitive project
+management discussion takes place on the contributors channel on the project
+Slack and/or the issue tracker. Occasionally, sensitive discussion and votes such as
+appointments will occur in private Slack channels.
+
+For most decisions a consensus seeking process of all interested contributors is used.
+Contributors try to find a resolution that has no open objections among core developers.
+If a reasonable amount of time (at least 7-days for non-trivial changes) has passed
+since the last change to a proposed contribution, the proposal has at least one approval
+(+1) and no rejections (-1) from core developers, it can be approved by lazy consensus.
+If a change is rejected, it is expected that an explanation and description of
+conditions (if any) to withdraw the rejection is provided.
+
+At any point during the discussion, any core developer in favour of a change can call
+for a vote, which will conclude two weeks from the call for the vote. Any vote to
+bypass a rejection from a core developer must be backed by an AEP (see the following
+section). For major contributions (such as a new module or major framework redesigns)
+an AEP may be requested without a rejection or vote. In the event a vote is called,
+the proposal must receive a two-thirds majority of core developers to be approved.
+
+All changes to the `aeon` code or documentation should be done via Pull Request.
+By default, push rights to the `main` GitHub branch are restricted for all core developers.
+In emergencies, the infrastructure workgroup may temporarily revoke the branch
+protection for group members and make direct commits, but this should happen only in
+emergencies where harm will come to the project unless timely action is taken.
+
+### Enhancement Proposals
+
+For contentious decision-making votes (not including appointment votes), a proposal
+must have been made public for discussion before the vote. It is recommended that this
+proposal is made as a consolidated document, in the form of an “aeon Enhancement
+Proposal” (AEP). The AEP template is available
+[here](https://github.com/aeon-toolkit/aeon-admin/blob/main/aep/aep_template.md), but
+the use of said template is not a requirement. A detailed issue or pull request can
+substitute an AEP if all parties believe it is sufficient, but a more formal proposal
+can be requested by any core developer.
+
+Having a rejection on a pull request does not necessitate the creation of an AEP and
+further discussion to find consensus can be held, but one must be created prior to any
+vote to bypass a rejection.
+
+A vote does not have to be called for an AEP to be submitted. If a contributor wants to
+make a more formal proposal or believes a change will be controversial, an AEP can be
+submitted to the community for discussion and comment.
+
+## Acknowledgements
+
+Substantial portions of this document were adapted from or inspired by the following
+projects governance documents:
+
+- [Scikit-learn](https://scikit-learn.org/stable/governance.html)
+- [SciPy](https://docs.scipy.org/doc/scipy/dev/governance.html)
+- [NumPy](https://numpy.org/doc/stable/dev/governance/governance.html#governance)
+- [Jupyter](https://jupyter.org/governance/overview.html)
+- [Pandas](https://pandas.pydata.org/about/governance.html)
diff --git a/docs/get_involved/governance.rst b/docs/get_involved/governance.rst
deleted file mode 100644
index 880a3560..00000000
--- a/docs/get_involved/governance.rst
+++ /dev/null
@@ -1,654 +0,0 @@
-.. _governance:
-
-Governance
-==========
-
-Overview
---------
-
-aeon is a consensus-based community project. Anyone with an interest in the project can join the community, contribute to the project, and participate in the governance process. This document describes how that participation takes place, which roles we have in our community, how we make decisions, and how we acknowledge contributions.
-
-We are particularly motivated to support new and/or anxious contributors, people who are looking to learn and develop their skills, and members of groups underrepresented in the tech sector. Go to our `contributing guide <https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTING.rst>`__ for more details.
-
-.. list-table::
-   :header-rows: 1
-
-   * - Section
-     - Purpose
-   * - :ref:`code-of-conduct`
-     - How we expect all members of the aeon community to interact
-   * - :ref:`roles`
-     - What roles we have in aeon's community and what rights and responsibilities they have
-   * - :ref:`decision-making`
-     - How and by whom decisions are made
-   * - :ref:`acknowledging-contributions`
-     - How we acknowledge contributions
-   * - :ref:`outlook`
-     - What we may change in the future
-
-.. _code-of-conduct:
-
-Code of Conduct
----------------
-
-We value the participation of every member of our community and want to
-ensure an that every contributor has an enjoyable and fulfilling
-experience. Accordingly, everyone who participates in the aeon project
-is expected to show respect and courtesy to other community members at
-all times.
-
-We ask all members of the community to conform to our `Code of
-Conduct <https://github.com/aeon-toolkit/aeon/blob/main/CODE_OF_CONDUCT.rst>`__.
-
-.. _roles:
-
-Roles
------
-
-We distinguish between the following key roles that community members
-may exercise. For each role, we describe their rights and
-responsibilities, and appointment process in more detail below.
-
-.. list-table::
-   :header-rows: 1
-
-   * - Role
-     - Rights/responsibilities
-     - Appointment
-   * - :ref:`contributors`
-     - \-
-     - Concrete contribution
-   * - :ref:`algorithm-maintainers`
-     - Algorithm maintenance, voting and veto right for changes to their algorithm
-     - Algorithm contribution or appointment by current maintainer
-   * - :ref:`core-developers`
-     - Direct write access, issue/PR management, veto right, voting, nomination
-     - Nomination by core developers, vote by core developers, 2/3 majority
-   * - :ref:`coc-committee-members`
-     - CoC maintenance, investigation and enforcement
-     - Nomination by core developers, vote by core developers, 2/3 majority
-   * - :ref:`cc-members`
-     - Conflict resolution, technical leadership, project management
-     - Nomination by core developers, vote by core developers, 2/3 majority
-
-.. _contributors:
-
-Contributors
-~~~~~~~~~~~~
-
-Contributors are community members who have contributed in concrete ways
-to the project. Anyone can become a contributor, and contributions can
-take many forms – not only code – as detailed in the `contributing
-guide <https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTING.rst>`__.
-
-For more details on how we acknowledge contributions, see :ref:`acknowledging-contributions` below.
-
-All contributors are listed in `CONTRIBUTORS.md <https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTORS.md>`__.
-
-.. _algorithm-maintainers:
-
-Algorithm maintainers
-~~~~~~~~~~~~~~~~~~~~~
-
-Algorithm maintainers are contributors who have contributed an
-algorithm. They have the same voting rights as core developers with
-regard to their algorithm.
-
-In aeon, algorithms are encapsulated in classes with specific
-interface requirements and are called estimators. To faciliate
-maintainership questions, we try to write algorithms in separate files
-when possible.
-
-To clarify, "algorithm" in the above sense means "implemented estimator class".
-That is, algorithm maintainers gain rights and responsibilities with respect to
-that python code.
-They do not gain any rights on abstract methodology, e.g., in a case where
-the class implements methodology invented by third parties.
-
-In particular, algorithm maintainers do not gain rights or responsibilities on other,
-potential implementations of the same methodology in their estimator class.
-
-Rights and responsibilities
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. list-table::
-  :header-rows: 1
-
-  * - Right/responsibility
-    - Description
-  * - Decision making with respect to their algorithm
-    - Algorithm maintainers can partipate in the decision making process by vetoing changes and casting votes with regard to proposed changes to their algorithm. This does not extend to proposed changes to the common framework and API.
-  * - Maintenance
-    - They are responsible for maintaining the code and documentation for their algorithm, including bug fixes, unit testing, coding style, compliance with the common API, docstrings, documentation and tutorials notebooks.
-  * - Support
-    - They are the first point of contact for users and other contributors for all questions, issues and proposals regarding their algorithm.
-
-Recall, "algorithm" refers to estimator classes.
-
-Therefore, the above rights and responsibilities exclude any power on further, potential implementations of the same or similar methodology.
-
-For instance, an algorithm maintainer of algorithm A implemented in class X cannot prohibit implementation of algorithm A in class Y.
-They can only make decisions about changes on class X. Class Y can be owned by a different algorithm maintainer.
-
-In particular, there can be multiple classes implementing algorithm A, and the algorithm maintainer of class X cannot prohibit implementation of, or make decisions on class Y.
-
-Expectations
-^^^^^^^^^^^^
-
-Without restriction to eligibility, it is generally expected that algorithm maintainers
-have a very good technical and methodological understanding of the algorithm they maintain.
-
-This understanding is typically present in inventors or proponents of said algorithm,
-but it is not necessary to be the inventor of an algorithm to be its maintainer.
-
-Eligibility
-^^^^^^^^^^^
-
-Anyone is eligible to be an algorithm maintainer.
-
-Anyone is eligible to be an algorithm maintainer of a specific algorithm that does not already have an algorithm maintainer.
-
-The presence of a specific implementation of a given abstract algorithm does not prevent anyone from becoming
-the algorithm maintainer of a different implementation of the same (or similar) abstract algorithm.
-
-Appointment
-^^^^^^^^^^^
-
-The contributor who contributes an algorithm is automatically appointed
-as its first maintainer.
-
-Algorithm maintainers are listed in the `CODEOWNERS <https://github
-.com/alan-turing-institute/aeon/blob/main/CODEOWNERS>`__ file.
-
-When an algorithm maintainer resigns, they can appoint another contributor as the
-new algorithm maintainer. No vote is required. This change should be reflected in the ``CODEOWNERS`` file.
-
-Algorithm maintainers can be appointed by CC simple majority for any algorithm without maintainers.
-
-End of tenure
-^^^^^^^^^^^^^
-
-If algorithm maintainers can no longer fulfil their maintenance
-responsibilities, maintainers are expected to resign.
-
-Algorithm maintainers that have been unresponsive for a 3 month period automatically
-give up their rights and responsibilities as algorithm maintainers.
-
-Unresponsiveness is defined as:
-* not engaging with decision making procedures within the reasonably time frames defined there
-* not reacting to issues or bug reports related to the algorithm, within 14 days
-
-.. _core-developers:
-
-Core developers
-~~~~~~~~~~~~~~~
-
-Core developers are contributors who have shown that they are dedicated
-to the continued development of the project through ongoing engagement
-with the community.
-
-Current core developers are listed in the `core-developers
-team <https://www.aeon-toolkit.org/en/stable/about/team.html>`__
-within the aeon organisation on GitHub.
-
-.. _rights-and-responsibilities-1:
-
-Rights and responsibilities
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. list-table::
-   :header-rows: 1
-
-   * - Right/responsibility
-     - Description
-   * - Direct access
-     - Being a core developer allows contributors to more easily carry on with their project related activities by giving them direct access to the project’s repository.
-   * - Issue/PR management
-     - Core developers are responsible for reviewing and managing issues and pull requests. This includes commenting on issues, reviewing code contributions, merging approved pull requests, and closing issues once resolved.
-   * - Decision making
-     - They can participate in the decision making process by vetoing changes and casting votes.
-   * - Nomination
-     - They can nominate new core developers, CoC committee members and CC members.
-
-Eligibility
-^^^^^^^^^^^
-
-Anyone is eligible to be a core developer.
-
-.. _appointment-1:
-
-Appointment
-^^^^^^^^^^^
-
-New core developers can be nominated by any current core developer. Once
-they have been nominated, there will be a vote by the current core
-developers.
-
-Voting on appointments is one of the few activities that takes
-place on the project’s private communication channels. The vote will be
-anonymous.
-
-While it is expected that most votes will be unanimous, a 2/3 majority of
-the cast votes is enough. The vote needs to be open for 7 days.
-
-End of tenure
-^^^^^^^^^^^^^
-
-Core developers can resign voluntarily at any point in time, by informing the CC in writing.
-
-Core developers that have not contributed to the project in the past
-one-year-period will automatically become *inactive*
-and give up their rights and responsibilities. When they become active
-again, they can retake their role without having to be appointed.
-
-Becoming inactive in the above sense means not contributing for the period via:
-
-* creating pull requests
-* commenting on pull requests or issues
-* attending one of the regular meetings
-
-Becoming active (after becoming inactive) in the above sense requires one of:
-
-* an approved pull request authored by the core developer
-* a contribution to the community that is minuted in one of the regular meetings
-
-.. _coc-committee-members:
-
-CoC committee members
-~~~~~~~~~~~~~~~~~~~~~
-
-CoC members are contributors with special rights and responsibilities.
-The current members of the CoC committee are listed in the
-`CoC <https://www.aeon-toolkit.org/en/stable/about/team.html>`__.
-
-.. _rights-and-responsibilities-2:
-
-Rights and responsibilities
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-CoC committee members are responsible for investigating potential CoC
-incidents and enforcing the CoC.
-They are the point of contact for reporting potential CoC incidents.
-
-In addition, they are responsible for maintaining and improving the CoC.
-
-Eligibility
-^^^^^^^^^^^
-
-Anyone is eligible to be a CoC Committee member with the exception that a person that
-is CC member can not be CoC member.
-
-
-.. _appointment-2:
-
-Appointment
-^^^^^^^^^^^
-
-Membership of the CoC is by nomination by a core developer and a vote by
-all core developers. A vote by the core
-developers will stay open for 7 days. CoC committee
-membership votes are subject to:
-
-* a 2/3 majority of all cast votes
-
-The vote will take place in private communication channels and will be
-anonymous.
-
-
-End of tenure
-^^^^^^^^^^^^^
-
-CoC members can resign voluntarily at any point in time, by informing the CoC or core developers in writing.
-
-CoC membership ends automatically after a person has been in this role for 2 years. If the person would like to continue the membership of CoC, a new appointment vote has to happen following the procedure as for appointment of a new CoC member.
-The vote can be requested by the affected CoC member or by any core developer.
-
-.. _cc-members:
-
-CC members
-~~~~~~~~~~
-
-CC (community council) members are core developers with additional rights and
-responsibilities to avoid deadlocks and ensure a smooth progress of the
-project.
-
-Current CC members are listed in the `community-council
-team <https://www.aeon-toolkit.org/en/stable/about/team.html>`__
-within the aeon organisation on GitHub.
-
-.. _rights-and-responsibilities-3:
-
-Rights and responsibilities
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. list-table::
-   :header-rows: 1
-
-   * - Right/responsibility
-     - Description
-   * - Decision making: conflict resolution
-     - see :ref:`stage-3` below
-   * - Technical direction
-     - Strategic planning, development roadmap
-   * - Project management
-     - Funding, collaborations with external organisations, community infrastructure (chat server, GitHub repositories, continuous integration accounts, social media accounts)
-
-Eligibility
-^^^^^^^^^^^
-
-Only core developers are eligible for appointment as CC members.
-Non-core-developers can be nominated, but this must be accompanied
-by a nomination for core developer. Members of the CoC Committee are
-not eligible to become CC members.
-
-.. _appointment-3:
-
-Appointment
-^^^^^^^^^^^
-
-Appointment to the CC is by nomination by a core developer and a vote by
-all core developers. A vote by core developers will
-stay open 7 days. Approval of appointment requires:
-
-* a 2/3 majority of all cast votes
-
-The core developer vote takes place in private communication channels,
-visible to, and only to, core developers, and is anonymous.
-The CC members' vote takes place in private communication channels,
-visible to, and only to, CC members, and is anonymous.
-
-End of tenure
-^^^^^^^^^^^^^
-
-CC members can resign voluntarily at any point in time, by informing the CC or core developers in writing.
-
-CC members who do not actively engage with their CC member role responsibilities are
-expected to resign voluntarily.
-
-Tenure also ends automatically when a CC member's tenure as core developer ends,
-e.g., through resignation or inactivity.
-
-CC membership ends automatically after a person has been in this role for 2 years. If the person would like to continue the membership of CC, a new appointment vote has to happen following the procedure as for appointment of a new CC member.
-The vote can be requested by the affected CC member or by any core developer.
-
-Communications
-^^^^^^^^^^^^^^
-
-The CC has regular public meetings that the full community is welcome to attend.
-
-For more details about our meetings, please go to our `community-council repository <https://github.com/aeon/community-council/>`__.
-
-To contact the CC directly, please send an email to contact@aeon-toolkit.org.
-
-
-.. _decision-making:
-
-Decision making
----------------
-
-The purpose of this section is to formalize the decision-making process
-used by the aeon project. We clarify:
-
-* what types of changes we make decision on,
-* how decisions are made, and
-* who participates in the decision making.
-
-aeon’s decision-making process is designed to take into account
-feedback from all community members and strives to find consensus, while
-avoiding deadlocks when no consensus can be found.
-
-All discussion and votes takes place on the project’s `issue
-tracker <https://github.com/aeon-toolkit/aeon/issues>`__,
-`pull requests <https://github.com/aeon-toolkit/aeon/pulls>`__ or an :ref:`steps`. Some
-sensitive discussions and appointment votes occur on private chats.
-
-The CC reserves the right to overrule decisions.
-
-We distinguish between the following types of proposed changes. The
-corresponding decision making process is described in more detail below.
-
-.. list-table::
-   :header-rows: 1
-
-   * - Type of change
-     - Decision making process
-   * - Code additions, such as new algorithms
-     - Lazy consensus, supported by the :ref:`algorithm-inclusion-guidelines`
-   * - Minor documentation changes, such as typo fixes, or addition/correction of a sentence
-     - Lazy consensus
-   * - Code changes and major documentation changes
-     - Lazy consensus
-   * - Changes to the API design, hard dependencies, or supported versions
-     - Lazy consensus, requires a :ref:`steps`
-   * - Changes to aeon's governance (this document and the CoC)
-     - Lazy consensus, requires a :ref:`steps`
-   * - Appointment
-     - Directly starts with voting (stage 2)
-
-.. _stage-1:
-
-Stage 1: lazy consensus with veto right
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-aeon uses a “consensus seeking” process for making decisions. The
-community tries to find a resolution that has no open objections among
-core developers.
-
--  Proposed changes should be in the form of GitHub pull requests (PR).
-   Some changes also require a worked out :ref:`steps`. This depends on the type of change, see
-   `decision making process <#Decision-making>`__ above.
--  For a proposed change to be approved via lazy consensus, it needs to
-   approval by at least one core developer (lazy consensus) and no rejection by a core developer (veto right).
-   The approval required for this condition must be by a core developer different from a proposer.
--  For a proposed change to be rejected via lazy consensus, it needs to receive a
-   rejection by at least one core developer, and no acceptance by a core developer.
--  Approvals must be in the form of a GitHub PR approval of the PR in question.
-   Rejections can be expressed as -1 comments, or any written comments
-   containing "I formally reject" in the PR, in reference to it.
--  Proposers are expected to give reasonable time for consideration, that is,
-   time and opportunity for core developers to review and
-   give their opinion on the PR.
-   14 days constitute "reasonable time" in the above sense.
-   The period resets at every new change made to the PR.
-   It starts only when all GitHub checks pass.
--  During this period, the PR can be merged if it has an approval and no rejection, but should be
-   reverted if it receives a rejection in addition.
--  If the "reasonable time" period elapses and no approval or rejection has been expressed on a PR,
-   the PR is scheduled at the top of agenda for the next developer meetup.
-   In that meeting, a core developer is assigned to review the PR and either approve or reject within 7 days of the meeting.
-
-Failure of lazy consensus, in the above sense, can arise only under the following condition:
-at least one approval and at least one rejection in the PR.
-
-When no consensus can be found, the decision is escaled to :ref:`stage-2`.
-
-.. _stage-2:
-
-Stage 2: voting
-~~~~~~~~~~~~~~~
-
-Voting takes place:
-
-* when no lazy consensus can be found in stage 1 above
-* for appointments
-
--  The start of a voting period after stage 1 is at the moment the lazy consensus fails.
--  Start and end time of the vote must be announced in the core developer channel, and on the PR (if on a PR).
--  The vote will conclude 7 days from the call for the vote.
--  Votes are voluntary. Abstentions are allowed. Core developers can
-   abstain by simply not casting a vote.
--  All votes are a binary vote: for or against accepting the proposal.
--  Votes are casts as comments: +1 (approval) or -1 (rejection).
-
-For all types of changes, except appointments, votes take place on the
-related public issue or pull request. The winning condition is a 2/3
-majority of the votes cast by core developers (including CC members) for the proposal.
-If the proposal cannot gather a 2/3 majority of the votes cast by core
-developers, the decision is escalated to the :ref:`stage-3`.
-
-For appointments, votes take place in private communication channels
-and are anonymous. The winning conditions vary depending on the role as
-described in :ref:`roles` above. Appointment decisions are not escalated to
-the CC. If a nomination cannot gather sufficient support, the nomination is
-rejected.
-
-.. _stage-3:
-
-Stage 3: conflict resolution
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If the proposed change cannot gather a 2/3 majority of the votes cast,
-the CC tries to resolve the deadlock.
-
--  The CC will use consensus seeking.
--  If no consensus can be found within 21 days since the beginning of the
--  stage-1 "reasonable time for consideration" period,
-   the decision is made through a simple majority vote (with tie breaking) among the CC
-   members.
--  Any proposal reaching stage 3 must be supported by an :ref:`steps`,
-   which has been made public at least 7 days, before the vote.
-
-.. _steps:
-
-aeon enhancement proposal
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-aeon enhancement proposals (STEPs) are required for:
-
-* certain types of proposed changes, by default, see `decision making process <#Decision-making>`__
-* for all stage 3 decisions
-
-If a STEP is required by a vote, it must have been made public at least 7 days before that vote.
-
-A STEP is a consolidated document, with a concise
-problem statement, a clear description of the proposed solution and a
-comparison with alternative solutions, as outlined in our
-`template <https://github.com/aeon/enhancement-proposals/blob/master/TEMPLATE.md>`__.
-
-A complete STEP must always include at least a high-level design for the proposed change,
-not just a wishlist of features.
-
-Usually, we collect and discuss proposals in aeon’s `repository for
-enhancement-proposals <https://github.com/aeon/enhancement-proposals>`__.
-
-For smaller changes, such as punctual changes to the API or governance documents,
-the STEP can also be be part of an issue or pull request.
-
-.. _algorithm-inclusion-guidelines:
-
-Algorithm inclusion guidelines
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Curation is about how we select contributions, which criteria we use in
-order to decide which contributions to include, and in which cases we
-deprecate and remove contributions.
-
-We have the following guidelines:
-
--  ``aeon`` aims to provide a repository for algorithms to enhance reproducible research,
-   putting no lower bounds on number of citations, algorithmic performance, or frequency of use.
--  For inclusion, a scientific reference must be available and linked to the python estimator.
-   A scientific reference is a formal description of the algorithm which
-   satisfies basic scientific requirements, e.g., be formally correct, complete, and
-   adhere with common conventions on presentation in the field of data science.
--  The scientific reference must be free from unfounded scientific claims, pseudo-science,
-   commercial marketing, or other content inappropriate for a scientific reference.
-   The scientific reference must adhere to proper scientific citation standards,
-   i.e., citing primary sources, giving proper credit.
-   The form of the scientific reference can be a description in the class docstring,
-   or a link to a scientific document, e.g., on the arXiv. Such a scientific document
-   need not be peer-reviewed or journal published, but must adhere to scientific standards.
--  We strive to consolidate existing functionality if it helps to improve
-   the usability and maintainability of the project. For example, when
-   there are multiple techniques for the same purpose, we may choose to present one variant as the "primary default",
-   and rarer variants as less accessible or findable alternatives. The choice of the "primary default"
-   may change with use and relevance in the user community.
-   We are aware that the choice of the "primary default" may give or remove visibility,
-   and aim to make the choice for usability and quality of the selection.
--  We are happy to accept historical algorithms of interest, as references to use in
-   reproduction studies, including historical versions that are faulty implementations.
-   Algorithms of historical interest will be clearly labelled as such, and inclusion
-   is primarily guided by relevance, e.g., as a reference in an important study,
-   relevance in the scientific discourse, or as an important algorithmic baseline.
--  From the algorithms or techniques that meet the above criteria, only
-   those which fit well within the current framework of aeon are accepted.
-   For algorithms that do not fit well into one of the current API definitions, the API
-   will have to be extended first. For extending current API, see the
-   `decision making process <#Decision-making>`__ for major changes.
-
-Note that your implementation need not be in aeon to be used together
-with aeon tools. You can implement your favorite algorithm in a aeon
-compatible way in one of `our companion
-repositories <https://github.com/aeon>`__ on GitHub. We will be happy
-to list it under `related
-software <https://github.com/aeon-toolkit/aeon/wiki/related-software>`__.
-
-If algorithms require major dependencies, we encourage to create a
-separate companion repository. For smaller
-dependencies which are limited to a few files, we encourage to use soft
-dependencies, which are only required for particular modules, but not
-for most of aeon’s functionality and not for installing aeon.
-
-.. _acknowledging-contributions:
-
-Acknowledging contributions
----------------------------
-
-aeon is collaboratively developed by its diverse community of
-developers, users, educators, and other stakeholders. We value all kinds
-of contributions and are committed to recognising each of them fairly.
-
-We follow the `all-contributors <https://allcontributors.org>`__
-specification to recognise all contributors, including those that don’t
-contribute code. Please see `our list of all
-contributors <https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTORS.md>`__.
-
-If you think, we’ve missed anything, please let us know or open a PR
-with the appropriate changes to
-`aeon/.all-contributorsrc <https://github.com/aeon-toolkit/aeon/blob/main/.all-contributorsrc>`__.
-
-Note that contributors do not own their contributions. aeon is an
-open-source project, and all code is contributed under `our open-source
-license <https://github.com/aeon-toolkit/aeon/blob/main/LICENSE>`__.
-All contributors acknowledge that they have all the rights to the code
-they contribute to make it available under this license.
-
-The project belongs to the aeon community, and all parts of it are
-always considered “work in progress” so that they can evolve over time
-with newer contributions.
-
-.. _outlook:
-
-Outlook
--------
-
-We are open to improvement suggestions for our governance model. Once
-the community grows more and aeon’s code base becomes more
-consolidated, we will consider the following changes:
-
--  Allow for more time to discuss changes, and more time to cast vote
-   when no consensus can be found,
--  Require more positive votes (less lazy consensus) to accept changes
-   during consensus seeking stage,
--  Reduce time for maintainers to reply to issues
-
-In addition, we plan to add more roles for managing/coordinating
-specific project:
-
-* Community manager (mentorship, outreach, social media, etc),
-* Sub-councils for project-specific technical leadership (e.g.  for documentation, learning tasks, continuous integration)
-
-.. _references:
-
-References
-----------
-
-Our governance model is inspired by various existing governance
-structures. In particular, we’d like to acknowledge:
-
-* scikit-learn’s `governance model <https://www.aeon-toolkit.org/en/latest/governance.html>`__
-* `The Turing Way <https://github.com/alan-turing-institute/the-turing-way>`__ project
-* `The Art of Community <https://www.jonobacon.com/books/artofcommunity/>`__ by Jono Bacon
-* The `astropy <https://www.astropy.org>`__ project
-* The `nipy <https://nipy.org>`__ project
-* The `scikit-hep <https://scikit-hep.org>`__ project
diff --git a/docs/governance.md b/docs/governance.md
new file mode 100644
index 00000000..fafee7cf
--- /dev/null
+++ b/docs/governance.md
@@ -0,0 +1,165 @@
+# Governance
+
+The purpose of this document is to formalize the governance process used by the `aeon`
+project, to clarify how decisions are made and how the various elements of our community
+interact. Our goal is to ensure a transparent, democratic, and inclusive decision-making
+process that empowers all community members to contribute to the project.
+
+`aeon` is a community-owned and community-run project. Anyone with an interest in the
+project can join the community, contribute to the project design and participate in the
+decision-making process.
+
+This document establishes the decision-making structure used by `aeon` which strives to
+find consensus from the community, while avoiding any deadlocks. Our decision-making
+process involves different roles, each with specific responsibilities.
+
+## Roles
+
+### Contributors
+
+Contributors are community members who have contributed in concrete ways to the project.
+Anyone can become a contributor, and contributions can take many forms – not only code –
+as detailed in the contributing guide. Contributors play a crucial role in shaping the
+project through participating in discussions and influencing the decision-making
+process.
+
+### Core Developers
+
+Core developers are community members that have made significant contributions and are
+trusted to continue the development of the project and engage with its community.
+Core developers are granted write access to the `aeon` repository and voting rights
+on all project decisions. They are expected to review code contributions
+and engage with topics or code they are knowledgeable about.
+
+New core developers are nominated by existing core developers and are subject to a
+two-thirds majority vote of existing core developers. Core developers are expected to
+maintain a reasonable amount of engagement with the project. Developing code,
+interacting with contributions and engaging with the broader community are all valid
+contributions for core developers.
+
+Core developers who no longer want or are able to engage with the project are expected
+to resign. Core developers may lose their role after a year of inactivity with no
+contributions (see above) and engagement with fellow developers. If this lack of
+engagement has been demonstrated, another core developer can suggest removal and create
+a pull request on the `aeon` repository. The removal will complete if this pull request
+is successfully merged.
+
+### Workgroups
+
+Extra responsibilities are delegated to workgroups, which are groups of contributors
+with a core developer lead. Workgroups are responsible for specific areas of the project
+and are expected to manage and make decisions in their area of responsibility.
+Workgroups are expected to be transparent about their activities and decisions, and to
+engage with the community where possible.
+
+Membership of workgroups and the leadership position of a workgroup is subject to a
+two-thirds majority vote of core developers. Workgroup members unable to fulfill their
+responsibilities are expected to resign from the workgroup. It is the responsibility of
+the workgroup lead to safeguard access to relevant project resources and accounts.
+
+#### Infrastructure Workgroup
+
+The infrastructure workgroup maintains the infrastructure of `aeon` to ensure the
+smooth running of the project. This includes ensuring the website remains online, that
+CI remains functional and other related tasks.
+
+The infrastructure workgroup maintains ownership of the `aeon` GitHub organisation and
+ReadTheDocs account.
+
+#### Release Management Workgroup
+
+The release management workgroup manages `aeon` releases. This includes deciding on
+release schedules, managing release candidates and publishing releases.
+
+The release management workgroup maintains ownership of the `aeon` PyPi project and
+conda feedstock.
+
+#### Finance Workgroup
+
+The finance workgroup is responsible for managing the project finances. This includes
+approving any project expenses and managing any finance related accounts.
+
+#### Communications Workgroup
+
+The role of the communications workgroups is to interact with the broader community
+through the `aeon` social network accounts and discussion forums. It is the
+responsibility of the communications workgroup to manage and maintain the `aeon`
+Twitter, LinkedIn, Slack and other relevant communications accounts.
+
+The communications team maintains access to social networking accounts and is
+responsible for managing access to the `aeon` email address. To help manage
+GitHub discussions, the communication workgroup is given triage access to the `aeon`
+GitHub repository.
+
+#### Code of Conduct Workgroup
+
+The Code of Conduct Workgroup (CoCW) consists of contributors tasked with making sure
+`aeon` remains a welcoming and inclusive community. CoCW responsibilities include
+maintaining the `aeon` Code of Conduct (CoC) and managing reports of breaking the CoC.
+CoCW members are expected to review reports of CoC violations, contact and discuss with
+involved individuals and make recommendations on actions to take.
+
+Any CoCW members involved in a CoC report or CoCW members which have a conflict of
+interest regarding the report are expected to recuse themselves from the process. The
+CoCW is given triage access to the `aeon` GitHub repository to moderate discussions if
+necessary.
+
+## Decision-Making Process
+
+Decisions about the future of the project are announced publicly to allow discussion
+with all members of the community. The whole process from proposal to implementation
+is fully visible, apart from topics considered sensitive. All non-sensitive project
+management discussion takes place on the contributors channel on the project
+Slack and/or the issue tracker. Occasionally, sensitive discussion and votes such as
+appointments will occur in private Slack channels.
+
+For most decisions a consensus seeking process of all interested contributors is used.
+Contributors try to find a resolution that has no open objections among core developers.
+If a reasonable amount of time (at least 7-days for non-trivial changes) has passed
+since the last change to a proposed contribution, the proposal has at least one approval
+(+1) and no rejections (-1) from core developers, it can be approved by lazy consensus.
+If a change is rejected, it is expected that an explanation and description of
+conditions (if any) to withdraw the rejection is provided.
+
+At any point during the discussion, any core developer in favour of a change can call
+for a vote, which will conclude two weeks from the call for the vote. Any vote to
+bypass a rejection from a core developer must be backed by an AEP (see the following
+section). For major contributions (such as a new module or major framework redesigns)
+an AEP may be requested without a rejection or vote. In the event a vote is called,
+the proposal must receive a two-thirds majority of core developers to be approved.
+
+All changes to the `aeon` code or documentation should be done via Pull Request.
+By default, push rights to the `main` GitHub branch are restricted for all core developers.
+In emergencies, the infrastructure workgroup may temporarily revoke the branch
+protection for group members and make direct commits, but this should happen only in
+emergencies where harm will come to the project unless timely action is taken.
+
+### Enhancement Proposals
+
+For contentious decision-making votes (not including appointment votes), a proposal
+must have been made public for discussion before the vote. It is recommended that this
+proposal is made as a consolidated document, in the form of an “aeon Enhancement
+Proposal” (AEP). The AEP template is available
+[here](https://github.com/aeon-toolkit/aeon-admin/blob/main/aep/aep_template.md), but
+the use of said template is not a requirement. A detailed issue or pull request can
+substitute an AEP if all parties believe it is sufficient, but a more formal proposal
+can be requested by any core developer.
+
+Having a rejection on a pull request does not necessitate the creation of an AEP and
+further discussion to find consensus can be held, but one must be created prior to any
+vote to bypass a rejection.
+
+A vote does not have to be called for an AEP to be submitted. If a contributor wants to
+make a more formal proposal or believes a change will be controversial, an AEP can be
+submitted to the community for discussion and comment.
+
+## Acknowledgements
+
+Substantial portions of this document were adapted from or inspired by the following
+projects governance documents:
+
+- [Scikit-learn](https://scikit-learn.org/stable/governance.html)
+- [SciPy](https://docs.scipy.org/doc/scipy/dev/governance.html)
+- [NumPy](https://numpy.org/doc/stable/dev/governance/governance.html#governance)
+- [Jupyter](https://jupyter.org/governance/overview.html)
+- [Pandas](https://pandas.pydata.org/about/governance.html)
diff --git a/docs/index.md b/docs/index.md
index 3ddd32a0..9ae06b15 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -249,6 +249,8 @@ developers
 
 contributors.md
 about.md
+governance.md
+code_of_conduct.md
 ```
 
 ```{toctree}
diff --git a/docs/make.bat b/docs/make.bat
index 4d9eb83d..27b775e3 100644
--- a/docs/make.bat
+++ b/docs/make.bat
@@ -7,10 +7,8 @@ REM Command file for Sphinx documentation
 if "%SPHINXBUILD%" == "" (
 	set SPHINXBUILD=sphinx-build
 )
-set SOURCEDIR=source
-set BUILDDIR=build
-
-if "%1" == "" goto help
+set SOURCEDIR=.
+set BUILDDIR=_build
 
 %SPHINXBUILD% >NUL 2>NUL
 if errorlevel 9009 (
@@ -21,15 +19,17 @@ if errorlevel 9009 (
 	echo.may add the Sphinx directory to PATH.
 	echo.
 	echo.If you don't have Sphinx installed, grab it from
-	echo.http://sphinx-doc.org/
+	echo.https://www.sphinx-doc.org/
 	exit /b 1
 )
 
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -T
 goto end
 
 :help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -T
 
 :end
 popd
diff --git a/examples/annotation/annotation.ipynb b/examples/annotation/annotation.ipynb
index d9b05f8c..5e52213d 100644
--- a/examples/annotation/annotation.ipynb
+++ b/examples/annotation/annotation.ipynb
@@ -20,12 +20,12 @@
     "\n",
     "- [ClaSP (Classification Score Profile) Segmentation](./segmentation_with_clasp.ipynb)\n",
     "\n",
-    "- [Heteregeneous Intrinsic Dimensionality Algorithm (Hidalgo) Segmentation](./hidalgo_segmentation.ipynb)\n",
-    "\n"
+    "- [Heteregeneous Intrinsic Dimensionality Algorithm (Hidalgo) Segmentation](./hidalgo_segmentation.ipynb)\n"
    ],
    "metadata": {
     "collapsed": false
-   }
+   },
+   "id": "aafb58205ef861c9"
   }
  ],
  "metadata": {
@@ -48,5 +48,5 @@
   }
  },
  "nbformat": 4,
- "nbformat_minor": 0
+ "nbformat_minor": 5
 }
diff --git a/pyproject.toml b/pyproject.toml
index d8c22928..a1b6b8c4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -112,18 +112,19 @@ binder = [
     "jupyterlab",
 ]
 docs = [
-    "jupyter",
-    "myst-parser",
-    "nbsphinx>=0.8.12",
-    "numpydoc",
-    "furo",
-    "sphinx_issues>=3.0.1",
-    "sphinx-design>=0.3.0",
+    "sphinx<8.0.0",
+    "sphinx-design",
+    "sphinx-version-warning",
+    "sphinx_issues",
     "sphinx-copybutton",
-    "sphinx>=5.0.0,<6", # sphinx has <6 because of shinx-design
     "sphinx-remove-toctrees",
-    "tabulate",
     "sphinxext-opengraph",
+    "nbsphinx",
+    "numpydoc",
+    "myst-parser",
+    "jupyter",
+    "furo",
+    "tabulate",
 ]
 
 [project.urls]
diff --git a/sweep.yaml b/sweep.yaml
index 73fcbc64..e22287a5 100644
--- a/sweep.yaml
+++ b/sweep.yaml
@@ -23,4 +23,7 @@ sandbox:
 
 rules:
   - "There should not be large chunks of code that are just commented out. Docstrings and explanations in code are okay though."
-  - "Update the relevant API page in 'docs/api_reference/' when new public functions and classes are added and not included in the API documentation. Only add functions and classes which are not already in the relevant API documentation and avoid duplicate entries."
+  - "Any clearly inefficient or redundant code can be optimized or refactored. Any improvements should not change the functionality of the code."
+  - "All public classes and functions should have a numpydoc style docstring. This should include a description of the class or function, the parameters, the class attributes or function return values, and a usage example for the class or function."
+  - "Update the relevant API page in 'docs/api_reference/' when new public functions and classes are added and not included in the API documentation. For example, if a new function is added to 'aeon/distances/', a `sphinx.ext.autosummary link` should also be added to 'docs/api_reference/distances.rst'. New sections in the page should not be created for individual functions and classes, add it to the most relevant existing one. Only add functions and classes which are not already in the relevant API page and avoid duplicate entries."
+  - "We use unit tests for our code. If we have 'class.py', there can be a 'tests/test_class.py' relative to the directory of the file. It is not necessary to create a test file for each new class and function, but any file specific tests should be in similarly named file."


Test patch:
diff --git a/.github/workflows/periodic_tests.yml b/.github/workflows/periodic_tests.yml
index 1ea730c7..d615ca79 100644
--- a/.github/workflows/periodic_tests.yml
+++ b/.github/workflows/periodic_tests.yml
@@ -10,6 +10,38 @@ concurrency:
   cancel-in-progress: true
 
 jobs:
+  check-manifest:
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.10"
+
+      - uses: pre-commit/action@v3.0.0
+        with:
+          extra_args: check-manifest --hook-stage manual
+
+  pre-commit:
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.10"
+
+      - uses: pre-commit/action@v3.0.0
+        with:
+          extra_args: --all-files
+
+      - name: Check for missing init files
+        run: build_tools/fail_on_missing_init_files.sh
+        shell: bash
+
   run-notebook-examples:
     runs-on: ubuntu-22.04
 
diff --git a/.github/workflows/pr_pytest.yml b/.github/workflows/pr_pytest.yml
index 96f150e6..41836f62 100644
--- a/.github/workflows/pr_pytest.yml
+++ b/.github/workflows/pr_pytest.yml
@@ -88,7 +88,7 @@ jobs:
 
   codecov:
     # run the code coverage job if a PR has the 'codecov actions' label
-    if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'codecov actions')
+    if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'codecov actions') }}
 
     needs: test-nosoftdeps
     runs-on: ubuntu-22.04
diff --git a/aeon/transformations/collection/tests/test_base.py b/aeon/transformations/collection/tests/test_base.py
index 6573798a..162db555 100644
--- a/aeon/transformations/collection/tests/test_base.py
+++ b/aeon/transformations/collection/tests/test_base.py
@@ -38,11 +38,7 @@ def test_collection_transformer_valid_input(data_gen):
 @pytest.mark.parametrize("dtype", ["pd.Series"])
 def test_collection_transformer_invalid_input(dtype):
     """Test that BaseCollectionTransformer fails with series input."""
-    y = convert_to(
-        _make_series(),
-        to_type=dtype,
-    )
-
+    y = (_make_series(),)
     t = _Dummy()
     with pytest.raises(TypeError):
         t.fit_transform(y)
diff --git a/aeon/transformations/collection/tests/test_segment.py b/aeon/transformations/collection/tests/test_segment.py
index 071e7cc0..4c4f4771 100644
--- a/aeon/transformations/collection/tests/test_segment.py
+++ b/aeon/transformations/collection/tests/test_segment.py
@@ -9,7 +9,6 @@ from aeon.transformations.collection.segment import (
     _rand_intervals_fixed_n,
     _rand_intervals_rand_n,
 )
-from aeon.utils._testing.collection import _make_nested_from_array
 
 N_ITER = 10
 
@@ -50,7 +49,7 @@ def test_output_format_dim(n_timepoints, n_instances, n_intervals):
 @pytest.mark.parametrize("bad_interval", [0, -0, "str", 1.2, -1.2, -1])
 def test_bad_input_args(bad_interval):
     """Check that exception is raised for bad input args."""
-    X = _make_nested_from_array(np.ones(10), n_instances=10, n_columns=2)
+    X = np.random.random(size=(10, 1, 20))
     with pytest.raises(ValueError):
         RandomIntervalSegmenter(n_intervals=bad_interval).fit(X)
 
diff --git a/aeon/transformations/collection/tests/test_timebinner.py b/aeon/transformations/collection/tests/test_timebinner.py
index c1dba6b2..328beea0 100644
--- a/aeon/transformations/collection/tests/test_timebinner.py
+++ b/aeon/transformations/collection/tests/test_timebinner.py
@@ -18,7 +18,7 @@ def test_timebinner():
     tb.fit(X)
     row = 1
     Xtb = tb.transform(X)
-    assert np.isclose(np.sum(X.iloc[row, 0][freq : (2 * freq)]), Xtb.iloc[row, 1])
+    assert np.isclose(np.sum(X.iloc[row, 0][freq : (2 * freq)]), Xtb[row, 1])
 
 
 def test_timebinner2():
@@ -32,9 +32,7 @@ def test_timebinner2():
     tb.fit(X)
     row = 3
     Xtb = tb.transform(X)
-    assert np.isclose(
-        np.max(X.iloc[row, 5][8 * 10 + 1 : 9 * 10 + 1]), Xtb.iloc[row, 58]
-    )
+    assert np.isclose(np.max(X.iloc[row, 5][8 * 10 + 1 : 9 * 10 + 1]), Xtb[row, 58])
 
 
 def test_timebinner3():
@@ -53,5 +51,5 @@ def test_timebinner3():
     Xtb = tb.transform(X)
     assert np.isclose(
         np.quantile(X.iloc[row, 0][col * freq + 1 : ((col + 1) * freq) + 1], q=0.25),
-        Xtb.iloc[row, col],
+        Xtb[row, col],
     )