Repository: OpenAssetIO/OpenAssetIO (language: python)

Issue:
Check TODO and needsref comments before release
## What

Check through the various `TODO`, `@todo` and `@needsref` comments littered about the codebase and raise them as github issues if they are important and delete the comment.

## Why

There are various such comments and some of them may be worth addressing before v1 is considered ready.

## Acceptance Criteria

There shouldn't be any  `TODO`, `@todo` and `@needsref` comments left in the codebase
Check TODO and needsref comments before release
## What

Check through the various `TODO`, `@todo` and `@needsref` comments littered about the codebase and raise them as github issues if they are important and delete the comment.

## Why

There are various such comments and some of them may be worth addressing before v1 is considered ready.

## Acceptance Criteria

There shouldn't be any  `TODO`, `@todo` and `@needsref` comments left in the codebase
Check TODO and needsref comments before release
## What

Check through the various `TODO`, `@todo` and `@needsref` comments littered about the codebase and raise them as github issues if they are important and delete the comment.

## Why

There are various such comments and some of them may be worth addressing before v1 is considered ready.

## Acceptance Criteria

There shouldn't be any  `TODO`, `@todo` and `@needsref` comments left in the codebase
Migrate examples that reference MC Traits to MC repo
## What

Migrate docs examples that use specific traits (see `Examples.dox`) to the MediaCreation repository such that they use actual traits from the package and work as real-world code.

## Why

The examples in the OpenAssetIO docs use contrived/hypothetical traits that mean they're not actually executable or representative of the current state of the MC traits and specifications.

## Acceptance Criteria

- Examples of simple MC workflows that are valid, runnable code.
- Examples in OpenAssetIO docs cleaned up to only contain valid code


Discussion / hints:

Fix patch:
diff --git a/doc/doxygen/src/EntitiesTraitsSpecifications.dox b/doc/doxygen/src/EntitiesTraitsSpecifications.dox
index 8916b369c..5b62bc322 100644
--- a/doc/doxygen/src/EntitiesTraitsSpecifications.dox
+++ b/doc/doxygen/src/EntitiesTraitsSpecifications.dox
@@ -330,11 +330,12 @@
  * Traits that are not relevant, missing all data, or not known to the
  * manager should be omitted.
  *
- * When populating @needsref entityTraits, any traits that are
- * applicable, or the manager is capable of providing data for should be
- * set. The result of this query will be used by a host to decide if it
- * is capable of consuming the entity's data in a specific way, so
- * including all applicable traits is generally benefical.
+ * When populating @fqref{managerApi.ManagerInterface.entityTraits}
+ * "entityTraits", any traits that are applicable, or the manager is
+ * capable of providing data for should be set. The result of this query
+ * will be used by a host to decide if it is capable of consuming the
+ * entity's data in a specific way, so including all applicable traits
+ * is generally benefical.
  *
  * @note This may need a little time to settle in practice. There has
  * been discussion around whether there is need to define 'excluded'
diff --git a/doc/doxygen/src/Examples.dox b/doc/doxygen/src/Examples.dox
index 2e783c9a2..ee41c5431 100644
--- a/doc/doxygen/src/Examples.dox
+++ b/doc/doxygen/src/Examples.dox
@@ -435,8 +435,8 @@
  *
  * @section example_generating_a_thumbnail Generating a Thumbnail During Publish
  *
- * This example demonstrates the correct handling in a @ref host of a hypothetical
- * @needsref WantsThumbnail trait if set by a @ref manager in its
+ * This example demonstrates the correct handling in a @ref host of a
+ * hypothetical WantsThumbnail trait if set by a @ref manager in its
  * @fqref{hostApi.Manager.managementPolicy} "managementPolicy" response.
  *
  * It follows on from the preceding publishing example.
diff --git a/doc/doxygen/src/ForHosts.dox b/doc/doxygen/src/ForHosts.dox
index 18aeb8ba9..9b21feca3 100644
--- a/doc/doxygen/src/ForHosts.dox
+++ b/doc/doxygen/src/ForHosts.dox
@@ -196,8 +196,8 @@
  *   calls to the API.
  *
  * - If a manager's @fqref{hostApi.Manager.managementPolicy}
- *   "managementPolicy" sets the @needsref WantsThumbnail trait, where
- *   possible, generate thumbnails as requested during publishing.
+ *   "managementPolicy" sets the WantsThumbnail trait, where possible,
+ *   generate thumbnails as requested during publishing.
  *
  * - Create @ref Specification "specifications" for any custom 'asset
  *   types' you may deal with.
diff --git a/doc/doxygen/src/ForManagers.dox b/doc/doxygen/src/ForManagers.dox
index 4fca09776..d48bb2412 100644
--- a/doc/doxygen/src/ForManagers.dox
+++ b/doc/doxygen/src/ForManagers.dox
@@ -51,9 +51,9 @@
  *   data from the relevant query methods when supplied the reference
  *   returned from the registration.
  *
