Repository: OpenAssetIO/OpenAssetIO (language: python)

Issue:
Hybrid Plugin System
## What

Add a plugin system that combines multiple plugin systemss and dispatches based on advertised capability, or priority order if multiple factories advertise a capability.

## ACs

- Uses a shared identifier to locate the conceptual same manager across each plugin system.
- Make sure this is generic so it composes the output of N plugin systems.
- Uses `hasCapability` to route.
- Picks the first if present in both.

## Implementation notes

- Ideally have a internally `RoutingManagerInterface(vector<ManagerInterfacePtr> interfaces)` as this allows eg: Cpp, GPRC Python, in-process Python.




Discussion / hints:
https://lucascheller.github.io/VFX-UsdAssetResolver/resolvers/production.html
https://forum.aousd.org/t/batching-ar2-queries/818/2?u=foundrytom

Fix patch:
diff --git a/examples/manager/SimpleCppManager/src/SimpleCppManager.cpp b/examples/manager/SimpleCppManager/src/SimpleCppManager.cpp
index bd28a3d39..ddb62507c 100644
--- a/examples/manager/SimpleCppManager/src/SimpleCppManager.cpp
+++ b/examples/manager/SimpleCppManager/src/SimpleCppManager.cpp
@@ -100,6 +100,9 @@ struct SimpleCppManagerInterface final : openassetio::managerApi::ManagerInterfa
     // Support customisable capabilities. Assume a single-row CSV
     // format.
     if (const auto& maybeValue = valueFromSettings(kSettingsKeyForCapabilities)) {
+      // Remove default capabilities - they will all come from settings.
+      capabilities_.clear();
+
       std::istringstream csvRowAsStream{*maybeValue};
       std::string capability;
       // Loop over each listed capability.


Test patch:
diff --git a/examples/manager/SimpleCppManager/tests/test_SimpleCppManager.py b/examples/manager/SimpleCppManager/tests/test_SimpleCppManager.py
index 6264050ca..0e551d712 100644
--- a/examples/manager/SimpleCppManager/tests/test_SimpleCppManager.py
+++ b/examples/manager/SimpleCppManager/tests/test_SimpleCppManager.py
@@ -137,6 +137,18 @@ def test_when_default_capability_then_default_capability_set_available(
         with pytest.raises(errors.NotImplementedException):
             a_fresh_simple_cpp_manager.contextFromPersistenceToken("abc")
 
+    def test_when_default_capability_missing_from_explicit_settings_then_capability_unavailable(
+        self, a_fresh_simple_cpp_manager
+    ):
+        settings = a_fresh_simple_cpp_manager.settings()
+        # Same as default, but without "resolution".
+        settings["capabilities"] = (
+            "entityReferenceIdentification,managementPolicyQueries,entityTraitIntrospection"
+        )
+        a_fresh_simple_cpp_manager.initialize(settings)
+
+        assert not a_fresh_simple_cpp_manager.hasCapability(Manager.Capability.kResolution)
+
     def test_when_capability_is_overridden_then_stub_implementations_available(
         self, a_fresh_simple_cpp_manager, a_context
     ):