- * - A @ref host may query other well-known or host-specific @needsref
- *   traits to provide additional customization of behavior or the
- *   handling of data referenced by an @ref entity_reference. For
+ * - A @ref host may query other well-known or host-specific @ref
+ *   trait "traits" to provide additional customization of behavior or
+ *   the handling of data referenced by an @ref entity_reference. For
  *   example, determining the correct frame range and colorspace of an
  *   image sequence.
  *
diff --git a/doc/doxygen/src/Glossary.dox b/doc/doxygen/src/Glossary.dox
index c1f076a53..3b3d6fe71 100644
--- a/doc/doxygen/src/Glossary.dox
+++ b/doc/doxygen/src/Glossary.dox
@@ -664,4 +664,21 @@
  * @see @fqref{hostApi.Manager.entityTraits} "Manager.entityTraits" /
  * @fqref{managerApi.ManagerInterface.entityTraits}
  * "ManagerInterface.entityTraits"
+ *
+ * @section trait_views Trait View classes
+ *
+ * The medium of exchange of @ref trait "traits" is primarily the
+ * @fqref{trait.TraitsData} "TraitsData" class. This is a low-level
+ * dictionary-like structure. Typcially, the low-level interface
+ * provided by `TraitsData` will not be used directly. Instead, concrete
+ * view classes will be used, which wrap the `TraitsData`, and provide
+ * typed accessor and mutator methods. For example,
+ *
+ * ```python
+ * url = LocatableContentTrait(traits_data).getLocation()
+ * ```
+ *
+ * See the [TraitGen project](https://github.com/OpenAssetIO/OpenAssetIO-TraitGen)
+ * and its usage in the [MediaCreation trait library](https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation)
+ * for more details.
  */
diff --git a/doc/doxygen/src/Thumbnails.dox b/doc/doxygen/src/Thumbnails.dox
deleted file mode 100644
index c0357fb4a..000000000
--- a/doc/doxygen/src/Thumbnails.dox
+++ /dev/null
@@ -1,60 +0,0 @@
-/*!
- * @page thumbnails Thumbnails
- *
- * @section thumbnails_overview Overview
- *
- * OpenAssetIO provides a mechanism for a @ref manager to request a
- * thumbnail from the @ref host when a new @ref entity is created, and
- * for a host to look up a thumbnail for an existing entity.
- *
- * @section thumbnails_creation_during_publish Requesting Thumbnail Creation
- * During Publishing
- *
- * In many situations, it is difficult for a @ref manager to
- * retrospectively generate a thumbnail for any given @ref entity. It
- * usually requires first-class knowledge of how the specific entity
- * type can be rendered to into an image.
- *
- * For image-based entities, this is potentially straightforward. The
- * whole purpose of OpenAssetIO though is to allow generic handling of
- * new entity types without specific logic within the manager.
- *
- * It seems fair then, to ask the @ref host to provide a thumbnail with
- * the entity when it is @ref publish "published". More often than not,
- * the host not only understands how this might be done, but has the
- * relevant data to hand to do it.
- *
- * To this end, a @ref manager can set the @needsref WantsThumbnail
- * trait in it's response to @fqref{managerApi.ManagerInterface.managementPolicy}
- * "managementPolicy" for any given set of entity @ref trait "traits".
- *
- * If supported by the host, it will then:
- * - Call @ref glossary_preflight with the target entity and the traits
- *   data from the @needsref ThumbnailSpecification, using the
- *   @fqref{access.PublishAccess.kCreateRelated} "kCreateRelated" access
- *   mode.
- * - Call @ref glossary_resolve on the returned reference to determine
- *   the desired width/height/format for the thumbnail.
- * - Generate a thumbnail and @ref glossary_register it to the same
- *   reference.
- *
- * @note The thumbnail generation process may be carried out
- * asynchronously by the host and registration may occur at an arbitrary
- * time in the future.
- *
- * @see @ref example_generating_a_thumbnail
- *
- * @todo Document the @ref trait "traits" used to determine
- * width/height/format, these should be the same as the fields of the
- * ThumbnailSpecification.
- *
- * @section thumbnails_lookup Looking Up Existing Thumbnails
- *
- * A host can use the standard @fqref{hostApi.Manager.getWithRelationship}
- * "getWithRelationship" method of the @ref manager to request the
- * @ref entity_reference "entity references" of any existing thumbnails
- * that depict a particular @ref entity.
- *
- * The call should supply a @needsref ThumbnailSpecification,
- * configured with the desired size/format of the thumbnail.
- */
diff --git a/src/openassetio-core-c/include/openassetio/c/managerApi/HostSession.h b/src/openassetio-core-c/include/openassetio/c/managerApi/HostSession.h
index 965f52f83..96406edb5 100644
--- a/src/openassetio-core-c/include/openassetio/c/managerApi/HostSession.h
+++ b/src/openassetio-core-c/include/openassetio/c/managerApi/HostSession.h
@@ -29,8 +29,6 @@ extern "C" {
 #define oa_managerApi_SharedHostSession_t OPENASSETIO_NS(managerApi_SharedHostSession_t)
 #define oa_managerApi_SharedHostSession_h OPENASSETIO_NS(managerApi_SharedHostSession_h)
 
-/// @todo HostSession method bindings
-
 /// @}
 // oa_managerApi_SharedHostSession_aliases
 
diff --git a/src/openassetio-core-c/src/errors.hpp b/src/openassetio-core-c/src/errors.hpp
index 37082d9bb..228e0b250 100644
--- a/src/openassetio-core-c/src/errors.hpp
+++ b/src/openassetio-core-c/src/errors.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2013-2022 The Foundry Visionmongers Ltd
+// Copyright 2013-2024 The Foundry Visionmongers Ltd
 #pragma once
 
 #include <openassetio/c/StringView.h>
@@ -16,9 +16,6 @@ namespace errors {
 /**
  * Throw the appropriate exception for given error code, if any.
  *
- * @todo Establish a mapping of code to exception type - currently
- *  everything is the OpenAssetIO base exception.
- *
  * @param code Error code.
  * @param msg Error message to bundle in exception.
  */
diff --git a/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp b/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp
index 472cce31d..f858261ea 100644
--- a/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp
+++ b/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp
@@ -54,37 +54,31 @@ class OPENASSETIO_CORE_C_EXPORT CManagerInterfaceAdapter : ManagerInterface {
   [[nodiscard]] bool hasCapability(Capability capability) override;
 
   /// Wrap the C suite's `initialize` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   void initialize(InfoDictionary managerSettings, const HostSessionPtr& hostSession) override;
 
   /// Wrap the C suite's `managementPolicy` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   [[nodiscard]] trait::TraitsDatas managementPolicy(const trait::TraitSets& traitSets,
                                                     access::PolicyAccess policyAccess,
                                                     const ContextConstPtr& context,
                                                     const HostSessionPtr& hostSession) override;
 
   /// Wrap the C suite's `isEntityReferenceString` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   [[nodiscard]] bool isEntityReferenceString(const Str& someString,
                                              const HostSessionPtr& hostSession) override;
 
   /// Wrap the C suite's `entityExists` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   void entityExists(const EntityReferences& entityReferences, const ContextConstPtr& context,
                     const HostSessionPtr& hostSession,
                     const ExistsSuccessCallback& successCallback,
                     const BatchElementErrorCallback& errorCallback) override;
 
   /// Wrap the C suite's `resolve` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   void resolve(const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
                access::ResolveAccess resolveAccess, const ContextConstPtr& context,
                const HostSessionPtr& hostSession, const ResolveSuccessCallback& successCallback,
                const BatchElementErrorCallback& errorCallback) override;
 
   /// Wrap the C suite's `preflight` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   void preflight(const EntityReferences& entityReferences, const trait::TraitsDatas& traitsDatas,
                  access::PublishingAccess publishingAccess, const ContextConstPtr& context,
                  const HostSessionPtr& hostSession,
@@ -92,7 +86,6 @@ class OPENASSETIO_CORE_C_EXPORT CManagerInterfaceAdapter : ManagerInterface {
                  const BatchElementErrorCallback& errorCallback) override;
 
   /// Wrap the C suite's `register` function.
-  /// @todo Implement C API. Currently throws `runtime_error`.
   void register_(const EntityReferences& entityReferences, const trait::TraitsDatas& traitsDatas,
                  access::PublishingAccess publishingAccess, const ContextConstPtr& context,
                  const HostSessionPtr& hostSession, const RegisterSuccessCallback& successCallback,
diff --git a/src/openassetio-core/include/openassetio/hostApi/HostInterface.hpp b/src/openassetio-core/include/openassetio/hostApi/HostInterface.hpp
index 757d64adf..70ded7ddb 100644
--- a/src/openassetio-core/include/openassetio/hostApi/HostInterface.hpp
+++ b/src/openassetio-core/include/openassetio/hostApi/HostInterface.hpp
@@ -100,8 +100,6 @@ class OPENASSETIO_CORE_EXPORT HostInterface {
    * { 'version' : '1.1v3' }
    *
    * @return Arbitrary manager-specific info dictionary.
-   *
-   * @todo Definitions for well-known keys such as 'version' etc.
    */
   [[nodiscard]] virtual InfoDictionary info();
   /// @}
diff --git a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
index b801460bb..75dae5d03 100644
--- a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
+++ b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2013-2023 The Foundry Visionmongers Ltd
+// Copyright 2013-2024 The Foundry Visionmongers Ltd
 #pragma once
 
 #include <functional>
@@ -281,8 +281,6 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
 
   /**
-   * @todo Document settings mechanism
-   *
    * @return Any settings relevant to the function of the manager with
    * their current values (or their defaults if @ref initialize has
    * not yet been called).
@@ -580,9 +578,6 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * Capability.kStatefulContexts.
    *
    * @see @ref Capability.kStatefulContexts
-   *
-   * @todo Should we concatenate the manager id in
-   * persistenceTokenForContext so we can verify that they match?
    */
   ContextPtr contextFromPersistenceToken(const Str& token);
 
@@ -714,8 +709,6 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * @throw errors::InputValidationException If the given string is not
    * recognized as an entity reference by this manager.
-   *
-   * @todo Use a custom exception type rather than std::domain_error.
    */
   [[nodiscard]] EntityReference createEntityReference(Str entityReferenceString);
 
@@ -2406,11 +2399,6 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * language and terminology used in your presentation of the asset
    * management system with other integrations of the system.
    *
-   * *3 - Thumbnails*
-   *
-   * The API provides a mechanism for a manager to request a thumbnail
-   * for an entity as it is being published, see: @ref thumbnails.
-   *
    * @{
    */
 
diff --git a/src/openassetio-core/include/openassetio/managerApi/Host.hpp b/src/openassetio-core/include/openassetio/managerApi/Host.hpp
index 6a20a0a3a..012773a03 100644
--- a/src/openassetio-core/include/openassetio/managerApi/Host.hpp
+++ b/src/openassetio-core/include/openassetio/managerApi/Host.hpp
@@ -31,8 +31,6 @@ OPENASSETIO_DECLARE_PTR(Host)
  * provided to all manager API entry points provides access to the
  * current host through the @fqref{managerApi.HostSession.host}
  * "HostSession.host" method
- *
- * @todo Add auditing functionality.
  */
 class OPENASSETIO_CORE_EXPORT Host final {
  public:
diff --git a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
index 56abfa923..7ba56b578 100644
--- a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
+++ b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2013-2023 The Foundry Visionmongers Ltd
+// Copyright 2013-2024 The Foundry Visionmongers Ltd
 
 #pragma once
 
@@ -150,7 +150,6 @@ OPENASSETIO_DECLARE_PTR(ManagerInterface)
  *    @li @ref info()
  *    @li @ref settings()
  *
- * @todo Finish/Document settings mechanism.
  * @see @ref initialize
  *
  * @note OpenAssetIO makes use of shared pointers to facilitate object
@@ -476,8 +475,6 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    */
 
   /**
-   * @todo Document settings mechanism
-   *
    * @param hostSession The API session.
    *
    * @return Any settings relevant to the function of the manager with
@@ -521,10 +518,6 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    *  @li @ref displayName()
    *  @li @ref info()
    *  @li @ref settings()
-   *
-   * @todo We need a 'teardown' method to, before a manager is
-   * de-activated in a host, to allow any event registrations etc...
-   * to be removed.
    */
   virtual void initialize(InfoDictionary managerSettings, const HostSessionPtr& hostSession);
 
diff --git a/src/openassetio-core/include/openassetio/trait/TraitsData.hpp b/src/openassetio-core/include/openassetio/trait/TraitsData.hpp
index ba73fcbd3..5c30d0b3b 100644
--- a/src/openassetio-core/include/openassetio/trait/TraitsData.hpp
+++ b/src/openassetio-core/include/openassetio/trait/TraitsData.hpp
@@ -58,8 +58,6 @@ OPENASSETIO_DECLARE_PTR(TraitsData)
  * to the consumer (host or manager, depending on the API method) to
  * decide how this should be handled.
  *
- * @todo Add InfoDictionary trait property value type.
- *
  * @see trait::property
  * @see @ref entities_traits_and_specifications
  */
diff --git a/src/openassetio-core/include/openassetio/trait/property.hpp b/src/openassetio-core/include/openassetio/trait/property.hpp
index 905902074..ed991fc91 100644
--- a/src/openassetio-core/include/openassetio/trait/property.hpp
+++ b/src/openassetio-core/include/openassetio/trait/property.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2013-2022 The Foundry Visionmongers Ltd
+// Copyright 2013-2024 The Foundry Visionmongers Ltd
 /**
  * Typedefs for the trait property data stored within specifications.
  */
@@ -31,7 +31,7 @@ namespace property {
  *
  * Keys must be UTF-8 compatible strings for required portability.
  *
- * Note that typically @needsref trait views will be used to access
+ * Note that typically @ref trait_views will be used to access
  * properties in a @ref TraitsData instance via concrete member
  * functions, so it is highly desirable that keys are ASCII to maximise
  * portability when mapping property keys to member function names.
@@ -56,7 +56,7 @@ using KeySet = std::unordered_set<Key>;
  *
  * IDs must be UTF-8 compatible strings for required portability.
  *
- * Note that typically @needsref trait views will be used to access
+ * Note that typically @ref trait_views will be used to access
  * properties within a @ref TraitsData instance, rather than direct
  * property access using a `TraitId`, so it is desirable that trait IDs
  * are ASCII to maximise portability when mapping IDs to class names.
diff --git a/src/openassetio-core/src/log/SeverityFilter.cpp b/src/openassetio-core/src/log/SeverityFilter.cpp
index d3ace0acc..b89824735 100644
--- a/src/openassetio-core/src/log/SeverityFilter.cpp
+++ b/src/openassetio-core/src/log/SeverityFilter.cpp
@@ -14,8 +14,6 @@ SeverityFilterPtr SeverityFilter::make(LoggerInterfacePtr upstreamLogger) {
 
 SeverityFilter::SeverityFilter(LoggerInterfacePtr upstreamLogger)
     : upstreamLogger_(std::move(upstreamLogger)) {
-  /// @todo [#546] Redesign and improve env var handling and associated logging.
-
   // If the env var is set to a suitable int, attempt to extract a valid
   // severity from it, and use as minSeverity_.
   if (const char* envSeverityStr = std::getenv("OPENASSETIO_LOGGING_SEVERITY")) {
diff --git a/src/openassetio-python/bridge/include/openassetio/python/converter.hpp b/src/openassetio-python/bridge/include/openassetio/python/converter.hpp
index 87c0ed75d..6889d6a84 100644
--- a/src/openassetio-python/bridge/include/openassetio/python/converter.hpp
+++ b/src/openassetio-python/bridge/include/openassetio/python/converter.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2023 The Foundry Visionmongers Ltd
+// Copyright 2024 The Foundry Visionmongers Ltd
 #pragma once
 
 #include <Python.h>
@@ -33,17 +33,12 @@ namespace python::converter {
  * @warning A Python environment, with `openassetio` imported, must be
  *          available in order to use this function.
  *
- * @warning Some methods on @ref manager and @ref ManagerInterface are
- * currently implemented in python, pending imminent port to C++. Due to
- * this, these methods will not yet be available for use on a python
- * object returned from this function.
- *
- * @param objectPtr A non-const OpenAssetIO pointer type, (eg, @needsref
- * ManagerPtr). The returned `PyObject` takes shared ownership of this
- * input \p objectPtr, and will keep the C++ instance alive until the
- * `PyObject` is destroyed.
- * This parameter must be the non-const OpenAssetIO pointer type as
- * casting to python erases constness.
+ * @param objectPtr A non-const OpenAssetIO pointer type, (e.g.
+ * @fqref{hostApi.ManagerPtr} "ManagerPtr"). The returned `PyObject`
+ * takes shared ownership of this input \p objectPtr, and will keep the
+ * C++ instance alive until the `PyObject` is destroyed. This parameter
+ * must be the non-const OpenAssetIO pointer type as casting to python
+ * erases constness.
  *
  * @return A `PyObject` pointer to an object of the Python API type
  * associated with the C++ API object provided. The reference count of
diff --git a/src/openassetio-python/cmodule/src/overrideMacros.hpp b/src/openassetio-python/cmodule/src/overrideMacros.hpp
index 8b99b7114..d55754f48 100644
--- a/src/openassetio-python/cmodule/src/overrideMacros.hpp
+++ b/src/openassetio-python/cmodule/src/overrideMacros.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2023 The Foundry Visionmongers Ltd
+// Copyright 2024 The Foundry Visionmongers Ltd
 #pragma once
 #include <pybind11/pybind11.h>
 
@@ -65,8 +65,6 @@
  * The following macro duplicates PYBIND11_OVERRIDE_PURE_NAME and
  * inserts a `gil_scoped_acquire`, in order to work around the problem
  * until it is fixed upstream.
- *
- * @todo Revert GIL workaround once upstream fix is available.
  */
 #define OPENASSETIO_PYBIND11_OVERRIDE_PURE_NAME(ret_type, cname, name, fn, ...)                 \
   do {                                                                                          \


Test patch:
diff --git a/src/openassetio-core-c/tests/StringViewReporting.hpp b/src/openassetio-core-c/tests/StringViewReporting.hpp
index ba9318162..e4982aed8 100644
--- a/src/openassetio-core-c/tests/StringViewReporting.hpp
+++ b/src/openassetio-core-c/tests/StringViewReporting.hpp
@@ -3,11 +3,6 @@
 /**
  * Comparison and stream operators to simplify asserting and reporting
  * of StringView instances during tests.
- *
- * @todo Move these operators out of tests to form part of the public
- * API. We expect such operators to be useful more broadly. This will
- * require some design work, however (e.g. is `capacity` compared when
- * determining StringView equality?).
  */
 #pragma once
 #include <openassetio/c/StringView.h>
diff --git a/src/openassetio-python/tests/conftest.py b/src/openassetio-python/tests/conftest.py
index 82c2faefb..20e70f6ed 100644
--- a/src/openassetio-python/tests/conftest.py
+++ b/src/openassetio-python/tests/conftest.py
@@ -1,5 +1,5 @@
 #
-#   Copyright 2013-2022 The Foundry Visionmongers Ltd
+#   Copyright 2013-2024 The Foundry Visionmongers Ltd
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
 Shared fixtures/code for pytest cases.
 """
 # pylint: disable=missing-function-docstring,redefined-outer-name
-import inspect
 from unittest import mock
 import sys
 
@@ -490,70 +489,3 @@ def next(self, hostSession):
 
     def close(self, hostSession):
         self.mock.close(hostSession)
-
-
-#
-# Python to C++ migration helpers
-#
-
-
-@pytest.fixture
-def method_introspector():
-    return MethodIntrospector()
-
-
-class MethodIntrospector:
-    """
-    Utility function for introspecting methods.
-
-    @todo Remove this class and related tests once C++ migration is
-    complete.
-    """
-
-    @staticmethod
-    def is_defined_in_python(method):
-        """
-        Returns True if the method is defined in Python (as opposed to
-        through a cmodule).
-
-        @param The method of a class to be checked (eg: Manager.info). This
-        should be passed from the Class itself, not an instance.
-        """
-        # The way pybind does its thing™, this returns True for a native
-        # Python implementation, False for a C++ method bound to Python
-        # (isntancemthod not function). Mildly tenuous, but serves a
-        # purpose. getsource and similar raise a TypeError if the supplied
-        # object isn't a function, so the flow control is simpler this way.
-        return inspect.isfunction(method)
-
-    @staticmethod
-    def is_implemented_once(klass, method_name):
-        """
-        Check if a method is only implemented once in the class hierarchy.
-
-        In particular, this is useful to ensure we aren't hiding a (C++)
-        base class method with a (Python) subclass method. I.e. this checks
-        that we haven't migrated a method to C++ then forgot to remove the
-        Python implementation.
-
-        This acts as a backup check in case we migrate a method then forget
-        to update the `is_defined_in_python` assertion.
-
-        @param klass: Class whose base class hierarchy to check.
-
-        @param method_name: Name of method to check.
-
-        @return: True if the method is only defined once in the hierarchy,
-        false otherwise.
-        """
-        # Get class hierarchy, including class itself.
-        class_hierarchy = inspect.getmro(klass)
-        # C++ instance methods are not hashable, so constructing a `set`,
-        # and then checking its length, doesn't work here.
-        maybe_impls = (getattr(base, method_name, None) for base in class_hierarchy)
-        impls = filter(None, maybe_impls)
-        first_impl = next(impls, None)  # Consume first element.
-        if first_impl is None:
-            # Not even implemented once.
-            return False
-        return all(impl == first_impl for impl in impls)
diff --git a/src/openassetio-python/tests/package/hostApi/test_manager.py b/src/openassetio-python/tests/package/hostApi/test_manager.py
index e1ee851a3..e101aeb44 100644
--- a/src/openassetio-python/tests/package/hostApi/test_manager.py
+++ b/src/openassetio-python/tests/package/hostApi/test_manager.py
@@ -47,12 +47,6 @@
 from openassetio.trait import TraitsData
 
 
-## @todo Remove comments regarding Entity methods when splitting them from core API
-
-# __str__ and __repr__ aren't tested as they're debug tricks that need
-# assessing when this is ported to cpp
-
-
 class BatchFirstMethodTest:
     """
     Assertion helpers for testing batch-first methods and their
@@ -618,10 +612,6 @@ def test_when_constructed_with_ManagerInterface_as_None_then_raises_TypeError(
 
 
 class Test_Manager_identifier:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.identifier)
-        assert method_introspector.is_implemented_once(Manager, "identifier")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface
     ):
@@ -650,10 +640,6 @@ def test_when_interface_provides_wrong_type_then_raises_RuntimeError(
 
 
 class Test_Manager_displayName:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.displayName)
-        assert method_introspector.is_implemented_once(Manager, "displayName")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface
     ):
@@ -677,10 +663,6 @@ def test_when_interface_provides_wrong_type_then_raises_RuntimeError(
 
 
 class Test_Manager_info:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.info)
-        assert method_introspector.is_implemented_once(Manager, "info")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface
     ):
@@ -704,10 +686,6 @@ def test_when_interface_provides_wrong_type_then_raises_RuntimeError(
 
 
 class Test_Manager_updateTerminology:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.updateTerminology)
-        assert method_introspector.is_implemented_once(Manager, "updateTerminology")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session
     ):
@@ -730,10 +708,6 @@ def test_input_not_modified(self, manager, mock_manager_interface, a_host_sessio
 
 
 class Test_Manager_settings:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.settings)
-        assert method_introspector.is_implemented_once(Manager, "settings")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session
     ):
@@ -748,10 +722,6 @@ def test_wraps_the_corresponding_method_of_the_held_interface(
 
 
 class Test_Manager_initialize:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.initialize)
-        assert method_introspector.is_implemented_once(Manager, "initialize")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session
     ):
@@ -897,10 +867,6 @@ def test_wraps_the_corresponding_method_of_the_held_interface(
 
 
 class Test_Manager_flushCaches:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.flushCaches)
-        assert method_introspector.is_implemented_once(Manager, "flushCaches")
-
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session
     ):
@@ -910,10 +876,6 @@ def test_wraps_the_corresponding_method_of_the_held_interface(
 
 
 class Test_Manager_isEntityReferenceString:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.isEntityReferenceString)
-        assert method_introspector.is_implemented_once(Manager, "isEntityReferenceString")
-
     @pytest.mark.parametrize("expected", (True, False))
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session, a_ref_string, expected
@@ -951,10 +913,6 @@ def test_when_prefix_given_in_info_then_prefix_used_and_interface_not_called(
 
 
 class Test_Manager_createEntityReference:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.createEntityReference)
-        assert method_introspector.is_implemented_once(Manager, "createEntityReference")
-
     def test_when_invalid_then_raises_InputValidationException(
         self, manager, mock_manager_interface, a_ref_string, a_host_session
     ):
@@ -983,10 +941,6 @@ def test_when_valid_then_returns_configured_EntityReference(
 
 
 class Test_Manager_createEntityReferenceIfValid:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.createEntityReferenceIfValid)
-        assert method_introspector.is_implemented_once(Manager, "createEntityReferenceIfValid")
-
     def test_when_invalid_then_returns_None(
         self, manager, mock_manager_interface, a_ref_string, a_host_session
     ):
@@ -2724,10 +2678,6 @@ def test_when_batched_throwing_overload_errors_then_raises(
 
 
 class Test_Manager_managementPolicy:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.managementPolicy)
-        assert method_introspector.is_implemented_once(Manager, "managementPolicy")
-
     def test_batch_overload_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session, some_entity_trait_sets, a_context
     ):
@@ -3238,10 +3188,6 @@ def test_when_batched_throwing_overload_errors_then_raises(
 
 
 class Test_Manager_createContext:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.createContext)
-        assert method_introspector.is_implemented_once(Manager, "createContext")
-
     def test_context_is_created_with_locale(self, manager, mock_manager_interface, a_host_session):
         mock_manager_interface.mock.hasCapability.return_value = False
 
@@ -3280,10 +3226,6 @@ def test_when_custom_state_supported_then_returned_context_contains_state(
 
 
 class Test_Manager_createChildContext:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.createChildContext)
-        assert method_introspector.is_implemented_once(Manager, "createChildContext")
-
     def test_when_called_with_parent_then_props_copied_and_createState_called_with_parent_state(
         self, manager, mock_manager_interface, a_host_session
     ):
@@ -3336,10 +3278,6 @@ def test_when_called_with_parent_with_no_managerState_then_createChildState_is_n
 
 
 class Test_Manager_persistenceTokenForContext:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.persistenceTokenForContext)
-        assert method_introspector.is_implemented_once(Manager, "persistenceTokenForContext")
-
     def test_when_called_then_the_managers_persistence_token_is_returned(
         self, manager, mock_manager_interface, a_host_session
     ):
@@ -3368,10 +3306,6 @@ def test_when_no_state_then_return_is_empty_and_persistenceTokenForState_is_not_
 
 
 class Test_Manager_contextFromPersistenceToken:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(Manager.contextFromPersistenceToken)
-        assert method_introspector.is_implemented_once(Manager, "contextFromPersistenceToken")
-
     def test_when_called_then_the_managers_restored_state_is_set_in_the_context(
         self, manager, mock_manager_interface, a_host_session
     ):
diff --git a/src/openassetio-python/tests/package/managerApi/test_managerinterface.py b/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
index 54fe75860..f09da5626 100644
--- a/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
+++ b/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
@@ -32,30 +32,18 @@
 
 
 class Test_ManagerInterface_identifier:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.identifier)
-        assert method_introspector.is_implemented_once(ManagerInterface, "identifier")
-
     def test_is_pure_virtual(self, manager_interface, pure_virtual_error_msg):
         with pytest.raises(RuntimeError, match=pure_virtual_error_msg.format("identifier")):
             manager_interface.identifier()
 
 
 class Test_ManagerInterface_displayName:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.displayName)
-        assert method_introspector.is_implemented_once(ManagerInterface, "displayName")
-
     def test_is_pure_virtual(self, manager_interface, pure_virtual_error_msg):
         with pytest.raises(RuntimeError, match=pure_virtual_error_msg.format("displayName")):
             manager_interface.displayName()
 
 
 class Test_ManagerInterface_info:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.info)
-        assert method_introspector.is_implemented_once(ManagerInterface, "info")
-
     def test_when_not_overridden_then_returns_empty_dict(self):
         info = ManagerInterface().info()
 
@@ -133,10 +121,6 @@ def test_names_indices_match_constants(self):
 
 
 class Test_ManagerInterface_hasCapability:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.updateTerminology)
-        assert method_introspector.is_implemented_once(ManagerInterface, "updateTerminology")
-
     def test_is_pure_virtual(self, manager_interface, pure_virtual_error_msg):
         with pytest.raises(RuntimeError, match=pure_virtual_error_msg.format("hasCapability")):
             manager_interface.hasCapability(ManagerInterface.Capability.kManagementPolicyQueries)
@@ -209,19 +193,11 @@ def test_default_implementation_raises_NotImplementedException(
 
 
 class Test_ManagerInterface_flushCaches:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.flushCaches)
-        assert method_introspector.is_implemented_once(ManagerInterface, "flushCaches")
-
     def test_default_implementation_exists(self, a_host_session):
         ManagerInterface().flushCaches(a_host_session)
 
 
 class Test_ManagerInterface_createState:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.createState)
-        assert method_introspector.is_implemented_once(ManagerInterface, "createState")
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_host_session, unimplemented_method_error_msg
     ):
@@ -233,10 +209,6 @@ def test_default_implementation_raises_NotImplementedException(
 
 
 class Test_ManagerInterface_createChildState:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.createChildState)
-        assert method_introspector.is_implemented_once(ManagerInterface, "createChildState")
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_host_session, unimplemented_method_error_msg
     ):
@@ -252,14 +224,6 @@ def test_when_none_is_supplied_then_TypeError_is_raised(self, a_host_session):
 
 
 class Test_ManagerInterface_persistenceTokenForState:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(
-            ManagerInterface.persistenceTokenForState
-        )
-        assert method_introspector.is_implemented_once(
-            ManagerInterface, "persistenceTokenForState"
-        )
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_host_session, unimplemented_method_error_msg
     ):
@@ -277,14 +241,6 @@ def test_when_none_is_supplied_then_TypeError_is_raised(self, a_host_session):
 
 
 class Test_ManagerInterface_stateFromPersistenceToken:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(
-            ManagerInterface.stateFromPersistenceToken
-        )
-        assert method_introspector.is_implemented_once(
-            ManagerInterface, "stateFromPersistenceToken"
-        )
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_host_session, unimplemented_method_error_msg
     ):
@@ -302,12 +258,6 @@ def test_when_none_is_supplied_then_TypeError_is_raised(self, a_host_session):
 
 
 class Test_ManagerInterface_defaultEntityReference:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(
-            ManagerInterface.defaultEntityReference
-        )
-        assert method_introspector.is_implemented_once(ManagerInterface, "defaultEntityReference")
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_context, a_host_session, unimplemented_method_error_msg
     ):
@@ -374,10 +324,6 @@ def fail(*_):
 
 
 class Test_ManagerInterface_getWithRelationship:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.getWithRelationship)
-        assert method_introspector.is_implemented_once(ManagerInterface, "getWithRelationship")
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_context, a_host_session, unimplemented_method_error_msg
     ):
@@ -404,10 +350,6 @@ def fail(*_):
 
 
 class Test_ManagerInterface_getWithRelationships:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(ManagerInterface.getWithRelationships)
-        assert method_introspector.is_implemented_once(ManagerInterface, "getWithRelationships")
-
     def test_default_implementation_raises_NotImplementedException(
         self, manager_interface, a_context, a_host_session, unimplemented_method_error_msg
     ):
@@ -445,12 +387,6 @@ def assert_is_default_pager(a_host_session, pager):
 
 
 class Test_ManagerInterface__createEntityReference:
-    def test_method_defined_in_cpp(self, method_introspector):
-        assert not method_introspector.is_defined_in_python(
-            ManagerInterface._createEntityReference  # pylint:disable=protected-access
-        )
-        assert method_introspector.is_implemented_once(ManagerInterface, "_createEntityReference")
-
     def test_when_input_is_string_then_wrapped_in_entity_reference(self, manager_interface):
         a_string = "some string"
         # pylint: disable=protected-access