Repository: OpenAssetIO/OpenAssetIO (language: python)

Issue:
Move `Access` out of `Context`
## Description

Closes #1054 
- #1054 

- [x] I have updated the release notes.
- [x] I have updated all relevant user documentation.

## Reviewer Notes

Rebased on https://github.com/OpenAssetIO/OpenAssetIO/pull/1058 and implemented `access` argument for `defaultEntityReference`.  This method isn't mentioned in the ACs so may need further discussion.  I struggled to think of a use-case (==docstring) for `kWrite` so left it as `kRead` and `kCreateRelated` only.

Not gone into depth with docs/docstring updates, given that docs cause the most review churn.

BAL is retargetted to a corresponding branch: `foundrytom/work/67-access`.  Once confirmed working and PR accepted, this commit can be dropped and the `integrations.yml` tests disabled to allow this PR to be merged. Subsequent work can then get dependent projects up to date and re-enable the integration tests.





Discussion / hints:

Fix patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 341db32c5..5a784183a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -6,6 +6,14 @@ v1.0.0-alpha.x
 
 ### Breaking changes
 
+> **Warning**
+>
+> - Removed `Context.Access` and instead added per-workflow access mode
+>  enumerations under an `access` namespace. Added these enums as
+>  required arguments to various API functions, replacing the usage of
+>  `Context.Access`.
+>  [#1054](https://github.com/OpenAssetIO/OpenAssetIO/issues/1054)
+
 - Changed signature of `preflight` to accept a `TraitsData` per
   reference, rather than a single trait set. The host is now expected to
   communicate any relevant information that it owns and is known at
diff --git a/doc/doxygen/src/Examples.dox b/doc/doxygen/src/Examples.dox
index cc1c40c22..970c74af7 100644
--- a/doc/doxygen/src/Examples.dox
+++ b/doc/doxygen/src/Examples.dox
@@ -144,7 +144,7 @@
  * `kInfoKey_EntityReferencesMatchPrefix` key.
  *
  * @code{.py}
- * from openassetio import Context
+ * from openassetio.access import ResolveAccess
  * from openassetio_mediacreation.traits.content import LocatableContentTrait
  *
  * # Note: this will raise an exception if given a string that is not
@@ -154,19 +154,18 @@
  * entity_reference = manager.createEntityReference(some_string)
  *
  * # All calls to the manager must have a Context, these should always
- * # be created by the target manager. The Context expresses the host's
- * # intent, and ensure that any manager state is properly managed
- * # between API calls.
+ * # be created by the target manager. The Context ensures that any
+ * # manager state is properly managed between API calls.
  * context = manager.createContext()
  *
- * # We describe what we want to do with the asset
- * context.access = context.access.kRead
- *
  * # We can now resolve a token we may have if it is a reference. In
  * # this example, we'll attempt to resolve the LocatableContent trait
- * # for the entity.
+ * # for the entity. We inform the manager that we just want to read the
+ * # current data for the entity (kRead). See the publishing examples
+ * # for use of the alternative `kWrite` access mode.
  * resolved_asset = manager.resolve(
- *         entity_reference, {LocatableContentTrait.kId}, context)
+ *         entity_reference, {LocatableContentTrait.kId},
+ *         ResolveAccess.kRead, context)
  * url = LocatableContentTrait(resolved_asset).getLocation()  # May be None
  * @endcode
  *
@@ -196,6 +195,8 @@
  *
  * @code{.py}
  *
+ * from openassetio.access import PolicyAccess
+ *
  * # Commented imports are illustrative and may not exist yet
  * from openassetio_mediacreation.traits.managementPolicy import (
  *     ManagedTrait,
@@ -207,13 +208,12 @@
  *     # TextFileSpecification
  * )
  *
- * # Make sure we have the correct context
- * context.access = context.kRead
- *
  * # We use the well-known specification for a text file to determine
  * # the correct trait set to query. Using the standard definition
- * # ensures consistent behaviour across managers/hosts.
- * [policy] = manager.managementPolicy([TextFileSpecification.kTraitSet], context)
+ * # ensures consistent behaviour across managers/hosts. We request
+ * # the manager's policy for read access to this entity type.
+ * [policy] = manager.managementPolicy(
+ *  [TextFileSpecification.kTraitSet], PolicyAccess.kRead, context)
  *
  * # We can now check which traits were imbued in the policy, the
  * # absence of a trait means it is unsupported.
@@ -245,6 +245,8 @@
  * the creation of new data. In this case, a simple text file.
  *
  * @code{.py}
+ * from openassetio.access import (
+ *     PolicyAccess, ResolveAccess, PublishingAccess)
  * # Commented imports are illustrative and may not exist yet
  * from openassetio_mediacreation.traits.managementPolicy import (
  *     ManagedTrait, ResolvesFutureEntitiesTrait
@@ -256,10 +258,12 @@
  *
  * # As ever, an appropriately configured context is required
  * context = manager.createContext()
- * context.access = context.kWrite
  *
- * # The first step is to see if the manager wants to manage text files
- * policy = manager.managementPolicy([TextFileSpecification.kTraitSet], context)[0]
+ * # The first step is to see if the manager wants to manage text files.
+ * # Note that this time we request the manager's policy for write
+ * # access.
+ * policy = manager.managementPolicy(
+ *     [TextFileSpecification.kTraitSet], PolicyAccess.kWrite, context)[0]
  *
  * if not ManagedTrait.isImbuedTo(policy):
  *   # The manager doesn't care about this type of asset
@@ -278,13 +282,18 @@
  * # publishing) and can be provided up-front.
  * file_spec = TextFileSpecification.create()
  * file_spec.markupTrait().setMarkupType("plain")
+ *
+ * # Our intent is to write data to this entity, not to create a new
+ * # related entity (kCreateRelated), so we use the kWrite access mode.
  * # NOTE: It is critical to always use the working_ref from now on.
- * working_ref = manager.preflight(entity_ref, file_spec, context)
+ * working_ref = manager.preflight(
+ *    entity_ref, file_spec, PublishingAccess.kWrite, context)
  *
  * # We then check if the manager can tell us where to save the file.
  * if ResolvesFutureEntitiesTrait.isImbuedTo(policy):
  *     working_data = manager.resolve(
- *             working_ref, TextFileSpecification.kTraitSet, context)
+ *             working_ref, TextFileSpecification.kTraitSet,
+ *             ResolveAccess.kWrite, context)
  *     working_spec = TextFileSpecification(working_data)
  *     if save_url := working_spec.locatableContentTrait().getLocation():
  *         save_path = pathFromURL(save_url)  # URL decode etc
@@ -302,7 +311,8 @@
  *
  * # Now the data has been written, we register the file and the publish
  * # is complete.
- * final_ref = manager.register(working_ref, file_spec.traitsData(), context)
+ * final_ref = manager.register(working_ref, file_spec.traitsData(),
+ *     PublishAccess.kWrite, context)
  *
  * # Keep this around for later, in case it is useful
  * with open(os.path.join(os.path.expanduser('~'), 'history', 'a') as f:
@@ -330,7 +340,8 @@
  * # reference, this gives us a reference we can now use for all
  * # interactions relating to the thumbnail itself.
  * thumbnail_ref = manager.preflight(
- *         final_ref, ThumbnailFileSpecification.kTraitSet, context)
+ *         final_ref, ThumbnailFileSpecification.kTraitSet,
+ *         PublishAccess.kCreateRelated, context)
  *
  * thumbnail_path = os.path.join(os.path.expanduser('~'), 'greeting.preview.png')
  * thumbnail_attr = {"width": 128, "height": 128}
@@ -338,7 +349,8 @@
  * # See if the manager can tell us where to put it, and what it should be like
  * if ResolvesFutureEntitiesTrait.isImbuedTo(policy):
  *     requested = manager.resolve(
- *             thumbnail_ref, ThumbnailFileSpecification.kTraitSet, context)
+ *             thumbnail_ref, ThumbnailFileSpecification.kTraitSet,
+ *             ResolveAccess.kWrite, context)
  *     requested_spec = ThumbnailFileSpecification(requested)
  *     if requested_path := requested_spec.locatableContentTrait().getLocation():
  *         thumbnail_path = pathFromURL(requested_path)
@@ -360,6 +372,8 @@
  * raster_trait.setWidth(thumbnail_attr["width"])
  * raster_trait.setHeight(thumbnail_attr["height"])
  *
- * manager.register(thumbnail_ref, thumbnail_spec.traitsData(), context)
+ * manager.register(
+ *     thumbnail_ref, thumbnail_spec.traitsData(),
+ *     PublishAccess.kWrite, context)
  * @endcode
  */
diff --git a/doc/doxygen/src/Glossary.dox b/doc/doxygen/src/Glossary.dox
index 5a5789b88..8f1ff6f82 100644
--- a/doc/doxygen/src/Glossary.dox
+++ b/doc/doxygen/src/Glossary.dox
@@ -400,10 +400,10 @@
  *
  * In this context, a property is "owned" by the host if it will not be
  * dictated by the manager during the publishing workflow (i.e. by @ref
- * glossary_resolve "resolving" with a @fqref{Context.Access.kWrite}
- * "write" access flag). Note that this definition of host ownership
- * might include properties that have been resolved from the manager at
- * some point in the past.
+ * glossary_resolve "resolving" with a
+ * @fqref{access.ResolveAccess.kWrite} "write" access flag). Note that
+ * this definition of host ownership might include properties that have
+ * been resolved from the manager at some point in the past.
  *
  * If a property is not already known or understood by the host, and is
  * optional, then it may be omitted.
@@ -502,9 +502,9 @@
  * Despite the API having no opinion as to the specific reaction of the
  * manager to registration, there are some occasions, specifically
  * around the creation of new related entities, where additional
- * instruction is required from the host. For this purpose, the use of
- * @fqref{Context.Access.kCreateRelated} "Context.Access.kCreateRelated"
- * in the context is used as a flag to indicate this intent.
+ * instruction is required from the host. For this purpose, use
+ * @fqref{access.PublishAccess.kCreateRelated} "kCreateRelated" as the
+ * access flag.
  *
  * @section glossary_resolve resolve
  *
diff --git a/doc/doxygen/src/ManagerState.dox b/doc/doxygen/src/ManagerState.dox
index 59f3fc2e2..138a6a566 100644
--- a/doc/doxygen/src/ManagerState.dox
+++ b/doc/doxygen/src/ManagerState.dox
@@ -87,7 +87,6 @@
  * activate Manager
  * worker -> Manager : createContext()
  * Manager --> worker : context
- * worker -> worker : context.access = context.kWrite
  * worker -> Manager : working_reference = preflight("ref://asset", context)
  * Manager --> worker : "ref://asset=v4&state=inflight"
  * worker -> Manager : persistenceTokenForContext(context)
@@ -105,7 +104,6 @@
  *     worker -> worker : token, ref_map = read_state_file("openassetio.json")
  *     worker -> Manager : contextFromPersistenceToken(token)
  *     Manager --> worker : context
- *     worker -> worker : context.access = context.kWrite
  *     worker -> worker : working_reference = ref_map["ref://asset"]
  *     worker -> Manager : spec = resolve(working_reference, {FileTrait.id}, context)
  *     Manager --> worker : Specification({"file": {"url": "file:///out.####.exr"}})
@@ -122,7 +120,6 @@
  * worker -> worker : token, ref_map = read_state_file("openassetio.json")
  * worker -> Manager : contextFromPersistenceToken(token)
  * Manager --> worker : context
- * worker -> worker : context.access = context.kWrite
  * worker -> worker : working_reference = ref_map["ref://asset"]
  * worker -> Manager : path = resolve(working_reference, FileTrait.id, context)
  * Manager --> worker : "file://out.####.exr"
diff --git a/doc/doxygen/src/Thumbnails.dox b/doc/doxygen/src/Thumbnails.dox
index edbbb4d85..c0357fb4a 100644
--- a/doc/doxygen/src/Thumbnails.dox
+++ b/doc/doxygen/src/Thumbnails.dox
@@ -31,8 +31,8 @@
  * 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{Context.Access.kCreateRelated} "Access.kCreateRelated"
- *   access mode.
+ *   @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
diff --git a/examples/host/simpleResolver/simpleResolver.py b/examples/host/simpleResolver/simpleResolver.py
index e5849b204..463a97714 100755
--- a/examples/host/simpleResolver/simpleResolver.py
+++ b/examples/host/simpleResolver/simpleResolver.py
@@ -26,6 +26,7 @@
 import sys
 
 from openassetio import BatchElementException
+from openassetio.access import ResolveAccess
 from openassetio.hostApi import HostInterface, ManagerFactory
 from openassetio.log import ConsoleLogger, SeverityFilter
 from openassetio.pluginSystem import PythonPluginSystemManagerImplementationFactory
@@ -193,15 +194,13 @@ def main():
     # interactive session.
 
     context = manager.createContext()
-    context.access = context.Access.kRead
 
     # Resolve the requested traits for the referenced entity.
     # Note that there are multiple overloaded signatures for `resolve`,
     # to aid in batch and/or exception-less workflows. See API docs for
     # more. Here we use the default single-ref exception-throwing
     # signature.
-
-    traits_data = manager.resolve(entity_reference, trait_set, context)
+    traits_data = manager.resolve(entity_reference, trait_set, ResolveAccess.kRead, context)
     print_traits_data(traits_data)
 
 
diff --git a/examples/manager/SampleAssetManager/python/SampleAssetManager/SampleAssetManagerInterface.py b/examples/manager/SampleAssetManager/python/SampleAssetManager/SampleAssetManagerInterface.py
index 3ddad40d2..1645078d8 100644
--- a/examples/manager/SampleAssetManager/python/SampleAssetManager/SampleAssetManagerInterface.py
+++ b/examples/manager/SampleAssetManager/python/SampleAssetManager/SampleAssetManagerInterface.py
@@ -65,6 +65,6 @@ def info(self):
         # to acquire the GIL and enter Python.
         return {constants.kInfoKey_EntityReferencesMatchPrefix: "sam:///"}
 
-    def managementPolicy(self, traitSets, context, hostSession):
+    def managementPolicy(self, traitSets, policyAccess, context, hostSession):
         # pylint: disable=unused-argument
         return [TraitsData() for _ in traitSets]
diff --git a/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.cpp b/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.cpp
index d405237bd..12a2c67e2 100644
--- a/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.cpp
+++ b/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.cpp
@@ -87,6 +87,7 @@ void CManagerInterfaceAdapter::initialize([[maybe_unused]] InfoDictionary manage
 
 trait::TraitsDatas CManagerInterfaceAdapter::managementPolicy(
     [[maybe_unused]] const trait::TraitSets& traitSets,
+    [[maybe_unused]] const access::PolicyAccess policyAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession) const {
   throw std::runtime_error{"Not implemented"};
@@ -110,6 +111,7 @@ void CManagerInterfaceAdapter::entityExists(
 void CManagerInterfaceAdapter::resolve(
     [[maybe_unused]] const EntityReferences& entityReferences,
     [[maybe_unused]] const trait::TraitSet& traitSet,
+    [[maybe_unused]] const access::ResolveAccess resolveAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     [[maybe_unused]] const ManagerInterface::ResolveSuccessCallback& successCallback,
@@ -120,6 +122,7 @@ void CManagerInterfaceAdapter::resolve(
 void CManagerInterfaceAdapter::preflight(
     [[maybe_unused]] const EntityReferences& entityReferences,
     [[maybe_unused]] const trait::TraitsDatas& traitsDatas,
+    [[maybe_unused]] const access::PublishingAccess publishingAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     [[maybe_unused]] const PreflightSuccessCallback& successCallback,
@@ -130,6 +133,7 @@ void CManagerInterfaceAdapter::preflight(
 void CManagerInterfaceAdapter::register_(
     [[maybe_unused]] const EntityReferences& entityReferences,
     [[maybe_unused]] const trait::TraitsDatas& traitsDatas,
+    [[maybe_unused]] const access::PublishingAccess publishingAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     [[maybe_unused]] const RegisterSuccessCallback& successCallback,
diff --git a/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp b/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp
index 777d6949a..274c5cf09 100644
--- a/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp
+++ b/src/openassetio-core-c/src/managerApi/CManagerInterfaceAdapter.hpp
@@ -52,8 +52,8 @@ class OPENASSETIO_CORE_C_EXPORT CManagerInterfaceAdapter : ManagerInterface {
   /// Wrap the C suite's `managementPolicy` function.
   /// @todo Implement C API. Currently throws `runtime_error`.
   [[nodiscard]] trait::TraitsDatas managementPolicy(
-      const trait::TraitSets& traitSets, const ContextConstPtr& context,
-      const HostSessionPtr& hostSession) const override;
+      const trait::TraitSets& traitSets, access::PolicyAccess policyAccess,
+      const ContextConstPtr& context, const HostSessionPtr& hostSession) const override;
 
   /// Wrap the C suite's `isEntityReferenceString` function.
   /// @todo Implement C API. Currently throws `runtime_error`.
@@ -70,22 +70,23 @@ class OPENASSETIO_CORE_C_EXPORT CManagerInterfaceAdapter : ManagerInterface {
   /// Wrap the C suite's `resolve` function.
   /// @todo Implement C API. Currently throws `runtime_error`.
   void resolve(const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
-               const ContextConstPtr& context, const HostSessionPtr& hostSession,
-               const ResolveSuccessCallback& successCallback,
+               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,
-                 const ContextConstPtr& context, const HostSessionPtr& hostSession,
+                 access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+                 const HostSessionPtr& hostSession,
                  const PreflightSuccessCallback& successCallback,
                  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,
-                 const ContextConstPtr& context, const HostSessionPtr& hostSession,
-                 const RegisterSuccessCallback& successCallback,
+                 access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+                 const HostSessionPtr& hostSession, const RegisterSuccessCallback& successCallback,
                  const BatchElementErrorCallback& errorCallback) override;
 
  private:
diff --git a/src/openassetio-core/include/openassetio/Context.hpp b/src/openassetio-core/include/openassetio/Context.hpp
index c9252ae5e..3bbf00474 100644
--- a/src/openassetio-core/include/openassetio/Context.hpp
+++ b/src/openassetio-core/include/openassetio/Context.hpp
@@ -16,77 +16,31 @@ inline namespace OPENASSETIO_CORE_ABI_VERSION {
 OPENASSETIO_DECLARE_PTR(Context)
 
 /**
- *  The Context object is used to convey information about the calling
- *  environment to a @ref manager. It encapsulates several key access
- *  properties, as well as providing additional information about the
- *  @ref host that may be useful to the @ref manager.
+ * The Context object is used to convey information about the calling
+ * environment to a @ref manager.
  *
- *  A Manager will also use this information to ensure it presents the
- *  correct UI, or behavior.
+ * It holds additional information about the @ref host that may be
+ * useful to the @ref manager. Optionally, it holds arbitrary, opaque
+ * (to the host), serializable manager state, which can be used by the
+ * manager plugin to tie together related calls.
  *
- *  The Context is passed to many calls in this API, and it may, or may
- *  not need to be used directly.
+ * A Manager will also use this information to ensure it presents the
+ * correct UI, or behavior.
  *
- *  @warning Contexts should never be directly constructed. Hosts should
- *  use @fqref{hostApi.Manager.createContext} "createContext" or
- *  @fqref{hostApi.Manager.createChildContext} "createChildContext". A
- *  Manager implementation should never need to create a context of it's
- *  own, one will always be supplied through the ManagerInterface entry
- *  points.
+ * The Context is passed to many calls in this API, and it may, or may
+ * not need to be used directly.
+ *
+ * @warning Contexts should never be directly constructed. Hosts should
+ * use @fqref{hostApi.Manager.createContext} "createContext" or
+ * @fqref{hostApi.Manager.createChildContext} "createChildContext". A
+ * Manager implementation should never need to create a context of it's
+ * own, one will always be supplied through the ManagerInterface entry
+ * points.
  */
 class OPENASSETIO_CORE_EXPORT Context final {
  public:
   OPENASSETIO_ALIAS_PTR(Context)
 
-  /**
-   * @name Access Pattern
-   */
-  enum class Access {
-    /**
-     * Host intends to read data. For example, trait property values
-     * obtained via `resolve` may be used to control the loading of data
-     * from a resource, and its subsequent interpretation.
-     */
-    kRead,
-    /**
-     * Host intends to write data.  For example, trait property values
-     * obtained via `resolve` may be used to control the writing of data
-     * to a resource, and specifics of its format or content. During
-     * publishing this must be used whenever the entity reference
-     * explicitly targets the specific entity whose data is being
-     * written. For example creating or updating a simple, unstructured
-     * asset such as an image or other file-based data.
-     */
-    kWrite,
-    /**
-     * Hosts intends to write data for a new entity in relation to
-     * another. During publishing this must be used whenever the entity
-     * reference points to an existing entity, and the intention is to
-     * create a  new, related entity instead of updating the target. For
-     * example, when programmatically creating new  entities under an
-     * existing parent collection, or the publishing of the components
-     * of a structured asset based on a single root entity reference.
-     */
-    kCreateRelated,
-    /// Unknown Access Pattern
-    kUnknown
-  };
-
-  static constexpr std::array kAccessNames{"read", "write", "createRelated", "unknown"};
-  /// @}
-
-  /**
-   * Describes what the @ref host is intending to do with the data.
-   *
-   * For example, when passed to resolve, it specifies if the @ref host
-   * is about to read or write. When configuring a BrowserWidget, then
-   * it will hint as to whether the Host is wanting to choose a new file
-   * name to save, or open an existing one.
-   *
-   * See also @ref create_related "Create related glossary entry".
-   */
-  Access access;
-
   /**
    * In many situations, the @ref trait_set of the desired @ref entity
    * itself is not entirely sufficient information to realize many
@@ -119,25 +73,11 @@ class OPENASSETIO_CORE_EXPORT Context final {
    * @fqref{hostApi.Manager.createContext} "Manager.createContext"
    * should always be used instead.
    */
-  [[nodiscard]] static ContextPtr make(Access access = Access::kUnknown,
-                                       TraitsDataPtr locale = nullptr,
+  [[nodiscard]] static ContextPtr make(TraitsDataPtr locale = nullptr,
                                        managerApi::ManagerStateBasePtr managerState = nullptr);
-  /**
-   * @return `true` if the context is a 'Read' based access pattern. If
-   * the access is unknown (Access::kUnknown), then `false` is returned.
-   */
-  [[nodiscard]] inline bool isForRead() const { return access == Access::kRead; }
-
-  /**
-   * @return `true` if the context is a 'Write' based access pattern. If
-   * the access is unknown (Access::kUnknown), then `false` is returned.
-   */
-  [[nodiscard]] inline bool isForWrite() const {
-    return access == Access::kWrite || access == Access::kCreateRelated;
-  }
 
  private:
-  Context(Access access, TraitsDataPtr locale, managerApi::ManagerStateBasePtr managerState);
+  Context(TraitsDataPtr locale, managerApi::ManagerStateBasePtr managerState);
 };
 }  // namespace OPENASSETIO_CORE_ABI_VERSION
 }  // namespace openassetio
diff --git a/src/openassetio-core/include/openassetio/access.hpp b/src/openassetio-core/include/openassetio/access.hpp
new file mode 100644
index 000000000..70e1f2f9f
--- /dev/null
+++ b/src/openassetio-core/include/openassetio/access.hpp
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright 2023 The Foundry Visionmongers Ltd
+#pragma once
+
+#include <array>
+
+#include <openassetio/export.h>
+
+namespace openassetio {
+inline namespace OPENASSETIO_CORE_ABI_VERSION {
+/**
+ * Access modes available for API operations.
+ */
+namespace access {
+
+/**
+ * Common constant values for strong enumerations.
+ *
+ * We must ensure correspondence between the values of the various
+ * workflows' strong enums. This allows for comparison, string lookup,
+ * simplifies serialisation, and supports language (especially C)
+ * bindings.
+ */
+enum Access { kRead = 0, kWrite, kCreateRelated };
+
+/// Mapping of access enum value to human-readable name.
+[[maybe_unused]] constexpr std::array kAccessNames{"read", "write", "createRelated"};
+
+/**
+ * Access pattern for a manager policy query.
+ *
+ * Since @fqref{hostApi.Manager.managementPolicy}
+ * "Manager.managementPolicy" /
+ * @fqref{managerApi.ManagerInterface.managementPolicy}
+ * "ManagerInterface.managementPolicy" is used to determine which
+ * functionality is supported by a manager, these constants largely
+ * mirror those for the relevant API methods.
+ */
+enum class PolicyAccess : int {
+  /**
+   * Host intends to read data.
+   *
+   * @see @ref ResolveAccess.kRead / @ref RelationsAccess.kRead
+   */
+  kRead = kRead,
+  /**
+   * Host intends to write data.
+   *
+   * @see @ref PublishingAccess.kWrite / @ref RelationsAccess.kWrite
+   */
+  kWrite = kWrite,
+  /**
+   * Hosts intends to write data for a new entity in relation to
+   * another.
+   *
+   * @see @ref PublishingAccess.kCreateRelated /
+   * @ref RelationsAccess.kCreateRelated
+   */
+  kCreateRelated = kCreateRelated,
+};
+
+/**
+ * Access pattern for @ref glossary_resolve "entity resolution".
+ */
+enum class ResolveAccess : int {
+  /**
+   * Used to query an existing entity for information.
+   *
+   * For example, trait property values may be used to control the
+   * loading of data from a resource, and its subsequent interpretation.
+   */
+  kRead = kRead,
+  /**
+   * Used by hosts to ask the manager how or where to write new data
+   * for an entity.
+   *
+   * For example, trait property values may be used to control the
+   * writing of data to a resource, and specifics of its format or
+   * content.
+   */
+  kWrite = kWrite,
+};
+
+/**
+ * Access pattern for @ref publish "publishing".
+ */
+enum class PublishingAccess : int {
+  /**
+   * Used whenever the entity reference explicitly targets the specific
+   * entity whose data is being written.
+   *
+   * For example creating or updating a simple, unstructured asset such
+   * as an image or other file-based data.
+   *
+   * Hosts should also choose this access mode if unsure which access
+   * mode is appropriate.
+   */
+  kWrite = kWrite,
+  /**
+   * Used whenever the entity reference points to an existing entity,
+   * and the intention is to create a  new, related entity instead of
+   * updating the target.
+   *
+   * For example, when programmatically creating new  entities under an
+   * existing parent collection, or the publishing of the components of
+   * a structured asset based on a single root entity reference.
+   */
+  kCreateRelated = kCreateRelated,
+};
+
+/**
+ * Access pattern for a relationship query.
+ *
+ * @see @fqref{hostApi.Manager.getWithRelationship}
+ * "Manager.getWithRelationship" /
+ * @fqref{managerApi.ManagerInterface.getWithRelationship}
+ * "ManagerInterface.getWithRelationship" (and similar).
+ */
+enum class RelationsAccess : int {
+  /**
+   * Used to retrieve references to pre-existing related entities.
+   */
+  kRead = kRead,
+  /**
+   * Used to retrieve references to related entities, with the intention
+   * of writing data to them.
+   *
+   * For example, during a @ref publish this could be used to retrieve
+   * references to the individual components of an entity, allowing the
+   * host to ask the manager for details on how and where to update
+   * them.
+   *
+   * This access mode should be used when the related entity already
+   * exists, or where the host is unsure whether it exists or not.
+   * Otherwise see @ref kCreateRelated
+   */
+  kWrite = kWrite,
+  /**
+   * Used to allow the manager to dictate a list of entities that the
+   * host should create.
+   *
+   * For example, during a @ref publish this could be used to decompose
+   * a single entity reference into a list of entity references, one for
+   * each component that the manager expects to be published, each with
+   * a different target location on disk.
+   *
+   * For querying pre-existing related entities, with the intention of
+   * writing new data, see @ref kWrite.
+   */
+  kCreateRelated = kCreateRelated,
+};
+
+/**
+ * Access pattern when querying a sensible default starting entity for
+ * further queries.
+ *
+ * @see @fqref{hostApi.Manager.defaultEntityReference}
+ * "Manager.defaultEntityReference" /
+ * @fqref{managerApi.ManagerInterface.defaultEntityReference}
+ * "ManagerInterface.defaultEntityReference",
+ */
+enum class DefaultEntityAccess : int {
+  /**
+   * Indicate that the @ref manager should provide an entity reference
+   * that can be queried for existing data.
+   *
+   * @see @ref ResolveAccess.kRead, @ref RelationsAccess.kRead
+   */
+  kRead = kRead,
+
+  /**
+   * Indicate that the @ref manager should provide a reference suitable
+   * for publishing to.
+   *
+   * @see @ref PublishingAccess.kWrite,
+   * @ref RelationsAccess.kWrite
+   */
+  kWrite = kWrite,
+
+  /**
+   * Indicate that the @ref manager should provide an entity reference
+   * that will be used to @ref publish one or more new entities to.
+   *
+   * @see @ref PublishingAccess.kCreateRelated,
+   * @ref RelationsAccess.kCreateRelated
+   */
+  kCreateRelated = kCreateRelated
+};
+}  // namespace access
+}  // namespace OPENASSETIO_CORE_ABI_VERSION
+}  // namespace openassetio
diff --git a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
index 4bb2da27a..c1d78d558 100644
--- a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
+++ b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
@@ -12,6 +12,7 @@
 #include <openassetio/BatchElementError.hpp>
 #include <openassetio/EntityReference.hpp>
 #include <openassetio/InfoDictionary.hpp>
+#include <openassetio/access.hpp>
 #include <openassetio/hostApi/EntityReferencePager.hpp>
 #include <openassetio/trait/collection.hpp>
 #include <openassetio/typedefs.hpp>
@@ -241,18 +242,18 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    *  - The trait set of the entity type in question. This is usually
    *    obtained from the relevant @ref Specification.
-   *  - For @fqref{Context.Access} "read" contexts, any additional
-   *    traits with properties that you wish to resolve for that type of
-   *    entity.
-   *  - For @fqref{Context.Access} "write" contexts, any additional
-   *    traits with properties that you wish to publish for that type of
-   *    entity.
+   *  - For @fqref{access.PolicyAccess.kRead} "read" usage, any
+   *    additional traits with properties that you wish to resolve for
+   *    that type of entity.
+   *  - For @fqref{access.PolicyAccess.kWrite} "write" usage, any
+   *    additional traits with properties that you wish to publish for
+   *    that type of entity.
    *
    * Along with the traits that describe the manager's desired
    * interaction pattern (ones with the `managementPolicy` usage
-   * metadata), the resulting @fqref{TraitsData} "TraitsData" will be imbued with
-   * (potentially a subset of) the requested traits, which the manager is
-   * capable of resolving/persisting the properties for.
+   * metadata), the resulting @fqref{TraitsData} "TraitsData" will be
+   * imbued with (potentially a subset of) the requested traits, which
+   * the manager is capable of resolving/persisting the properties for.
    *
    * If a requested trait is not present, then the manager will never
    * return properties for that trait in @ref resolve, or be able to
@@ -271,12 +272,12 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * by these projects to retrieve data from the supplied TraitsData
    * instead of querying directly using string literals.
    *
-   * @warning The @fqref{Context.access} "access" of the supplied
-   * context will be considered by the manager. If it is set to read,
-   * then its response applies to resolution. If write, then it applies
-   * to publishing. Managers may not handle both operations in the same
-   * way. In most situations you will need to make separate queries for
-   * read and write and adapt your business logic accordingly.
+   * @warning The @p policyAccess that is supplied will be considered by
+   * the manager. If it is set to read, then its response applies to
+   * resolution. If write, then it applies to publishing. Managers may
+   * not handle both operations in the same way. In most situations you
+   * will need to make separate queries for read and write and adapt
+   * your business logic accordingly.
    *
    * @note There is no requirement to call this method before any other
    * API interaction, though it is strongly recommended to do so where
@@ -285,11 +286,14 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * @param traitSets The entity @ref trait "traits" to query.
    *
+   * @param policyAccess Intended operation type to perform on entities.
+   *
    * @param context The calling context.
    *
    * @return a `TraitsData` for each element in @p traitSets.
    */
   [[nodiscard]] trait::TraitsDatas managementPolicy(const trait::TraitSets& traitSets,
+                                                    access::PolicyAccess policyAccess,
                                                     const ContextConstPtr& context) const;
 
   /**
@@ -666,12 +670,14 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * @param entityReferences Entity references to query.
    *
-   * @param context The calling context.
-   *
    * @param traitSet The trait IDs to resolve for the supplied list of
    * entity references. Only traits applicable to the supplied entity
    * references will be set in the resulting data.
    *
+   * @param resolveAccess The intended usage of the data.
+   *
+   * @param context The calling context.
+   *
    * @param successCallback Callback that will be called for each
    * successful resolution of an entity reference. It will be
    * given the corresponding index of the entity reference in
@@ -687,7 +693,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * that initiated the call to `resolve`.
    */
   void resolve(const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
-               const ContextConstPtr& context, const ResolveSuccessCallback& successCallback,
+               access::ResolveAccess resolveAccess, const ContextConstPtr& context,
+               const ResolveSuccessCallback& successCallback,
                const BatchElementErrorCallback& errorCallback);
 
   /**
@@ -697,8 +704,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref resolve(const EntityReferences&, <!--
-   * --> const trait::TraitSet&, const ContextConstPtr&, <!--
-   * --> const ResolveSuccessCallback&, <!--
+   * --> const trait::TraitSet&, access::ResolveAccess, <!--
+   * --> const ContextConstPtr&, const ResolveSuccessCallback&, <!--
    * --> const BatchElementErrorCallback& errorCallback)
    * "callback variation" for more details on resolution behaviour.
    *
@@ -713,6 +720,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * reference. Only traits applicable to the supplied entity reference
    * will be set in the resulting data.
    *
+   * @param resolveAccess The intended usage of the data.
+   *
    * @param context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -722,7 +731,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * @return Populated data.
    */
   TraitsDataPtr resolve(const EntityReference& entityReference, const trait::TraitSet& traitSet,
-                        const ContextConstPtr& context,
+                        access::ResolveAccess resolveAccess, const ContextConstPtr& context,
                         const BatchElementErrorPolicyTag::Exception& errorPolicyTag = {});
 
   /**
@@ -741,8 +750,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref resolve(const EntityReferences&, <!--
-   * --> const trait::TraitSet&, const ContextConstPtr&, <!--
-   * --> const ResolveSuccessCallback&, <!--
+   * --> const trait::TraitSet&, access::ResolveAccess, <!--
+   * --> const ContextConstPtr&, const ResolveSuccessCallback&, <!--
    * --> const BatchElementErrorCallback& errorCallback)
    * "callback variation" for more details on resolution behaviour.
    *
@@ -752,6 +761,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * reference. Only traits applicable to the supplied entity reference
    * will be set in the resulting data.
    *
+   * @param resolveAccess The intended usage of the data.
+   *
    * @param context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -763,7 +774,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   std::variant<BatchElementError, TraitsDataPtr> resolve(
       const EntityReference& entityReference, const trait::TraitSet& traitSet,
-      const ContextConstPtr& context, const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
+      access::ResolveAccess resolveAccess, const ContextConstPtr& context,
+      const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
 
   /**
    * Provides a @fqref{TraitsData} "TraitsData" populated with the
@@ -772,8 +784,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref resolve(const EntityReferences&, <!--
-   * --> const trait::TraitSet&, const ContextConstPtr&, <!--
-   * --> const ResolveSuccessCallback&, <!--
+   * --> const trait::TraitSet&, access::ResolveAccess, <!--
+   * --> const ContextConstPtr&, const ResolveSuccessCallback&, <!--
    * --> const BatchElementErrorCallback& errorCallback)
    * "callback variation" for more details on resolution behaviour.
    *
@@ -789,6 +801,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * entity references. Only traits applicable to the supplied entity
    * references will be set in the resulting data.
    *
+   * @param resolveAccess The intended usage of the data.
+   *
    * @param context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -799,7 +813,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   std::vector<TraitsDataPtr> resolve(
       const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
-      const ContextConstPtr& context,
+      access::ResolveAccess resolveAccess, const ContextConstPtr& context,
       const BatchElementErrorPolicyTag::Exception& errorPolicyTag = {});
 
   /**
@@ -820,8 +834,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref resolve(const EntityReferences&, <!--
-   * --> const trait::TraitSet&, const ContextConstPtr&, <!--
-   * --> const ResolveSuccessCallback&, <!--
+   * --> const trait::TraitSet&, access::ResolveAccess, <!--
+   * --> const ContextConstPtr&, const ResolveSuccessCallback&, <!--
    * --> const BatchElementErrorCallback& errorCallback)
    * "callback variation" for more details on resolution behaviour.
    *
@@ -831,6 +845,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * entity references. Only traits applicable to the supplied entity
    * references will be set in the resulting data.
    *
+   * @param resolveAccess The intended usage of the data.
+   *
    * @param context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -842,7 +858,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   std::vector<std::variant<BatchElementError, TraitsDataPtr>> resolve(
       const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
-      const ContextConstPtr& context, const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
+      access::ResolveAccess resolveAccess, const ContextConstPtr& context,
+      const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
 
   /**
    * Callback signature used for a successful default entity reference query.
@@ -864,6 +881,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * required, these will be interpreted in conjunction with the context
    * to determine the most sensible default.
    *
+   * @param defaultEntityAccess Intended usage of the returned entity
+   * reference(s).
+   *
    * @param context The calling context.
    *
    * @param successCallback Callback that will be called for each
@@ -885,7 +905,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * not recognised by the manager. The callback will be called on the
    * same thread that initiated the call to `defaultEntityReference`.
    */
-  void defaultEntityReference(const trait::TraitSets& traitSets, const ContextConstPtr& context,
+  void defaultEntityReference(const trait::TraitSets& traitSets,
+                              access::DefaultEntityAccess defaultEntityAccess,
+                              const ContextConstPtr& context,
                               const DefaultEntityReferenceSuccessCallback& successCallback,
                               const BatchElementErrorCallback& errorCallback);
 
@@ -960,6 +982,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * @param relationshipTraitsData The traits of the relationship to
    * query.
    *
+   * @param relationsAccess The intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param successCallback Callback that will be called for each
@@ -987,7 +1012,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   void getWithRelationship(const EntityReferences& entityReferences,
                            const TraitsDataPtr& relationshipTraitsData,
-                           const ContextConstPtr& context,
+                           access::RelationsAccess relationsAccess, const ContextConstPtr& context,
                            const RelationshipSuccessCallback& successCallback,
                            const BatchElementErrorCallback& errorCallback,
                            const trait::TraitSet& resultTraitSet = {});
@@ -1013,6 +1038,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * @param relationshipTraitsDatas The traits of the relationships to
    * query.
    *
+   * @param relationsAccess The intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param successCallback Callback that will be called for each
@@ -1040,6 +1068,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   void getWithRelationships(const EntityReference& entityReference,
                             const trait::TraitsDatas& relationshipTraitsDatas,
+                            access::RelationsAccess relationsAccess,
                             const ContextConstPtr& context,
                             const RelationshipSuccessCallback& successCallback,
                             const BatchElementErrorCallback& errorCallback,
@@ -1066,6 +1095,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * fixed for the lifetime of pager object given to the @p
    * successCallback. Must be greater than zero.
    *
+   * @param relationsAccess The intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param successCallback Callback that will be called for each
@@ -1093,6 +1125,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   void getWithRelationshipPaged(const EntityReferences& entityReferences,
                                 const TraitsDataPtr& relationshipTraitsData, size_t pageSize,
+                                access::RelationsAccess relationsAccess,
                                 const ContextConstPtr& context,
                                 const PagedRelationshipSuccessCallback& successCallback,
                                 const BatchElementErrorCallback& errorCallback,
@@ -1112,6 +1145,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * fixed for the lifetime of pager object given to the @p
    * successCallback. Must be greater than zero.
    *
+   * @param relationsAccess The intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param successCallback Callback that will be called for each
@@ -1143,7 +1179,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   void getWithRelationshipsPaged(const EntityReference& entityReference,
                                  const trait::TraitsDatas& relationshipTraitsDatas,
-                                 size_t pageSize, const ContextConstPtr& context,
+                                 size_t pageSize, access::RelationsAccess relationsAccess,
+                                 const ContextConstPtr& context,
                                  const PagedRelationshipSuccessCallback& successCallback,
                                  const BatchElementErrorCallback& errorCallback,
                                  const trait::TraitSet& resultTraitSet = {});
@@ -1254,15 +1291,11 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * the Manager may even use this opportunity to switch to some
    * temporary working path or some such.
    *
-   * @warning If the supplied @fqref{TraitsData} "trait data" is missing
+   * @note If the supplied @fqref{TraitsData} "trait data" is missing
    * traits or properties required by the manager for any input entity
    * reference, then that element will error. See @ref
    * glossary_preflight "glossary entry" for details.
    *
-   * @note It's vital that the @ref Context is well configured here,
-   * in particular the @fqref{Context.access}
-   * "Context.access".
-   *
    * @warning The working @ref entity_reference returned by this
    * method should *always* be used in place of the original
    * reference supplied to `preflight` for resolves prior to
@@ -1277,10 +1310,12 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * owns and can provide at this time. See @ref glossary_preflight
    * "glossary entry" for details.
    *
-   * @param context The calling context. This is not
-   * replaced with an array in order to simplify implementation.
-   * Otherwise, transactional handling has the potential to be
-   * extremely complex if different contexts are allowed.
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
+   * @param context The calling context.
    *
    * @param successCallback Callback that will be called for each
    * successful preflight of an entity reference. It will be given
@@ -1301,7 +1336,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * @see @ref register_
    */
   void preflight(const EntityReferences& entityReferences, const trait::TraitsDatas& traitsHints,
-                 const ContextConstPtr& context, const PreflightSuccessCallback& successCallback,
+                 access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+                 const PreflightSuccessCallback& successCallback,
                  const BatchElementErrorCallback& errorCallback);
 
   /**
@@ -1311,8 +1347,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref preflight(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const PreflightSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const PreflightSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on preflight behaviour.
    *
@@ -1329,6 +1365,11 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * determining the type of entity to publish, complete with any
    * properties that can be provided at this time.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
    * @param context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -1338,7 +1379,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * the publishing operation
    */
   EntityReference preflight(const EntityReference& entityReference,
-                            const TraitsDataPtr& traitsHint, const ContextConstPtr& context,
+                            const TraitsDataPtr& traitsHint,
+                            access::PublishingAccess publishingAccess,
+                            const ContextConstPtr& context,
                             const BatchElementErrorPolicyTag::Exception& errorPolicyTag = {});
 
   /**
@@ -1348,8 +1391,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref preflight(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const PreflightSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const PreflightSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on preflight behaviour.
    *
@@ -1369,6 +1412,11 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * determining the type of entity to publish, complete with any
    * properties that can be provided at this time.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
    * @param context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -1381,7 +1429,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   std::variant<BatchElementError, EntityReference> preflight(
       const EntityReference& entityReference, const TraitsDataPtr& traitsHint,
-      const ContextConstPtr& context, const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
+      access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+      const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
 
   /**
    * This call signals your intent as a host application to do some
@@ -1390,8 +1439,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref preflight(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const PreflightSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const PreflightSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on preflight behaviour.
    *
@@ -1408,6 +1457,11 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * determining the type of entity to publish, complete with any
    * properties that can be provided at this time.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
    * @param context The calling context. The same calling context is
    * used for each entity reference.
    *
@@ -1418,7 +1472,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * of the publishing operation
    */
   EntityReferences preflight(const EntityReferences& entityReferences,
-                             const trait::TraitsDatas& traitsHints, const ContextConstPtr& context,
+                             const trait::TraitsDatas& traitsHints,
+                             access::PublishingAccess publishingAccess,
+                             const ContextConstPtr& context,
                              const BatchElementErrorPolicyTag::Exception& errorPolicyTag = {});
 
   /**
@@ -1428,8 +1484,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref preflight(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const PreflightSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const PreflightSuccessCallback&, <!--
    * --> const BatchElementErrorCallback& errorCallback)
    * "callback variation" for more details on preflight behaviour.
    *
@@ -1451,6 +1507,11 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * determining the type of entity to publish, complete with any
    * properties that can be provided at this time.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
    * @param context The calling context. The same calling context is
    * used for each entity reference.
    *
@@ -1464,7 +1525,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   std::vector<std::variant<BatchElementError, EntityReference>> preflight(
       const EntityReferences& entityReferences, const trait::TraitsDatas& traitsHints,
-      const ContextConstPtr& context, const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
+      access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+      const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
 
   /**
    * Callback signature used for a successful register operation on a
@@ -1529,6 +1591,13 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * NOTE: All supplied instances should have the same trait set,
    * batching with varying traits is not supported.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity. Note that if the @p entityReference came from a
+   * @ref preflight call, then @fqref{access.PublishAccess.kWrite}
+   * "kWrite" is the only valid value here.
+   *
    * @param context Context The calling context.
    *
    * @param successCallback Callback that will be called for each
@@ -1560,7 +1629,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   // NOLINTNEXTLINE(readability-identifier-naming)
   void register_(const EntityReferences& entityReferences,
-                 const trait::TraitsDatas& entityTraitsDatas, const ContextConstPtr& context,
+                 const trait::TraitsDatas& entityTraitsDatas,
+                 access::PublishingAccess publishingAccess, const ContextConstPtr& context,
                  const RegisterSuccessCallback& successCallback,
                  const BatchElementErrorCallback& errorCallback);
 
@@ -1571,8 +1641,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref register_(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const RegisterSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const RegisterSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on register_ behaviour.
    *
@@ -1586,6 +1656,13 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * @param entityTraitsData The data to register for the entity.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity. Note that if the @p entityReference came from a
+   * @ref preflight call, then @fqref{access.PublishAccess.kWrite}
+   * "kWrite" is the only valid value here.
+   *
    * @param context Context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -1596,7 +1673,9 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   // NOLINTNEXTLINE(readability-identifier-naming)
   EntityReference register_(const EntityReference& entityReference,
-                            const TraitsDataPtr& entityTraitsData, const ContextConstPtr& context,
+                            const TraitsDataPtr& entityTraitsData,
+                            access::PublishingAccess publishingAccess,
+                            const ContextConstPtr& context,
                             const BatchElementErrorPolicyTag::Exception& errorPolicyTag = {});
 
   /**
@@ -1606,8 +1685,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref register_(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const RegisterSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const RegisterSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on register_ behaviour.
    *
@@ -1624,6 +1703,13 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * @param entityTraitsData The data to register for the entity.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity. Note that if the @p entityReference came from a
+   * @ref preflight call, then @fqref{access.PublishAccess.kWrite}
+   * "kWrite" is the only valid value here.
+   *
    * @param context Context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -1636,7 +1722,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
   // NOLINTNEXTLINE(readability-identifier-naming)
   std::variant<BatchElementError, EntityReference> register_(
       const EntityReference& entityReference, const TraitsDataPtr& entityTraitsData,
-      const ContextConstPtr& context, const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
+      access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+      const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
 
   /**
    * Register should be used to 'publish' new entities either when
@@ -1645,8 +1732,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref register_(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const RegisterSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const RegisterSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on register_ behaviour.
    *
@@ -1662,6 +1749,13 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * NOTE: All supplied instances should have the same trait set,
    * batching with varying traits is not supported.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity. Note that if the @p entityReference came from a
+   * @ref preflight call, then @fqref{access.PublishAccess.kWrite}
+   * "kWrite" is the only valid value here.
+   *
    * @param context Context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -1673,7 +1767,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
   // NOLINTNEXTLINE(readability-identifier-naming)
   std::vector<EntityReference> register_(
       const EntityReferences& entityReferences, const trait::TraitsDatas& entityTraitsDatas,
-      const ContextConstPtr& context,
+      access::PublishingAccess publishingAccess, const ContextConstPtr& context,
       const BatchElementErrorPolicyTag::Exception& errorPolicyTag = {});
 
   /**
@@ -1683,8 +1777,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *
    * See documentation for the <!--
    * --> @ref register_(const EntityReferences&, <!--
-   * --> const trait::TraitsDatas&, const ContextConstPtr&, <!--
-   * --> const RegisterSuccessCallback&, <!--
+   * --> const trait::TraitsDatas&, access::PublishingAccess, <!--
+   * --> const ContextConstPtr&, const RegisterSuccessCallback&, <!--
    * --> const BatchElementErrorCallback&)
    * "callback variation" for more details on register_ behaviour.
    *
@@ -1705,6 +1799,13 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * NOTE: All supplied instances should have the same trait set,
    * batching with varying traits is not supported.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity. Note that if the @p entityReference came from a
+   * @ref preflight call, then @fqref{access.PublishAccess.kWrite}
+   * "kWrite" is the only valid value here.
+   *
    * @param context Context The calling context.
    *
    * @param errorPolicyTag  Parameter for selecting the appropriate
@@ -1718,7 +1819,8 @@ class OPENASSETIO_CORE_EXPORT Manager final {
   // NOLINTNEXTLINE(readability-identifier-naming)
   std::vector<std::variant<BatchElementError, EntityReference>> register_(
       const EntityReferences& entityReferences, const trait::TraitsDatas& entityTraitsDatas,
-      const ContextConstPtr& context, const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
+      access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+      const BatchElementErrorPolicyTag::Variant& errorPolicyTag);
 
   /// @}
 
diff --git a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
index fb4510aed..22c4016b7 100644
--- a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
+++ b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
@@ -12,6 +12,7 @@
 #include <openassetio/BatchElementError.hpp>
 #include <openassetio/EntityReference.hpp>
 #include <openassetio/InfoDictionary.hpp>
+#include <openassetio/access.hpp>
 #include <openassetio/trait/collection.hpp>
 #include <openassetio/typedefs.hpp>
 
@@ -376,9 +377,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    *
    * This method must return a @fqref{TraitsData} "TraitsData" for each
    * requested @ref trait_set. The implementation of this method should
-   * carefully consider the @fqref{Context.access} "access" set in the
-   * supplied @ref Context, and imbue suitable traits in the result to
-   * define:
+   * carefully consider the given @fqref{access.PolicyAccess} "access",
+   * and imbue suitable traits in the result to define:
    *
    *   - Whether and how that kind of entity is managed (traits with the
    *    `managementPolicy` usage metadata)
@@ -402,14 +402,16 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * by these projects to manipulate the result TraitsData instead of
    * directly setting traits and properties with string literals.
    *
-   * @warning The @fqref{Context.access} "access"
-   * specified in the supplied context should be carefully considered.
-   * A host will independently query the policy for both read and
-   * write access to determine if resolution and publishing features
-   * are applicable to this implementation.
+   * @warning The given @p policyAccess should be carefully considered.
+   * A host will independently query the policy for both read and write
+   * access to determine if resolution and publishing features are
+   * applicable to this implementation.
    *
    * @param traitSets The entity @ref trait "traits" to query.
    *
+   * @param policyAccess Type of operation that the host would like to
+   * perform on entities of the type given in @p traitSets.
+   *
    * @param context The calling context.
    *
    * @param hostSession The API session.
@@ -417,8 +419,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @return a `TraitsData` for each element in @p traitSets.
    */
   [[nodiscard]] virtual trait::TraitsDatas managementPolicy(
-      const trait::TraitSets& traitSets, const ContextConstPtr& context,
-      const HostSessionPtr& hostSession) const = 0;
+      const trait::TraitSets& traitSets, access::PolicyAccess policyAccess,
+      const ContextConstPtr& context, const HostSessionPtr& hostSession) const = 0;
 
   /**
    * @}
@@ -764,6 +766,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @param traitSet The traits to resolve for the supplied list of
    * entity references.
    *
+   * @param resolveAccess The host's intended usage of the data.
+   *
    * @param context The calling context.
    *
    * @param hostSession The API session.
@@ -787,7 +791,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @see @fqref{BatchElementError} "BatchElementError"
    */
   virtual void resolve(const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
-                       const ContextConstPtr& context, const HostSessionPtr& hostSession,
+                       access::ResolveAccess resolveAccess, const ContextConstPtr& context,
+                       const HostSessionPtr& hostSession,
                        const ResolveSuccessCallback& successCallback,
                        const BatchElementErrorCallback& errorCallback) = 0;
 
@@ -821,6 +826,9 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * required, these will be interpreted in conjunction with the context
    * to determine the most sensible default.
    *
+   * @param defaultEntityAccess Intended usage of the returned entity
+   * reference(s).
+   *
    * @param context The calling context.
    *
    * @param hostSession The host session that maps to the caller, this
@@ -847,6 +855,7 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * initiated the call to `defaultEntityReference`.
    */
   virtual void defaultEntityReference(const trait::TraitSets& traitSets,
+                                      access::DefaultEntityAccess defaultEntityAccess,
                                       const ContextConstPtr& context,
                                       const HostSessionPtr& hostSession,
                                       const DefaultEntityReferenceSuccessCallback& successCallback,
@@ -925,6 +934,9 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @param resultTraitSet A hint as to what traits the returned
    * entities should have. May be empty.
    *
+   * @param relationsAccess The host's intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param hostSession The host session that maps to the caller, this
@@ -958,6 +970,7 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
   virtual void getWithRelationship(const EntityReferences& entityReferences,
                                    const TraitsDataPtr& relationshipTraitsData,
                                    const trait::TraitSet& resultTraitSet,
+                                   access::RelationsAccess relationsAccess,
                                    const ContextConstPtr& context,
                                    const HostSessionPtr& hostSession,
                                    const RelationshipSuccessCallback& successCallback,
@@ -990,12 +1003,15 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @param resultTraitSet A hint as to what traits the returned
    * entities should have. May be empty.
    *
+   * @param relationsAccess The host's intended usage of the returned
+   * references.
+   *
+   * @param context Context The calling context.
+   *
    * @param hostSession The host session that maps to the caller, this
    * should be used for all logging and provides access to the Host
    * object representing the process that initiated the API session.
    *
-   * @param context Context The calling context.
-   *
    * @param successCallback Callback that must be called for each
    * successful relationship query for an input relationship. It should
    * be given the corresponding index of the relationship definition in
@@ -1020,6 +1036,7 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
   virtual void getWithRelationships(const EntityReference& entityReference,
                                     const trait::TraitsDatas& relationshipTraitsDatas,
                                     const trait::TraitSet& resultTraitSet,
+                                    access::RelationsAccess relationsAccess,
                                     const ContextConstPtr& context,
                                     const HostSessionPtr& hostSession,
                                     const RelationshipSuccessCallback& successCallback,
@@ -1046,6 +1063,9 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * be fixed for the lifetime of pager object given to the @p
    * successCallback. Guaranteed to be greater than zero.
    *
+   * @param relationsAccess The host's intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param hostSession The host session that maps to the caller, this
@@ -1082,6 +1102,7 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
   virtual void getWithRelationshipPaged(const EntityReferences& entityReferences,
                                         const TraitsDataPtr& relationshipTraitsData,
                                         const trait::TraitSet& resultTraitSet, size_t pageSize,
+                                        access::RelationsAccess relationsAccess,
                                         const ContextConstPtr& context,
                                         const HostSessionPtr& hostSession,
                                         const PagedRelationshipSuccessCallback& successCallback,
@@ -1101,6 +1122,9 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * fixed for the lifetime of pager object given to the @p
    * successCallback. Guaranteed to be greater than zero.
    *
+   * @param relationsAccess The host's intended usage of the returned
+   * references.
+   *
    * @param context The calling context.
    *
    * @param hostSession The host session that maps to the caller, this
@@ -1141,6 +1165,7 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
   virtual void getWithRelationshipsPaged(const EntityReference& entityReference,
                                          const trait::TraitsDatas& relationshipTraitsDatas,
                                          const trait::TraitSet& resultTraitSet, size_t pageSize,
+                                         access::RelationsAccess relationsAccess,
                                          const ContextConstPtr& context,
                                          const HostSessionPtr& hostSession,
                                          const PagedRelationshipSuccessCallback& successCallback,
@@ -1249,9 +1274,9 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * required traits for any of the provided references (maybe they are
    * mismatched with the target entity), or the populated properties are
    * insufficient or invalid for upcoming @ref resolve for
-   * @fqref{Context.Access.kWrite} "write" requests or the eventual
-   * @ref register_, then error that element with an appropriate
-   * @fqref{BatchElementError.ErrorCode}.
+   * @fqref{access.ResolveAccess.kWrite} "write" requests or the
+   * eventual @ref register_, then error that element with an
+   * appropriate @fqref{BatchElementError.ErrorCode}.
    *
    * @param entityReferences An @ref entity_reference for each entity
    * that it is desired to publish the forthcoming data to. See the
@@ -1262,6 +1287,11 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * properties the host owns and can provide at this time. See @ref
    * glossary_preflight "glossary entry" for more information.
    *
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
    * @param context The calling context. This is not replaced with an
    * array in order to simplify implementation. Otherwise, transactional
    * handling has the potential to be extremely complex if different
@@ -1291,8 +1321,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * "kEntityAccessError" should be used if the access pattern cannot be
    * adhered to, for example when attempting to write any target
    * references that are conceptually read-only in response to
-   * @fqref{Context.Access.kWrite} "Access.kWrite", or in response to
-   * @fqref{Context.Access.kCreateRelated} "Access.kCreateRelated" if
+   * @fqref{access.PublishAccess.kWrite} "kWrite", or in response to
+   * @fqref{access.PublishAccess.kCreateRelated} "kCreateRelated" if
    * creating related entities is not supported. A
    * @fqref{BatchElementError.ErrorCode.kInvalidPreflightHint}
    * "kInvalidPreflightHint" should be used for any target references
@@ -1302,7 +1332,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @see @ref register_
    */
   virtual void preflight(const EntityReferences& entityReferences,
-                         const trait::TraitsDatas& traitsHints, const ContextConstPtr& context,
+                         const trait::TraitsDatas& traitsHints,
+                         access::PublishingAccess publishingAccess, const ContextConstPtr& context,
                          const HostSessionPtr& hostSession,
                          const PreflightSuccessCallback& successCallback,
                          const BatchElementErrorCallback& errorCallback) = 0;
@@ -1371,10 +1402,12 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * the long-term persistent storage location of the registered data,
    * after it has been re-located by the manager.
    *
-   * @param context The calling context. This is not
-   * replaced with an array in order to simplify implementation.
-   * Otherwise, transactional handling has the potential to be
-   * extremely complex if different contexts are allowed.
+   * @param publishingAccess Whether to perform a generic
+   * @fqref{access.PublishAccess.kWrite} "write" to an entity or to
+   * (explicitly) @fqref{access.PublishAccess.kCreateRelated} "create a
+   * related" entity.
+   *
+   * @param context The calling context.
    *
    * @param hostSession The API session.
    *
@@ -1402,7 +1435,8 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
   // NOLINTNEXTLINE(readability-identifier-naming)
   virtual void register_(const EntityReferences& entityReferences,
                          const trait::TraitsDatas& entityTraitsDatas,
-                         const ContextConstPtr& context, const HostSessionPtr& hostSession,
+                         access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+                         const HostSessionPtr& hostSession,
                          const RegisterSuccessCallback& successCallback,
                          const BatchElementErrorCallback& errorCallback) = 0;
 
diff --git a/src/openassetio-core/src/Context.cpp b/src/openassetio-core/src/Context.cpp
index 10890e42e..c85621691 100644
--- a/src/openassetio-core/src/Context.cpp
+++ b/src/openassetio-core/src/Context.cpp
@@ -4,13 +4,11 @@
 
 namespace openassetio {
 inline namespace OPENASSETIO_CORE_ABI_VERSION {
-ContextPtr Context::make(Access access, TraitsDataPtr locale,
-                         managerApi::ManagerStateBasePtr managerState) {
-  return std::shared_ptr<Context>(new Context(access, std::move(locale), std::move(managerState)));
+ContextPtr Context::make(TraitsDataPtr locale, managerApi::ManagerStateBasePtr managerState) {
+  return std::shared_ptr<Context>(new Context(std::move(locale), std::move(managerState)));
 }
 
-Context::Context(Access access_, TraitsDataPtr locale_,
-                 managerApi::ManagerStateBasePtr managerState_)
-    : access{access_}, locale{std::move(locale_)}, managerState{std::move(managerState_)} {}
+Context::Context(TraitsDataPtr locale_, managerApi::ManagerStateBasePtr managerState_)
+    : locale{std::move(locale_)}, managerState{std::move(managerState_)} {}
 }  // namespace OPENASSETIO_CORE_ABI_VERSION
 }  // namespace openassetio
diff --git a/src/openassetio-core/src/hostApi/Manager.cpp b/src/openassetio-core/src/hostApi/Manager.cpp
index 069f95d50..7896e7444 100644
--- a/src/openassetio-core/src/hostApi/Manager.cpp
+++ b/src/openassetio-core/src/hostApi/Manager.cpp
@@ -105,8 +105,9 @@ void Manager::initialize(InfoDictionary managerSettings) {
 void Manager::flushCaches() { managerInterface_->flushCaches(hostSession_); }
 
 trait::TraitsDatas Manager::managementPolicy(const trait::TraitSets &traitSets,
+                                             const access::PolicyAccess policyAccess,
                                              const ContextConstPtr &context) const {
-  return managerInterface_->managementPolicy(traitSets, context, hostSession_);
+  return managerInterface_->managementPolicy(traitSets, policyAccess, context, hostSession_);
 }
 
 ContextPtr Manager::createContext() {
@@ -117,10 +118,9 @@ ContextPtr Manager::createContext() {
 }
 
 ContextPtr Manager::createChildContext(const ContextPtr &parentContext) {
-  // Copy-construct the locale so changes made to the child context don't
-  // affect the parent (and visa versa).
-  ContextPtr context =
-      Context::make(parentContext->access, TraitsData::make(parentContext->locale));
+  // Copy-construct the locale so changes made to the child context
+  // don't affect the parent (and vice versa).
+  ContextPtr context = Context::make(TraitsData::make(parentContext->locale));
   if (parentContext->managerState) {
     context->managerState =
         managerInterface_->createChildState(parentContext->managerState, hostSession_);
@@ -177,21 +177,21 @@ void Manager::entityExists(const EntityReferences &entityReferences,
 }
 
 void Manager::resolve(const EntityReferences &entityReferences, const trait::TraitSet &traitSet,
-                      const ContextConstPtr &context,
+                      const access::ResolveAccess resolveAccess, const ContextConstPtr &context,
                       const ResolveSuccessCallback &successCallback,
                       const BatchElementErrorCallback &errorCallback) {
-  managerInterface_->resolve(entityReferences, traitSet, context, hostSession_, successCallback,
-                             errorCallback);
+  managerInterface_->resolve(entityReferences, traitSet, resolveAccess, context, hostSession_,
+                             successCallback, errorCallback);
 }
 
 // Singular Except
 TraitsDataPtr hostApi::Manager::resolve(
     const EntityReference &entityReference, const trait::TraitSet &traitSet,
-    const ContextConstPtr &context,
+    const access::ResolveAccess resolveAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Exception &errorPolicyTag) {
   TraitsDataPtr resolveResult;
   resolve(
-      {entityReference}, traitSet, context,
+      {entityReference}, traitSet, resolveAccess, context,
       [&resolveResult]([[maybe_unused]] std::size_t index, TraitsDataPtr data) {
         resolveResult = std::move(data);
       },
@@ -205,11 +205,11 @@ TraitsDataPtr hostApi::Manager::resolve(
 // Singular variant
 std::variant<BatchElementError, TraitsDataPtr> hostApi::Manager::resolve(
     const EntityReference &entityReference, const trait::TraitSet &traitSet,
-    const ContextConstPtr &context,
+    const access::ResolveAccess resolveAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Variant &errorPolicyTag) {
   std::variant<BatchElementError, TraitsDataPtr> resolveResult;
   resolve(
-      {entityReference}, traitSet, context,
+      {entityReference}, traitSet, resolveAccess, context,
       [&resolveResult]([[maybe_unused]] std::size_t index, TraitsDataPtr data) {
         resolveResult = std::move(data);
       },
@@ -223,13 +223,13 @@ std::variant<BatchElementError, TraitsDataPtr> hostApi::Manager::resolve(
 // Multi except
 std::vector<TraitsDataPtr> hostApi::Manager::resolve(
     const EntityReferences &entityReferences, const trait::TraitSet &traitSet,
-    const ContextConstPtr &context,
+    const access::ResolveAccess resolveAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Exception &errorPolicyTag) {
   std::vector<TraitsDataPtr> resolveResult;
   resolveResult.resize(entityReferences.size());
 
   resolve(
-      entityReferences, traitSet, context,
+      entityReferences, traitSet, resolveAccess, context,
       [&resolveResult](std::size_t index, TraitsDataPtr data) {
         resolveResult[index] = std::move(data);
       },
@@ -244,12 +244,12 @@ std::vector<TraitsDataPtr> hostApi::Manager::resolve(
 // Multi variant
 std::vector<std::variant<BatchElementError, TraitsDataPtr>> hostApi::Manager::resolve(
     const EntityReferences &entityReferences, const trait::TraitSet &traitSet,
-    const ContextConstPtr &context,
+    const access::ResolveAccess resolveAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Variant &errorPolicyTag) {
   std::vector<std::variant<BatchElementError, TraitsDataPtr>> resolveResult;
   resolveResult.resize(entityReferences.size());
   resolve(
-      entityReferences, traitSet, context,
+      entityReferences, traitSet, resolveAccess, context,
       [&resolveResult](std::size_t index, TraitsDataPtr data) {
         resolveResult[index] = std::move(data);
       },
@@ -261,36 +261,41 @@ std::vector<std::variant<BatchElementError, TraitsDataPtr>> hostApi::Manager::re
 }
 
 void Manager::defaultEntityReference(const trait::TraitSets &traitSets,
+                                     const access::DefaultEntityAccess defaultEntityAccess,
                                      const ContextConstPtr &context,
                                      const DefaultEntityReferenceSuccessCallback &successCallback,
                                      const BatchElementErrorCallback &errorCallback) {
-  managerInterface_->defaultEntityReference(traitSets, context, hostSession_, successCallback,
-                                            errorCallback);
+  managerInterface_->defaultEntityReference(traitSets, defaultEntityAccess, context, hostSession_,
+                                            successCallback, errorCallback);
 }
 
 void Manager::getWithRelationship(const EntityReferences &entityReferences,
                                   const TraitsDataPtr &relationshipTraitsData,
+                                  const access::RelationsAccess relationsAccess,
                                   const ContextConstPtr &context,
                                   const Manager::RelationshipSuccessCallback &successCallback,
                                   const Manager::BatchElementErrorCallback &errorCallback,
                                   const trait::TraitSet &resultTraitSet) {
   managerInterface_->getWithRelationship(entityReferences, relationshipTraitsData, resultTraitSet,
-                                         context, hostSession_, successCallback, errorCallback);
+                                         relationsAccess, context, hostSession_, successCallback,
+                                         errorCallback);
 }
 
 void Manager::getWithRelationships(const EntityReference &entityReference,
                                    const trait::TraitsDatas &relationshipTraitsDatas,
+                                   const access::RelationsAccess relationsAccess,
                                    const ContextConstPtr &context,
                                    const Manager::RelationshipSuccessCallback &successCallback,
                                    const Manager::BatchElementErrorCallback &errorCallback,
                                    const trait::TraitSet &resultTraitSet) {
   managerInterface_->getWithRelationships(entityReference, relationshipTraitsDatas, resultTraitSet,
-                                          context, hostSession_, successCallback, errorCallback);
+                                          relationsAccess, context, hostSession_, successCallback,
+                                          errorCallback);
 }
 
 void Manager::getWithRelationshipPaged(
     const EntityReferences &entityReferences, const TraitsDataPtr &relationshipTraitsData,
-    size_t pageSize, const ContextConstPtr &context,
+    size_t pageSize, const access::RelationsAccess relationsAccess, const ContextConstPtr &context,
     const Manager::PagedRelationshipSuccessCallback &successCallback,
     const Manager::BatchElementErrorCallback &errorCallback,
     const trait::TraitSet &resultTraitSet) {
@@ -310,14 +315,14 @@ void Manager::getWithRelationshipPaged(
         auto pager = hostApi::EntityReferencePager::make(std::move(pagerInterface), hostSession);
         successCallback(idx, std::move(pager));
       };
-  managerInterface_->getWithRelationshipPaged(entityReferences, relationshipTraitsData,
-                                              resultTraitSet, pageSize, context, hostSession_,
-                                              convertingPagerSuccessCallback, errorCallback);
+  managerInterface_->getWithRelationshipPaged(
+      entityReferences, relationshipTraitsData, resultTraitSet, pageSize, relationsAccess, context,
+      hostSession_, convertingPagerSuccessCallback, errorCallback);
 }
 
 void Manager::getWithRelationshipsPaged(
     const EntityReference &entityReference, const trait::TraitsDatas &relationshipTraitsDatas,
-    size_t pageSize, const ContextConstPtr &context,
+    size_t pageSize, const access::RelationsAccess relationsAccess, const ContextConstPtr &context,
     const Manager::PagedRelationshipSuccessCallback &successCallback,
     const Manager::BatchElementErrorCallback &errorCallback,
     const trait::TraitSet &resultTraitSet) {
@@ -337,13 +342,15 @@ void Manager::getWithRelationshipsPaged(
         auto pager = hostApi::EntityReferencePager::make(std::move(pagerInterface), hostSession);
         successCallback(idx, std::move(pager));
       };
-  managerInterface_->getWithRelationshipsPaged(entityReference, relationshipTraitsDatas,
-                                               resultTraitSet, pageSize, context, hostSession_,
-                                               convertingPagerSuccessCallback, errorCallback);
+  managerInterface_->getWithRelationshipsPaged(
+      entityReference, relationshipTraitsDatas, resultTraitSet, pageSize, relationsAccess, context,
+      hostSession_, convertingPagerSuccessCallback, errorCallback);
 }
 
 void Manager::preflight(const EntityReferences &entityReferences,
-                        const trait::TraitsDatas &traitsHints, const ContextConstPtr &context,
+                        const trait::TraitsDatas &traitsHints,
+                        const access::PublishingAccess publishingAccess,
+                        const ContextConstPtr &context,
                         const PreflightSuccessCallback &successCallback,
                         const BatchElementErrorCallback &errorCallback) {
   if (entityReferences.size() != traitsHints.size()) {
@@ -354,17 +361,17 @@ void Manager::preflight(const EntityReferences &entityReferences,
     message += " traits hints.";
     throw std::out_of_range{message};
   }
-  managerInterface_->preflight(entityReferences, traitsHints, context, hostSession_,
-                               successCallback, errorCallback);
+  managerInterface_->preflight(entityReferences, traitsHints, publishingAccess, context,
+                               hostSession_, successCallback, errorCallback);
 }
 
 EntityReference Manager::preflight(
     const EntityReference &entityReference, const TraitsDataPtr &traitsHint,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const Manager::BatchElementErrorPolicyTag::Exception &errorPolicyTag) {
   EntityReference result{""};
   preflight(
-      {entityReference}, {traitsHint}, context,
+      {entityReference}, {traitsHint}, publishingAccess, context,
       [&result]([[maybe_unused]] std::size_t index, EntityReference preflightedRef) {
         result = std::move(preflightedRef);
       },
@@ -377,11 +384,11 @@ EntityReference Manager::preflight(
 
 std::variant<BatchElementError, EntityReference> Manager::preflight(
     const EntityReference &entityReference, const TraitsDataPtr &traitsHint,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const Manager::BatchElementErrorPolicyTag::Variant &errorPolicyTag) {
   std::variant<BatchElementError, EntityReference> result;
   preflight(
-      {entityReference}, {traitsHint}, context,
+      {entityReference}, {traitsHint}, publishingAccess, context,
       [&result]([[maybe_unused]] std::size_t index, EntityReference preflightedRef) {
         result = std::move(preflightedRef);
       },
@@ -394,13 +401,13 @@ std::variant<BatchElementError, EntityReference> Manager::preflight(
 
 EntityReferences Manager::preflight(
     const EntityReferences &entityReferences, const trait::TraitsDatas &traitsHints,
-    const ContextConstPtr &context,
+    access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const Manager::BatchElementErrorPolicyTag::Exception &errorPolicyTag) {
   EntityReferences results;
   results.resize(entityReferences.size(), EntityReference{""});
 
   preflight(
-      entityReferences, traitsHints, context,
+      entityReferences, traitsHints, publishingAccess, context,
       [&results](std::size_t index, EntityReference preflightedRef) {
         results[index] = std::move(preflightedRef);
       },
@@ -414,12 +421,12 @@ EntityReferences Manager::preflight(
 
 std::vector<std::variant<BatchElementError, EntityReference>> Manager::preflight(
     const EntityReferences &entityReferences, const trait::TraitsDatas &traitsHints,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const Manager::BatchElementErrorPolicyTag::Variant &errorPolicyTag) {
   std::vector<std::variant<BatchElementError, EntityReference>> results;
   results.resize(entityReferences.size());
   preflight(
-      entityReferences, traitsHints, context,
+      entityReferences, traitsHints, publishingAccess, context,
       [&results](std::size_t index, EntityReference entityReference) {
         results[index] = std::move(entityReference);
       },
@@ -432,6 +439,7 @@ std::vector<std::variant<BatchElementError, EntityReference>> Manager::preflight
 
 void Manager::register_(const EntityReferences &entityReferences,
                         const trait::TraitsDatas &entityTraitsDatas,
+                        const access::PublishingAccess publishingAccess,
                         const ContextConstPtr &context,
                         const RegisterSuccessCallback &successCallback,
                         const BatchElementErrorCallback &errorCallback) {
@@ -443,18 +451,18 @@ void Manager::register_(const EntityReferences &entityReferences,
     message += " traits datas.";
     throw std::out_of_range{message};
   }
-  return managerInterface_->register_(entityReferences, entityTraitsDatas, context, hostSession_,
-                                      successCallback, errorCallback);
+  return managerInterface_->register_(entityReferences, entityTraitsDatas, publishingAccess,
+                                      context, hostSession_, successCallback, errorCallback);
 }
 
 // Singular Except
 EntityReference hostApi::Manager::register_(
     const EntityReference &entityReference, const TraitsDataPtr &entityTraitsData,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Exception &errorPolicyTag) {
   EntityReference result("");
   register_(
-      {entityReference}, {entityTraitsData}, context,
+      {entityReference}, {entityTraitsData}, publishingAccess, context,
       [&result]([[maybe_unused]] std::size_t index, EntityReference registeredRef) {
         result = std::move(registeredRef);
       },
@@ -468,11 +476,11 @@ EntityReference hostApi::Manager::register_(
 // Singular variant
 std::variant<BatchElementError, EntityReference> hostApi::Manager::register_(
     const EntityReference &entityReference, const TraitsDataPtr &entityTraitsData,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Variant &errorPolicyTag) {
   std::variant<BatchElementError, EntityReference> result;
   register_(
-      {entityReference}, {entityTraitsData}, context,
+      {entityReference}, {entityTraitsData}, publishingAccess, context,
       [&result]([[maybe_unused]] std::size_t index, EntityReference registeredRef) {
         result = std::move(registeredRef);
       },
@@ -486,13 +494,13 @@ std::variant<BatchElementError, EntityReference> hostApi::Manager::register_(
 // Multi except
 std::vector<EntityReference> hostApi::Manager::register_(
     const EntityReferences &entityReferences, const trait::TraitsDatas &entityTraitsDatas,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Exception &errorPolicyTag) {
   std::vector<EntityReference> result;
   result.resize(entityReferences.size(), EntityReference{""});
 
   register_(
-      entityReferences, entityTraitsDatas, context,
+      entityReferences, entityTraitsDatas, publishingAccess, context,
       [&result](std::size_t index, EntityReference registeredRef) {
         result[index] = std::move(registeredRef);
       },
@@ -507,12 +515,12 @@ std::vector<EntityReference> hostApi::Manager::register_(
 // Multi variant
 std::vector<std::variant<BatchElementError, EntityReference>> hostApi::Manager::register_(
     const EntityReferences &entityReferences, const trait::TraitsDatas &entityTraitsDatas,
-    const ContextConstPtr &context,
+    const access::PublishingAccess publishingAccess, const ContextConstPtr &context,
     [[maybe_unused]] const BatchElementErrorPolicyTag::Variant &errorPolicyTag) {
   std::vector<std::variant<BatchElementError, EntityReference>> result;
   result.resize(entityReferences.size());
   register_(
-      entityReferences, entityTraitsDatas, context,
+      entityReferences, entityTraitsDatas, publishingAccess, context,
       [&result](std::size_t index, EntityReference registeredRef) {
         result[index] = std::move(registeredRef);
       },
diff --git a/src/openassetio-core/src/managerApi/ManagerInterface.cpp b/src/openassetio-core/src/managerApi/ManagerInterface.cpp
index ee1e52996..74d0db1a4 100644
--- a/src/openassetio-core/src/managerApi/ManagerInterface.cpp
+++ b/src/openassetio-core/src/managerApi/ManagerInterface.cpp
@@ -60,7 +60,9 @@ EntityReference ManagerInterface::createEntityReference(Str entityReferenceStrin
 }
 
 void ManagerInterface::defaultEntityReference(
-    const trait::TraitSets& traitSets, [[maybe_unused]] const ContextConstPtr& context,
+    const trait::TraitSets& traitSets,
+    [[maybe_unused]] const access::DefaultEntityAccess defaultEntityAccess,
+    [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     [[maybe_unused]] const DefaultEntityReferenceSuccessCallback& successCallback,
     const BatchElementErrorCallback& errorCallback) {
@@ -76,6 +78,7 @@ void ManagerInterface::getWithRelationship(
     const EntityReferences& entityReferences,
     [[maybe_unused]] const TraitsDataPtr& relationshipTraitsData,
     [[maybe_unused]] const trait::TraitSet& resultTraitSet,
+    [[maybe_unused]] const access::RelationsAccess relationsAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     const ManagerInterface::RelationshipSuccessCallback& successCallback,
@@ -88,6 +91,7 @@ void ManagerInterface::getWithRelationships(
     [[maybe_unused]] const EntityReference& entityReference,
     const trait::TraitsDatas& relationshipTraitsDatas,
     [[maybe_unused]] const trait::TraitSet& resultTraitSet,
+    [[maybe_unused]] const access::RelationsAccess relationsAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     const ManagerInterface::RelationshipSuccessCallback& successCallback,
@@ -117,6 +121,7 @@ void ManagerInterface::getWithRelationshipPaged(
     const EntityReferences& entityReferences,
     [[maybe_unused]] const TraitsDataPtr& relationshipTraitsData,
     [[maybe_unused]] const trait::TraitSet& resultTraitSet, [[maybe_unused]] size_t pageSize,
+    [[maybe_unused]] const access::RelationsAccess relationsAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     const PagedRelationshipSuccessCallback& successCallback,
@@ -132,6 +137,7 @@ void ManagerInterface::getWithRelationshipsPaged(
     [[maybe_unused]] const EntityReference& entityReference,
     const trait::TraitsDatas& relationshipTraitsDatas,
     [[maybe_unused]] const trait::TraitSet& resultTraitSet, [[maybe_unused]] size_t pageSize,
+    [[maybe_unused]] const access::RelationsAccess relationsAccess,
     [[maybe_unused]] const ContextConstPtr& context,
     [[maybe_unused]] const HostSessionPtr& hostSession,
     const PagedRelationshipSuccessCallback& successCallback,
diff --git a/src/openassetio-python/cmodule/CMakeLists.txt b/src/openassetio-python/cmodule/CMakeLists.txt
index b4228e5b1..63e7e7d3a 100644
--- a/src/openassetio-python/cmodule/CMakeLists.txt
+++ b/src/openassetio-python/cmodule/CMakeLists.txt
@@ -41,6 +41,7 @@ target_sources(
     openassetio-python-module
     PRIVATE
     src/_openassetio.cpp
+    src/accessBinding.cpp
     src/constantsBinding.cpp
     src/BatchElementErrorBinding.cpp
     src/ContextBinding.cpp
diff --git a/src/openassetio-python/cmodule/src/ContextBinding.cpp b/src/openassetio-python/cmodule/src/ContextBinding.cpp
index 97fb79423..1b12f369d 100644
--- a/src/openassetio-python/cmodule/src/ContextBinding.cpp
+++ b/src/openassetio-python/cmodule/src/ContextBinding.cpp
@@ -20,25 +20,13 @@ void registerContext(const py::module& mod) {
 
   py::class_<Context, ContextPtr> context{mod, "Context", py::is_final()};
 
-  py::enum_<Context::Access>{context, "Access"}
-      .value("kRead", Context::Access::kRead)
-      .value("kWrite", Context::Access::kWrite)
-      .value("kCreateRelated", Context::Access::kCreateRelated)
-      .value("kUnknown", Context::Access::kUnknown);
-
-  context.def_readonly_static("kAccessNames", &Context::kAccessNames);
-
   context
       .def(py::init(RetainCommonPyArgs::forFn<&Context::make>()),
-           py::arg_v("access", Context::Access::kUnknown), py::arg_v("locale", TraitsDataPtr{}),
-           py::arg_v("managerState", ManagerStateBasePtr{}))
-      .def_readwrite("access", &Context::access)
+           py::arg_v("locale", TraitsDataPtr{}), py::arg_v("managerState", ManagerStateBasePtr{}))
       .def_readwrite("locale", &Context::locale)
       .def_property(
           "managerState", [](const Context& self) { return self.managerState; },
           [](Context& self, PyRetainingManagerStateBasePtr managerState) {
             self.managerState = std::move(managerState);
-          })
-      .def("isForRead", &Context::isForRead)
-      .def("isForWrite", &Context::isForWrite);
+          });
 }
diff --git a/src/openassetio-python/cmodule/src/_openassetio.cpp b/src/openassetio-python/cmodule/src/_openassetio.cpp
index 5e8206fb7..d6cb870c0 100644
--- a/src/openassetio-python/cmodule/src/_openassetio.cpp
+++ b/src/openassetio-python/cmodule/src/_openassetio.cpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2013-2022 The Foundry Visionmongers Ltd
+// Copyright 2013-2023 The Foundry Visionmongers Ltd
 
 #include "_openassetio.hpp"
 
@@ -12,11 +12,13 @@ PYBIND11_MODULE(_openassetio, mod) {
   // pybind11 will properly report type names in its docstring/error
   // output.
 
+  const py::module access = mod.def_submodule("access");
   const py::module managerApi = mod.def_submodule("managerApi");
   const py::module hostApi = mod.def_submodule("hostApi");
   const py::module log = mod.def_submodule("log");
   const py::module constants = mod.def_submodule("constants");
 
+  registerAccess(access);
   registerConstants(constants);
   registerLoggerInterface(log);
   registerConsoleLogger(log);
diff --git a/src/openassetio-python/cmodule/src/_openassetio.hpp b/src/openassetio-python/cmodule/src/_openassetio.hpp
index 28a255740..836d03a78 100644
--- a/src/openassetio-python/cmodule/src/_openassetio.hpp
+++ b/src/openassetio-python/cmodule/src/_openassetio.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2013-2022 The Foundry Visionmongers Ltd
+// Copyright 2013-2023 The Foundry Visionmongers Ltd
 /**
  * Python binding bootstrap functions and typedefs.
  */
@@ -64,6 +64,9 @@ using RetainCommonPyArgs = openassetio::RetainPyArgs<
 /// Concise pybind alias.
 namespace py = pybind11;
 
+/// Register constants for use as dict keys.
+void registerAccess(const py::module& mod);
+
 /// Register constants for use as dict keys.
 void registerConstants(const py::module& mod);
 
diff --git a/src/openassetio-python/cmodule/src/accessBinding.cpp b/src/openassetio-python/cmodule/src/accessBinding.cpp
new file mode 100644
index 000000000..23060fbfc
--- /dev/null
+++ b/src/openassetio-python/cmodule/src/accessBinding.cpp
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright 2023 The Foundry Visionmongers Ltd
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include <openassetio/access.hpp>
+
+#include "_openassetio.hpp"
+
+void registerAccess(const py::module& mod) {
+  namespace access = openassetio::access;
+
+  py::enum_<access::PolicyAccess>{mod, "PolicyAccess"}
+      .value("kRead", access::PolicyAccess::kRead)
+      .value("kWrite", access::PolicyAccess::kWrite)
+      .value("kCreateRelated", access::PolicyAccess::kCreateRelated);
+
+  py::enum_<access::ResolveAccess>{mod, "ResolveAccess"}
+      .value("kRead", access::ResolveAccess::kRead)
+      .value("kWrite", access::ResolveAccess::kWrite);
+
+  py::enum_<access::PublishingAccess>{mod, "PublishingAccess"}
+      .value("kWrite", access::PublishingAccess::kWrite)
+      .value("kCreateRelated", access::PublishingAccess::kCreateRelated);
+
+  py::enum_<access::RelationsAccess>{mod, "RelationsAccess"}
+      .value("kRead", access::RelationsAccess::kRead)
+      .value("kWrite", access::RelationsAccess::kWrite)
+      .value("kCreateRelated", access::RelationsAccess::kCreateRelated);
+
+  py::enum_<access::DefaultEntityAccess>{mod, "DefaultEntityAccess"}
+      .value("kRead", access::DefaultEntityAccess::kRead)
+      .value("kWrite", access::DefaultEntityAccess::kWrite)
+      .value("kCreateRelated", access::DefaultEntityAccess::kCreateRelated);
+
+  mod.attr("kAccessNames") = access::kAccessNames;
+}
diff --git a/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp b/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp
index b3f79acfa..8ce2ccaa0 100644
--- a/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp
+++ b/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp
@@ -32,6 +32,7 @@ void validateTraitsDatas(const TraitsDatas& traitsDatas) {
 }  // namespace
 
 void registerManager(const py::module& mod) {
+  namespace access = openassetio::access;
   namespace trait = openassetio::trait;
   using openassetio::BatchElementError;
   using openassetio::ContextConstPtr;
@@ -70,7 +71,7 @@ void registerManager(const py::module& mod) {
       .def("initialize", &Manager::initialize, py::arg("managerSettings"))
       .def("flushCaches", &Manager::flushCaches)
       .def("managementPolicy", &Manager::managementPolicy, py::arg("traitSets"),
-           py::arg("context").none(false))
+           py::arg("policyAccess"), py::arg("context").none(false))
       .def("createContext", &Manager::createContext)
       .def("createChildContext", &Manager::createChildContext,
            py::arg("parentContext").none(false))
@@ -85,25 +86,27 @@ void registerManager(const py::module& mod) {
       .def("entityExists", &Manager::entityExists, py::arg("entityReferences"),
            py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"))
       .def("updateTerminology", &Manager::updateTerminology, py::arg("terms"))
+      .def(
+          "resolve",
+          py::overload_cast<const EntityReferences&, const trait::TraitSet&, access::ResolveAccess,
+                            const ContextConstPtr&, const Manager::ResolveSuccessCallback&,
+                            const Manager::BatchElementErrorCallback&>(&Manager::resolve),
+          py::arg("entityReferences"), py::arg("traitSet"), py::arg("resolveAccess"),
+          py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"))
       .def("resolve",
-           static_cast<void (Manager::*)(
-               const EntityReferences&, const trait::TraitSet&, const ContextConstPtr&,
-               const Manager::ResolveSuccessCallback&, const Manager::BatchElementErrorCallback&)>(
+           py::overload_cast<const EntityReference&, const trait::TraitSet&, access::ResolveAccess,
+                             const ContextConstPtr&,
+                             const Manager::BatchElementErrorPolicyTag::Exception&>(
                &Manager::resolve),
-           py::arg("entityReferences"), py::arg("traitSet"), py::arg("context").none(false),
-           py::arg("successCallback"), py::arg("errorCallback"))
-      .def("resolve",
-           static_cast<TraitsDataPtr (Manager::*)(
-               const EntityReference&, const trait::TraitSet&, const ContextConstPtr&,
-               const Manager::BatchElementErrorPolicyTag::Exception&)>(&Manager::resolve),
-           py::arg("entityReference"), py::arg("traitSet"), py::arg("context").none(false),
-           py::arg("errorPolicyTag"))
+           py::arg("entityReference"), py::arg("traitSet"), py::arg("resolveAccess"),
+           py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def("resolve",
-           static_cast<std::variant<BatchElementError, TraitsDataPtr> (Manager::*)(
-               const EntityReference&, const trait::TraitSet&, const ContextConstPtr&,
-               const Manager::BatchElementErrorPolicyTag::Variant&)>(&Manager::resolve),
-           py::arg("entityReference"), py::arg("traitSet"), py::arg("context").none(false),
-           py::arg("errorPolicyTag"))
+           py::overload_cast<const EntityReference&, const trait::TraitSet&, access::ResolveAccess,
+                             const ContextConstPtr&,
+                             const Manager::BatchElementErrorPolicyTag::Variant&>(
+               &Manager::resolve),
+           py::arg("entityReference"), py::arg("traitSet"), py::arg("resolveAccess"),
+           py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "resolve",
           // TODO(DF): Technically we shouldn't need this overload,
@@ -112,22 +115,26 @@ void registerManager(const py::module& mod) {
           // `py::arg("errorPolicyTag") = {}`. However, this causes a
           // memory leak in pybind11.
           [](Manager& self, const EntityReference& entityReference,
-             const trait::TraitSet& traitSet, const ContextConstPtr& context) {
-            return self.resolve(entityReference, traitSet, context);
+             const trait::TraitSet& traitSet, const access::ResolveAccess access,
+             const ContextConstPtr& context) {
+            return self.resolve(entityReference, traitSet, access, context);
           },
-          py::arg("entityReference"), py::arg("traitSet"), py::arg("context").none(false))
+          py::arg("entityReference"), py::arg("traitSet"), py::arg("resolveAccess"),
+          py::arg("context").none(false))
       .def("resolve",
-           static_cast<std::vector<TraitsDataPtr> (Manager::*)(
-               const EntityReferences&, const trait::TraitSet&, const ContextConstPtr&,
-               const Manager::BatchElementErrorPolicyTag::Exception&)>(&Manager::resolve),
-           py::arg("entityReferences"), py::arg("traitSet"), py::arg("context").none(false),
-           py::arg("errorPolicyTag"))
+           py::overload_cast<const EntityReferences&, const trait::TraitSet&,
+                             access::ResolveAccess, const ContextConstPtr&,
+                             const Manager::BatchElementErrorPolicyTag::Exception&>(
+               &Manager::resolve),
+           py::arg("entityReferences"), py::arg("traitSet"), py::arg("resolveAccess"),
+           py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def("resolve",
-           static_cast<std::vector<std::variant<BatchElementError, TraitsDataPtr>> (Manager::*)(
-               const EntityReferences&, const trait::TraitSet&, const ContextConstPtr&,
-               const Manager::BatchElementErrorPolicyTag::Variant&)>(&Manager::resolve),
-           py::arg("entityReferences"), py::arg("traitSet"), py::arg("context").none(false),
-           py::arg("errorPolicyTag"))
+           py::overload_cast<const EntityReferences&, const trait::TraitSet&,
+                             access::ResolveAccess, const ContextConstPtr&,
+                             const Manager::BatchElementErrorPolicyTag::Variant&>(
+               &Manager::resolve),
+           py::arg("entityReferences"), py::arg("traitSet"), py::arg("resolveAccess"),
+           py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "resolve",
           // TODO(DF): Technically we shouldn't need this overload,
@@ -136,176 +143,195 @@ void registerManager(const py::module& mod) {
           // `py::arg("errorPolicyTag") = {}`. However, this causes a
           // memory leak in pybind11.
           [](Manager& self, const EntityReferences& entityReferences,
-             const trait::TraitSet& traitSet, const ContextConstPtr& context) {
-            return self.resolve(entityReferences, traitSet, context);
+             const trait::TraitSet& traitSet, const access::ResolveAccess resolveAccess,
+             const ContextConstPtr& context) {
+            return self.resolve(entityReferences, traitSet, resolveAccess, context);
           },
-          py::arg("entityReferences"), py::arg("traitSet"), py::arg("context").none(false))
+          py::arg("entityReferences"), py::arg("traitSet"), py::arg("resolveAccess"),
+          py::arg("context").none(false))
       .def("defaultEntityReference", &Manager::defaultEntityReference, py::arg("traitSets"),
-           py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"))
+           py::arg("defaultEntityAccess"), py::arg("context").none(false),
+           py::arg("successCallback"), py::arg("errorCallback"))
       .def("getWithRelationship", &Manager::getWithRelationship, py::arg("entityReferences"),
-           py::arg("relationshipTraitsData").none(false), py::arg("context").none(false),
-           py::arg("successCallback"), py::arg("errorCallback"),
+           py::arg("relationshipTraitsData").none(false), py::arg("relationsAccess"),
+           py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"),
            py::arg("resultTraitSet") = trait::TraitSet{})
       .def(
           "getWithRelationships",
           [](Manager& self, const EntityReference& entityReference,
-             const trait::TraitsDatas& relationshipTraitsDatas, const ContextConstPtr& context,
+             const trait::TraitsDatas& relationshipTraitsDatas,
+             const access::RelationsAccess relationsAccess, const ContextConstPtr& context,
              const Manager::RelationshipSuccessCallback& successCallback,
              const Manager::BatchElementErrorCallback& errorCallback,
              const trait::TraitSet& resultTraitSet) {
             validateTraitsDatas(relationshipTraitsDatas);
-            self.getWithRelationships(entityReference, relationshipTraitsDatas, context,
-                                      successCallback, errorCallback, resultTraitSet);
+            self.getWithRelationships(entityReference, relationshipTraitsDatas, relationsAccess,
+                                      context, successCallback, errorCallback, resultTraitSet);
           },
           py::arg("entityReference"), py::arg("relationshipTraitsDatas"),
-          py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"),
+          py::arg("context").none(false), py::arg("context").none(false),
+          py::arg("successCallback"), py::arg("errorCallback"),
           py::arg("resultTraitSet") = trait::TraitSet{})
-      .def("getWithRelationshipPaged",
-           py::overload_cast<const EntityReferences&, const TraitsDataPtr&, std::size_t,
-                             const ContextConstPtr&,
-                             const Manager::PagedRelationshipSuccessCallback&,
-                             const Manager::BatchElementErrorCallback&, const trait::TraitSet&>(
-               &Manager::getWithRelationshipPaged),
+      .def("getWithRelationshipPaged", &Manager::getWithRelationshipPaged,
            py::arg("entityReferences"), py::arg("relationshipTraitsData").none(false),
-           py::arg("pageSize"), py::arg("context").none(false), py::arg("successCallback"),
-           py::arg("errorCallback"), py::arg("resultTraitSet") = trait::TraitSet{})
+           py::arg("pageSize"), py::arg("relationsAccess"), py::arg("context").none(false),
+           py::arg("successCallback"), py::arg("errorCallback"),
+           py::arg("resultTraitSet") = trait::TraitSet{})
       .def(
           "getWithRelationshipsPaged",
           [](Manager& self, const EntityReference& entityReference,
              const trait::TraitsDatas& relationshipTraitsDatas, size_t pageSize,
-             const ContextConstPtr& context,
+             const access::RelationsAccess relationsAccess, const ContextConstPtr& context,
              const Manager::PagedRelationshipSuccessCallback& successCallback,
              const Manager::BatchElementErrorCallback& errorCallback,
              const trait::TraitSet& resultTraitSet) {
             validateTraitsDatas(relationshipTraitsDatas);
             self.getWithRelationshipsPaged(entityReference, relationshipTraitsDatas, pageSize,
-                                           context, successCallback, errorCallback,
-                                           resultTraitSet);
+                                           relationsAccess, context, successCallback,
+                                           errorCallback, resultTraitSet);
           },
           py::arg("entityReference"), py::arg("relationshipTraitsDatas"), py::arg("pageSize"),
-          py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"),
-          py::arg("resultTraitSet") = trait::TraitSet{})
+          py::arg("relationsAccess"), py::arg("context").none(false), py::arg("successCallback"),
+          py::arg("errorCallback"), py::arg("resultTraitSet") = trait::TraitSet{})
       .def(
           "preflight",
           [](Manager& self, const EntityReferences& entityReferences,
-             const trait::TraitsDatas& traitsHints, const ContextConstPtr& context,
+             const trait::TraitsDatas& traitsHints,
+             const access::PublishingAccess publishingAccess, const ContextConstPtr& context,
              const Manager::PreflightSuccessCallback& successCallback,
              const Manager::BatchElementErrorCallback& errorCallback) {
             validateTraitsDatas(traitsHints);
-            self.preflight(entityReferences, traitsHints, context, successCallback, errorCallback);
+            self.preflight(entityReferences, traitsHints, publishingAccess, context,
+                           successCallback, errorCallback);
           },
-          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("context").none(false),
-          py::arg("successCallback"), py::arg("errorCallback"))
+          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("publishAccess"),
+          py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"))
       .def("preflight",
-           py::overload_cast<const EntityReference&, const TraitsDataPtr&, const ContextConstPtr&,
+           py::overload_cast<const EntityReference&, const TraitsDataPtr&,
+                             access::PublishingAccess, const ContextConstPtr&,
                              const Manager::BatchElementErrorPolicyTag::Exception&>(
                &Manager::preflight),
-           py::arg("entityReference"), py::arg("traitsHint").none(false),
+           py::arg("entityReference"), py::arg("traitsHint").none(false), py::arg("publishAccess"),
            py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def("preflight",
-           py::overload_cast<const EntityReference&, const TraitsDataPtr&, const ContextConstPtr&,
+           py::overload_cast<const EntityReference&, const TraitsDataPtr&,
+                             access::PublishingAccess, const ContextConstPtr&,
                              const Manager::BatchElementErrorPolicyTag::Variant&>(
                &Manager::preflight),
-           py::arg("entityReference"), py::arg("traitsHint").none(false),
+           py::arg("entityReference"), py::arg("traitsHint").none(false), py::arg("publishAccess"),
            py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "preflight",
           [](Manager& self, const EntityReference& entityReference,
-             const TraitsDataPtr& traitsHint, const ContextConstPtr& context) {
-            return self.preflight(entityReference, traitsHint, context);
+             const TraitsDataPtr& traitsHint, const access::PublishingAccess publishingAccess,
+             const ContextConstPtr& context) {
+            return self.preflight(entityReference, traitsHint, publishingAccess, context);
           },
-          py::arg("entityReference"), py::arg("traitsHint").none(false),
+          py::arg("entityReference"), py::arg("traitsHint").none(false), py::arg("publishAccess"),
           py::arg("context").none(false))
       .def(
           "preflight",
           [](Manager& self, const EntityReferences& entityReferences,
-             const trait::TraitsDatas& traitsHints, const ContextConstPtr& context,
+             const trait::TraitsDatas& traitsHints,
+             const access::PublishingAccess publishingAccess, const ContextConstPtr& context,
              const Manager::BatchElementErrorPolicyTag::Exception& tag) {
             validateTraitsDatas(traitsHints);
-            return self.preflight(entityReferences, traitsHints, context, tag);
+            return self.preflight(entityReferences, traitsHints, publishingAccess, context, tag);
           },
-          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("context").none(false),
-          py::arg("errorPolicyTag"))
+          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("publishAccess"),
+          py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "preflight",
           [](Manager& self, const EntityReferences& entityReferences,
-             const trait::TraitsDatas& traitsHints, const ContextConstPtr& context,
+             const trait::TraitsDatas& traitsHints,
+             const access::PublishingAccess publishingAccess, const ContextConstPtr& context,
              const Manager::BatchElementErrorPolicyTag::Variant& tag) {
             validateTraitsDatas(traitsHints);
-            return self.preflight(entityReferences, traitsHints, context, tag);
+            return self.preflight(entityReferences, traitsHints, publishingAccess, context, tag);
           },
-          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("context").none(false),
-          py::arg("errorPolicyTag"))
+          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("publishAccess"),
+          py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "preflight",
           [](Manager& self, const EntityReferences& entityReferences,
-             const trait::TraitsDatas& traitsHints, const ContextConstPtr& context) {
+             const trait::TraitsDatas& traitsHints,
+             const access::PublishingAccess publishingAccess, const ContextConstPtr& context) {
             validateTraitsDatas(traitsHints);
-            return self.preflight(entityReferences, traitsHints, context);
+            return self.preflight(entityReferences, traitsHints, publishingAccess, context);
           },
-          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("context").none(false))
+          py::arg("entityReferences"), py::arg("traitsHints"), py::arg("publishAccess"),
+          py::arg("context").none(false))
       .def(
           "register",
           [](Manager& self, const EntityReferences& entityReferences,
-             const trait::TraitsDatas& entityTraitsDatas, const ContextConstPtr& context,
+             const trait::TraitsDatas& entityTraitsDatas,
+             const access::PublishingAccess publishingAccess, const ContextConstPtr& context,
              const Manager::RegisterSuccessCallback& successCallback,
              const Manager::BatchElementErrorCallback& errorCallback) {
             validateTraitsDatas(entityTraitsDatas);
-            self.register_(entityReferences, entityTraitsDatas, context, successCallback,
-                           errorCallback);
+            self.register_(entityReferences, entityTraitsDatas, publishingAccess, context,
+                           successCallback, errorCallback);
           },
-          py::arg("entityReferences"), py::arg("entityTraitsDatas"),
+          py::arg("entityReferences"), py::arg("entityTraitsDatas"), py::arg("publishAccess"),
           py::arg("context").none(false), py::arg("successCallback"), py::arg("errorCallback"))
 
       .def("register",
-           static_cast<EntityReference (Manager::*)(
-               const EntityReference&, const TraitsDataPtr&, const ContextConstPtr&,
-               const Manager::BatchElementErrorPolicyTag::Exception&)>(&Manager::register_),
+           py::overload_cast<const EntityReference&, const TraitsDataPtr&,
+                             access::PublishingAccess, const ContextConstPtr&,
+                             const Manager::BatchElementErrorPolicyTag::Exception&>(
+               &Manager::register_),
            py::arg("entityReference"), py::arg("entityTraitsData").none(false),
-           py::arg("context").none(false), py::arg("errorPolicyTag"))
+           py::arg("publishAccess"), py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def("register",
-           static_cast<std::variant<BatchElementError, EntityReference> (Manager::*)(
-               const EntityReference&, const TraitsDataPtr&, const ContextConstPtr&,
-               const Manager::BatchElementErrorPolicyTag::Variant&)>(&Manager::register_),
+           py::overload_cast<const EntityReference&, const TraitsDataPtr&,
+                             access::PublishingAccess, const ContextConstPtr&,
+                             const Manager::BatchElementErrorPolicyTag::Variant&>(
+               &Manager::register_),
            py::arg("entityReference"), py::arg("entityTraitsData").none(false),
-           py::arg("context").none(false), py::arg("errorPolicyTag"))
+           py::arg("publishAccess"), py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "register",
           [](Manager& self, const EntityReference& entityReference,
-             const TraitsDataPtr& entityTraitsData, const ContextConstPtr& context) {
-            return self.register_(entityReference, entityTraitsData, context);
+             const TraitsDataPtr& entityTraitsData,
+             const access::PublishingAccess publishingAccess, const ContextConstPtr& context) {
+            return self.register_(entityReference, entityTraitsData, publishingAccess, context);
           },
           py::arg("entityReference"), py::arg("entityTraitsData").none(false),
-          py::arg("context").none(false))
+          py::arg("publishAccess"), py::arg("context").none(false))
       .def(
           "register",
           [](Manager& self, const EntityReferences& entityReferences,
-             const TraitsDatas& entityTraitsDatas, const ContextConstPtr& context,
+             const TraitsDatas& entityTraitsDatas, const access::PublishingAccess publishingAccess,
+             const ContextConstPtr& context,
              const Manager::BatchElementErrorPolicyTag::Exception& errorPolicyTag) {
             validateTraitsDatas(entityTraitsDatas);
-            return self.register_(entityReferences, entityTraitsDatas, context, errorPolicyTag);
+            return self.register_(entityReferences, entityTraitsDatas, publishingAccess, context,
+                                  errorPolicyTag);
           },
-          py::arg("entityReferences"), py::arg("entityTraitsDatas"),
+          py::arg("entityReferences"), py::arg("entityTraitsDatas"), py::arg("publishAccess"),
           py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "register",
           [](Manager& self, const EntityReferences& entityReferences,
-             const TraitsDatas& entityTraitsDatas, const ContextConstPtr& context,
+             const TraitsDatas& entityTraitsDatas, const access::PublishingAccess publishingAccess,
+             const ContextConstPtr& context,
              const Manager::BatchElementErrorPolicyTag::Variant& errorPolicyTag) {
             validateTraitsDatas(entityTraitsDatas);
-            return self.register_(entityReferences, entityTraitsDatas, context, errorPolicyTag);
+            return self.register_(entityReferences, entityTraitsDatas, publishingAccess, context,
+                                  errorPolicyTag);
           },
-          py::arg("entityReferences"), py::arg("entityTraitsDatas"),
+          py::arg("entityReferences"), py::arg("entityTraitsDatas"), py::arg("publishAccess"),
           py::arg("context").none(false), py::arg("errorPolicyTag"))
       .def(
           "register",
           [](Manager& self, const EntityReferences& entityReferences,
-             const TraitsDatas& entityTraitsDatas, const ContextConstPtr& context) {
+             const TraitsDatas& entityTraitsDatas, const access::PublishingAccess publishingAccess,
+             const ContextConstPtr& context) {
             validateTraitsDatas(entityTraitsDatas);
-            return self.register_(entityReferences, entityTraitsDatas, context);
+            return self.register_(entityReferences, entityTraitsDatas, publishingAccess, context);
           },
           py::arg("entityReference"), py::arg("entityTraitsData").none(false),
-          py::arg("context").none(false))
+          py::arg("publishAccess"), py::arg("context").none(false))
       // @todo Remove one C++ API matches Python, and we remove ManagerFactory.py
       .def("_interface", &Manager::_interface)
       .def("_hostSession", &Manager::_hostSession);
diff --git a/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp b/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp
index a7a9a5512..171f4e495 100644
--- a/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp
+++ b/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp
@@ -55,10 +55,10 @@ struct PyManagerInterface : ManagerInterface {
   }
 
   [[nodiscard]] trait::TraitsDatas managementPolicy(
-      const trait::TraitSets& traitSets, const ContextConstPtr& context,
-      const HostSessionPtr& hostSession) const override {
+      const trait::TraitSets& traitSets, access::PolicyAccess policyAccess,
+      const ContextConstPtr& context, const HostSessionPtr& hostSession) const override {
     PYBIND11_OVERRIDE_PURE(trait::TraitsDatas, ManagerInterface, managementPolicy, traitSets,
-                           context, hostSession);
+                           policyAccess, context, hostSession);
   }
 
   ManagerStateBasePtr createState(const HostSessionPtr& hostSession) override {
@@ -102,85 +102,89 @@ struct PyManagerInterface : ManagerInterface {
   }
 
   void resolve(const EntityReferences& entityReferences, const trait::TraitSet& traitSet,
-               const ContextConstPtr& context, const HostSessionPtr& hostSession,
-               const ResolveSuccessCallback& successCallback,
+               const access::ResolveAccess resolveAccess, const ContextConstPtr& context,
+               const HostSessionPtr& hostSession, const ResolveSuccessCallback& successCallback,
                const BatchElementErrorCallback& errorCallback) override {
-    PYBIND11_OVERRIDE_PURE(void, ManagerInterface, resolve, entityReferences, traitSet, context,
-                           hostSession, successCallback, errorCallback);
+    PYBIND11_OVERRIDE_PURE(void, ManagerInterface, resolve, entityReferences, traitSet,
+                           resolveAccess, context, hostSession, successCallback, errorCallback);
   }
 
-  void defaultEntityReference(const trait::TraitSets& traitSets, const ContextConstPtr& context,
-                              const HostSessionPtr& hostSession,
+  void defaultEntityReference(const trait::TraitSets& traitSets,
+                              const access::DefaultEntityAccess defaultEntityAccess,
+                              const ContextConstPtr& context, const HostSessionPtr& hostSession,
                               const DefaultEntityReferenceSuccessCallback& successCallback,
                               const BatchElementErrorCallback& errorCallback) override {
-    PYBIND11_OVERRIDE(void, ManagerInterface, defaultEntityReference, traitSets, context,
-                      hostSession, successCallback, errorCallback);
+    PYBIND11_OVERRIDE(void, ManagerInterface, defaultEntityReference, traitSets,
+                      defaultEntityAccess, context, hostSession, successCallback, errorCallback);
   }
 
   void getWithRelationship(
       const EntityReferences& entityReferences, const TraitsDataPtr& relationshipTraitsData,
-      const trait::TraitSet& resultTraitSet, const ContextConstPtr& context,
-      const HostSessionPtr& hostSession,
+      const trait::TraitSet& resultTraitSet, const access::RelationsAccess relationsAccess,
+      const ContextConstPtr& context, const HostSessionPtr& hostSession,
       const ManagerInterface::RelationshipSuccessCallback& successCallback,
       const ManagerInterface::BatchElementErrorCallback& errorCallback) override {
     PYBIND11_OVERRIDE(void, ManagerInterface, getWithRelationship, entityReferences,
-                      relationshipTraitsData, resultTraitSet, context, hostSession,
-                      successCallback, errorCallback);
+                      relationshipTraitsData, resultTraitSet, relationsAccess, context,
+                      hostSession, successCallback, errorCallback);
   }
 
   void getWithRelationships(
       const EntityReference& entityReference, const trait::TraitsDatas& relationshipTraitsDatas,
-      const trait::TraitSet& resultTraitSet, const ContextConstPtr& context,
-      const HostSessionPtr& hostSession,
+      const trait::TraitSet& resultTraitSet, const access::RelationsAccess relationsAccess,
+      const ContextConstPtr& context, const HostSessionPtr& hostSession,
       const ManagerInterface::RelationshipSuccessCallback& successCallback,
       const ManagerInterface::BatchElementErrorCallback& errorCallback) override {
     PYBIND11_OVERRIDE(void, ManagerInterface, getWithRelationships, entityReference,
-                      relationshipTraitsDatas, resultTraitSet, context, hostSession,
-                      successCallback, errorCallback);
+                      relationshipTraitsDatas, resultTraitSet, relationsAccess, context,
+                      hostSession, successCallback, errorCallback);
   }
 
   void getWithRelationshipPaged(
       const EntityReferences& entityReferences, const TraitsDataPtr& relationshipTraitsData,
-      const trait::TraitSet& resultTraitSet, size_t pageSize, const ContextConstPtr& context,
+      const trait::TraitSet& resultTraitSet, size_t pageSize,
+      const access::RelationsAccess relationsAccess, const ContextConstPtr& context,
       const HostSessionPtr& hostSession,
       const ManagerInterface::PagedRelationshipSuccessCallback& successCallback,
       const ManagerInterface::BatchElementErrorCallback& errorCallback) override {
     OPENASSETIO_PYBIND11_OVERRIDE_ARGS(
         void, ManagerInterface, getWithRelationshipPaged,
-        (entityReferences, relationshipTraitsData, resultTraitSet, pageSize, context, hostSession,
-         successCallback, errorCallback),
-        entityReferences, relationshipTraitsData, resultTraitSet, pageSize, context, hostSession,
-        RetainCommonPyArgs::forFn(successCallback), errorCallback);
+        (entityReferences, relationshipTraitsData, resultTraitSet, pageSize, relationsAccess,
+         context, hostSession, successCallback, errorCallback),
+        entityReferences, relationshipTraitsData, resultTraitSet, pageSize, relationsAccess,
+        context, hostSession, RetainCommonPyArgs::forFn(successCallback), errorCallback);
   }
 
   void getWithRelationshipsPaged(
       const EntityReference& entityReference, const trait::TraitsDatas& relationshipTraitsDatas,
-      const trait::TraitSet& resultTraitSet, size_t pageSize, const ContextConstPtr& context,
+      const trait::TraitSet& resultTraitSet, size_t pageSize,
+      const access::RelationsAccess relationsAccess, const ContextConstPtr& context,
       const HostSessionPtr& hostSession,
       const ManagerInterface::PagedRelationshipSuccessCallback& successCallback,
       const ManagerInterface::BatchElementErrorCallback& errorCallback) override {
     OPENASSETIO_PYBIND11_OVERRIDE_ARGS(
         void, ManagerInterface, getWithRelationshipsPaged,
-        (entityReference, relationshipTraitsDatas, resultTraitSet, pageSize, context, hostSession,
-         successCallback, errorCallback),
-        entityReference, relationshipTraitsDatas, resultTraitSet, pageSize, context, hostSession,
-        RetainCommonPyArgs::forFn(successCallback), errorCallback);
+        (entityReference, relationshipTraitsDatas, resultTraitSet, pageSize, relationsAccess,
+         context, hostSession, successCallback, errorCallback),
+        entityReference, relationshipTraitsDatas, resultTraitSet, pageSize, relationsAccess,
+        context, hostSession, RetainCommonPyArgs::forFn(successCallback), errorCallback);
   }
 
   void preflight(const EntityReferences& entityReferences, const trait::TraitsDatas& traitsHints,
-                 const ContextConstPtr& context, const HostSessionPtr& hostSession,
+                 const access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+                 const HostSessionPtr& hostSession,
                  const PreflightSuccessCallback& successCallback,
                  const BatchElementErrorCallback& errorCallback) override {
     PYBIND11_OVERRIDE_PURE(void, ManagerInterface, preflight, entityReferences, traitsHints,
-                           context, hostSession, successCallback, errorCallback);
+                           publishingAccess, context, hostSession, successCallback, errorCallback);
   }
 
   void register_(const EntityReferences& entityReferences, const trait::TraitsDatas& traitsDatas,
-                 const ContextConstPtr& context, const HostSessionPtr& hostSession,
-                 const RegisterSuccessCallback& successCallback,
+                 const access::PublishingAccess publishingAccess, const ContextConstPtr& context,
+                 const HostSessionPtr& hostSession, const RegisterSuccessCallback& successCallback,
                  const BatchElementErrorCallback& errorCallback) override {
     PYBIND11_OVERRIDE_PURE(void, ManagerInterface, register, entityReferences, traitsDatas,
-                           context, hostSession, successCallback, errorCallback);
+                           publishingAccess, context, hostSession, successCallback, errorCallback);
   }
 
   // Hoist protected members
@@ -212,7 +216,7 @@ void registerManagerInterface(const py::module& mod) {
            py::arg("hostSession").none(false))
       .def("flushCaches", &ManagerInterface::flushCaches, py::arg("hostSession").none(false))
       .def("managementPolicy", &ManagerInterface::managementPolicy, py::arg("traitSets"),
-           py::arg("context").none(false), py::arg("hostSession").none(false))
+           py::arg("access"), py::arg("context").none(false), py::arg("hostSession").none(false))
       .def("createState", &ManagerInterface::createState, py::arg("hostSession").none(false))
       .def("createChildState", RetainCommonPyArgs::forFn<&ManagerInterface::createChildState>(),
            py::arg("parentState").none(false), py::arg("hostSession").none(false))
@@ -229,40 +233,40 @@ void registerManagerInterface(const py::module& mod) {
       .def("updateTerminology", &ManagerInterface::updateTerminology, py::arg("terms"),
            py::arg("hostSession").none(false))
       .def("resolve", &ManagerInterface::resolve, py::arg("entityReferences"), py::arg("traitSet"),
-           py::arg("context").none(false), py::arg("hostSession").none(false),
+           py::arg("access"), py::arg("context").none(false), py::arg("hostSession").none(false),
            py::arg("successCallback"), py::arg("errorCallback"))
       .def("defaultEntityReference", &ManagerInterface::defaultEntityReference,
-           py::arg("traitSets"), py::arg("context").none(false),
+           py::arg("traitSets"), py::arg("defaultEntityAccess"), py::arg("context").none(false),
            py::arg("hostSession").none(false), py::arg("successCallback"),
            py::arg("errorCallback"))
       .def("getWithRelationshipPaged", &ManagerInterface::getWithRelationshipPaged,
            py::arg("entityReferences"), py::arg("relationshipTraitsData").none(false),
-           py::arg("resultTraitSet"), py::arg("pageSize").none(false),
+           py::arg("resultTraitSet"), py::arg("pageSize").none(false), py::arg("relationsAccess"),
            py::arg("context").none(false), py::arg("hostSession").none(false),
            py::arg("successCallback"), py::arg("errorCallback"))
       .def("getWithRelationshipsPaged", &ManagerInterface::getWithRelationshipsPaged,
            py::arg("entityReference"), py::arg("relationshipTraitsDatas"),
-           py::arg("resultTraitSet"), py::arg("pageSize").none(false),
+           py::arg("resultTraitSet"), py::arg("pageSize").none(false), py::arg("relationsAccess"),
            py::arg("context").none(false), py::arg("hostSession").none(false),
            py::arg("successCallback"), py::arg("errorCallback"))
       .def("getWithRelationship", &ManagerInterface::getWithRelationship,
            py::arg("entityReferences"), py::arg("relationshipTraitsData").none(false),
-           py::arg("resultTraitSet"), py::arg("context").none(false),
+           py::arg("resultTraitSet"), py::arg("relationsAccess"), py::arg("context").none(false),
            py::arg("hostSession").none(false), py::arg("successCallback"),
            py::arg("errorCallback"))
       .def("getWithRelationships", &ManagerInterface::getWithRelationships,
            py::arg("entityReference"), py::arg("relationshipTraitsDatas"),
-           py::arg("resultTraitSet"), py::arg("context").none(false),
+           py::arg("resultTraitSet"), py::arg("relationsAccess"), py::arg("context").none(false),
            py::arg("hostSession").none(false), py::arg("successCallback"),
            py::arg("errorCallback"))
       .def("preflight", &ManagerInterface::preflight, py::arg("entityReferences"),
-           py::arg("traitsHints"), py::arg("context").none(false),
+           py::arg("traitsHints"), py::arg("publishingAccess"), py::arg("context").none(false),
            py::arg("hostSession").none(false), py::arg("successCallback"),
            py::arg("errorCallback"))
       .def("register", &ManagerInterface::register_, py::arg("entityReferences"),
-           py::arg("entityTraitsDatas"), py::arg("context").none(false),
-           py::arg("hostSession").none(false), py::arg("successCallback"),
-           py::arg("errorCallback"))
+           py::arg("entityTraitsDatas"), py::arg("publishingAccess"),
+           py::arg("context").none(false), py::arg("hostSession").none(false),
+           py::arg("successCallback"), py::arg("errorCallback"))
       .def("_createEntityReference", &PyManagerInterface::createEntityReference,
            py::arg("entityReferenceString"));
 }
diff --git a/src/openassetio-python/package/openassetio/access.py b/src/openassetio-python/package/openassetio/access.py
new file mode 100644
index 000000000..73fa8fb9e
--- /dev/null
+++ b/src/openassetio-python/package/openassetio/access.py
@@ -0,0 +1,29 @@
+#
+#   Copyright 2023 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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+"""
+@namespace openassetio.access
+Provides access mode related constants for use in API functions.
+"""
+
+from openassetio import _openassetio  # pylint: disable=no-name-in-module
+
+
+kAccessNames = _openassetio.access.kAccessNames
+PolicyAccess = _openassetio.access.PolicyAccess
+ResolveAccess = _openassetio.access.ResolveAccess
+PublishingAccess = _openassetio.access.PublishingAccess
+RelationsAccess = _openassetio.access.RelationsAccess
+DefaultEntityAccess = _openassetio.access.DefaultEntityAccess


Test patch:
diff --git a/src/openassetio-core-c/tests/hostApi/ManagerTest.cpp b/src/openassetio-core-c/tests/hostApi/ManagerTest.cpp
index 22371d9ea..4a3d8fd72 100644
--- a/src/openassetio-core-c/tests/hostApi/ManagerTest.cpp
+++ b/src/openassetio-core-c/tests/hostApi/ManagerTest.cpp
@@ -44,12 +44,12 @@ struct MockManagerInterface : trompeloeil::mock_interface<managerApi::ManagerInt
   IMPLEMENT_CONST_MOCK0(displayName);
   IMPLEMENT_CONST_MOCK0(info);
   IMPLEMENT_MOCK2(initialize);
-  IMPLEMENT_CONST_MOCK3(managementPolicy);
+  IMPLEMENT_CONST_MOCK4(managementPolicy);
   IMPLEMENT_CONST_MOCK2(isEntityReferenceString);
   IMPLEMENT_MOCK5(entityExists);
-  IMPLEMENT_MOCK6(resolve);
-  IMPLEMENT_MOCK6(preflight);
-  IMPLEMENT_MOCK6(register_);  // NOLINT(readability-identifier-naming)
+  IMPLEMENT_MOCK7(resolve);
+  IMPLEMENT_MOCK7(preflight);
+  IMPLEMENT_MOCK7(register_);  // NOLINT(readability-identifier-naming)
 };
 /**
  * Mock implementation of a HostInterface.
diff --git a/src/openassetio-core/tests/CMakeLists.txt b/src/openassetio-core/tests/CMakeLists.txt
index 6fc898990..c9f6529fb 100644
--- a/src/openassetio-core/tests/CMakeLists.txt
+++ b/src/openassetio-core/tests/CMakeLists.txt
@@ -46,7 +46,7 @@ target_link_libraries(
 add_custom_target(
     openassetio.internal.core-cpp-test
     COMMAND
-"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/\
+    "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/\
 $<TARGET_FILE_NAME:openassetio-core-cpp-test-exe>"
 )
 
diff --git a/src/openassetio-core/tests/ContextTest.cpp b/src/openassetio-core/tests/ContextTest.cpp
index af631b743..06ca068e3 100644
--- a/src/openassetio-core/tests/ContextTest.cpp
+++ b/src/openassetio-core/tests/ContextTest.cpp
@@ -12,7 +12,6 @@ OPENASSETIO_FWD_DECLARE(managerApi, ManagerStateBase)
 using openassetio::Context;
 
 SCENARIO("Context constructor is private") {
-  STATIC_REQUIRE_FALSE(
-      std::is_constructible_v<Context, Context::Access, openassetio::TraitsDataPtr,
-                              openassetio::managerApi::ManagerStateBasePtr>);
+  STATIC_REQUIRE_FALSE(std::is_constructible_v<Context, openassetio::TraitsDataPtr,
+                                               openassetio::managerApi::ManagerStateBasePtr>);
 }
diff --git a/src/openassetio-core/tests/hostApi/ManagerTest.cpp b/src/openassetio-core/tests/hostApi/ManagerTest.cpp
index cfda4e2d7..fb2388293 100644
--- a/src/openassetio-core/tests/hostApi/ManagerTest.cpp
+++ b/src/openassetio-core/tests/hostApi/ManagerTest.cpp
@@ -29,12 +29,12 @@ struct MockManagerInterface : trompeloeil::mock_interface<managerApi::ManagerInt
   IMPLEMENT_CONST_MOCK0(displayName);
   IMPLEMENT_CONST_MOCK0(info);
   IMPLEMENT_MOCK2(initialize);
-  IMPLEMENT_CONST_MOCK3(managementPolicy);
+  IMPLEMENT_CONST_MOCK4(managementPolicy);
   IMPLEMENT_CONST_MOCK2(isEntityReferenceString);
   IMPLEMENT_MOCK5(entityExists);
-  IMPLEMENT_MOCK6(resolve);
-  IMPLEMENT_MOCK6(preflight);
-  IMPLEMENT_MOCK6(register_);  // NOLINT(readability-identifier-naming)
+  IMPLEMENT_MOCK7(resolve);
+  IMPLEMENT_MOCK7(preflight);
+  IMPLEMENT_MOCK7(register_);  // NOLINT(readability-identifier-naming)
 };
 /**
  * Mock implementation of a HostInterface.
@@ -98,6 +98,7 @@ SCENARIO("Resolving entities") {
     auto& mockManagerInterface = fixture.mockManagerInterface;
     const auto& context = fixture.context;
     const auto& hostSession = fixture.hostSession;
+    const auto resolveAccess = openassetio::access::ResolveAccess::kRead;
 
     GIVEN("manager plugin successfully resolves a single entity reference") {
       const openassetio::EntityReference ref = openassetio::EntityReference{"testReference"};
@@ -107,21 +108,24 @@ SCENARIO("Resolving entities") {
       expected->addTrait("aTestTrait");
 
       // With success callback side effect
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(0, expected));
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(0, expected));
 
       WHEN("singular resolve is called with default errorPolicyTag") {
-        const openassetio::TraitsDataPtr actual = manager->resolve(ref, traits, context);
+        const openassetio::TraitsDataPtr actual =
+            manager->resolve(ref, traits, resolveAccess, context);
         THEN("returned TraitsData is as expected") { CHECK(expected.get() == actual.get()); }
       }
       WHEN("singular resolve is called with kException errorPolicyTag") {
-        const openassetio::TraitsDataPtr actual = manager->resolve(
-            ref, traits, context, hostApi::Manager::BatchElementErrorPolicyTag::kException);
+        const openassetio::TraitsDataPtr actual =
+            manager->resolve(ref, traits, resolveAccess, context,
+                             hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned TraitsData is as expected") { CHECK(expected.get() == actual.get()); }
       }
       WHEN("singular resolve is called with kVariant errorPolicyTag") {
         std::variant<openassetio::BatchElementError, openassetio::TraitsDataPtr> actual =
-            manager->resolve(ref, traits, context,
+            manager->resolve(ref, traits, resolveAccess, context,
                              hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned variant contains the expected TraitsData") {
           CHECK(std::holds_alternative<openassetio::TraitsDataPtr>(actual));
@@ -144,24 +148,26 @@ SCENARIO("Resolving entities") {
       std::vector<openassetio::TraitsDataPtr> expectedVec{expected1, expected2, expected3};
 
       // With success callback side effect
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(0, expectedVec[0]))
-          .LR_SIDE_EFFECT(_5(1, expectedVec[1]))
-          .LR_SIDE_EFFECT(_5(2, expectedVec[2]));
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(0, expectedVec[0]))
+          .LR_SIDE_EFFECT(_6(1, expectedVec[1]))
+          .LR_SIDE_EFFECT(_6(2, expectedVec[2]));
 
       WHEN("batch resolve is called with default errorPolicyTag") {
         const std::vector<openassetio::TraitsDataPtr> actualVec =
-            manager->resolve(refs, traits, context);
+            manager->resolve(refs, traits, resolveAccess, context);
         THEN("returned list of TraitsDatas is as expected") { CHECK(expectedVec == actualVec); }
       }
       WHEN("batch resolve is called with kException errorPolicyTag") {
-        const std::vector<openassetio::TraitsDataPtr> actualVec = manager->resolve(
-            refs, traits, context, hostApi::Manager::BatchElementErrorPolicyTag::kException);
+        const std::vector<openassetio::TraitsDataPtr> actualVec =
+            manager->resolve(refs, traits, resolveAccess, context,
+                             hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned list of TraitsDatas is as expected") { CHECK(expectedVec == actualVec); }
       }
       WHEN("batch resolve is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::TraitsDataPtr>>
-            actualVec = manager->resolve(refs, traits, context,
+            actualVec = manager->resolve(refs, traits, resolveAccess, context,
                                          hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants contains the expected TraitsDatas") {
           CHECK(expectedVec.size() == actualVec.size());
@@ -187,28 +193,30 @@ SCENARIO("Resolving entities") {
       std::vector<openassetio::TraitsDataPtr> expectedVec{expected1, expected2, expected3};
 
       // With success callback side effect, given out of order.
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(2, expectedVec[2]))
-          .LR_SIDE_EFFECT(_5(0, expectedVec[0]))
-          .LR_SIDE_EFFECT(_5(1, expectedVec[1]));
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(2, expectedVec[2]))
+          .LR_SIDE_EFFECT(_6(0, expectedVec[0]))
+          .LR_SIDE_EFFECT(_6(1, expectedVec[1]));
 
       WHEN("batch resolve is called with default errorPolicyTag") {
         const std::vector<openassetio::TraitsDataPtr> actualVec =
-            manager->resolve(refs, traits, context);
+            manager->resolve(refs, traits, resolveAccess, context);
         THEN("returned list of TraitsDatas is ordered in index order") {
           CHECK(expectedVec == actualVec);
         }
       }
       WHEN("batch resolve is called with kException errorPolicyTag") {
-        const std::vector<openassetio::TraitsDataPtr> actualVec = manager->resolve(
-            refs, traits, context, hostApi::Manager::BatchElementErrorPolicyTag::kException);
+        const std::vector<openassetio::TraitsDataPtr> actualVec =
+            manager->resolve(refs, traits, resolveAccess, context,
+                             hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned list of TraitsDatas is ordered in index order") {
           CHECK(expectedVec == actualVec);
         }
       }
       WHEN("batch resolve is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::TraitsDataPtr>>
-            actualVec = manager->resolve(refs, traits, context,
+            actualVec = manager->resolve(refs, traits, resolveAccess, context,
                                          hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants is ordered in index order") {
           CHECK(expectedVec.size() == actualVec.size());
@@ -229,12 +237,13 @@ SCENARIO("Resolving entities") {
           openassetio::BatchElementError::ErrorCode::kMalformedEntityReference, "Error Message"};
 
       // With error callback side effect
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(0, expected));
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(0, expected));
 
       WHEN("singular resolve is called with default errorPolicyTag") {
         THEN("an exception is thrown") {
-          CHECK_THROWS_MATCHES(manager->resolve(ref, traits, context),
+          CHECK_THROWS_MATCHES(manager->resolve(ref, traits, resolveAccess, context),
                                openassetio::MalformedEntityReferenceBatchElementException,
                                Catch::Message("Error Message"));
         }
@@ -242,7 +251,7 @@ SCENARIO("Resolving entities") {
       WHEN("singular resolve is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           CHECK_THROWS_MATCHES(
-              manager->resolve(ref, traits, context,
+              manager->resolve(ref, traits, resolveAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException),
               openassetio::MalformedEntityReferenceBatchElementException,
               Catch::Message("Error Message"));
@@ -250,7 +259,7 @@ SCENARIO("Resolving entities") {
       }
       WHEN("singular resolve is called with kVariant errorPolicyTag") {
         std::variant<openassetio::BatchElementError, openassetio::TraitsDataPtr> actual =
-            manager->resolve(ref, traits, context,
+            manager->resolve(ref, traits, resolveAccess, context,
                              hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned variant contains the expected BatchElementError") {
           CHECK(std::holds_alternative<openassetio::BatchElementError>(actual));
@@ -276,14 +285,15 @@ SCENARIO("Resolving entities") {
           "Entity Access Error Message"};
 
       // With mixed callback side effect
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(2, expectedValue2))
-          .LR_SIDE_EFFECT(_6(0, expectedError0))
-          .LR_SIDE_EFFECT(_6(1, expectedError1));
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(2, expectedValue2))
+          .LR_SIDE_EFFECT(_7(0, expectedError0))
+          .LR_SIDE_EFFECT(_7(1, expectedError1));
 
       WHEN("batch resolve is called with default errorPolicyTag") {
         THEN("an exception is thrown") {
-          CHECK_THROWS_MATCHES(manager->resolve(refs, traits, context),
+          CHECK_THROWS_MATCHES(manager->resolve(refs, traits, resolveAccess, context),
                                openassetio::MalformedEntityReferenceBatchElementException,
                                Catch::Message("Malformed Mock Error🤖"));
         }
@@ -291,7 +301,7 @@ SCENARIO("Resolving entities") {
       WHEN("batch resolve is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           CHECK_THROWS_MATCHES(
-              manager->resolve(refs, traits, context,
+              manager->resolve(refs, traits, resolveAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException),
               openassetio::MalformedEntityReferenceBatchElementException,
               Catch::Message("Malformed Mock Error🤖"));
@@ -299,7 +309,7 @@ SCENARIO("Resolving entities") {
       }
       WHEN("batch resolve is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::TraitsDataPtr>>
-            actualVec = manager->resolve(refs, traits, context,
+            actualVec = manager->resolve(refs, traits, resolveAccess, context,
                                          hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants contains the expected objects") {
           auto error0 = std::get<openassetio::BatchElementError>(actualVec[0]);
@@ -356,17 +366,19 @@ TEMPLATE_TEST_CASE(
         "manager plugin will encounter an entity-specific error when next resolving a reference") {
       const openassetio::EntityReference ref = openassetio::EntityReference{"testReference"};
       const openassetio::EntityReferences refs = {ref};
+      const auto resolveAccess = openassetio::access::ResolveAccess::kRead;
 
       const openassetio::BatchElementError expectedError{kExpectedErrorCode, "Some error message"};
 
       // With error callback side effect
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(123, expectedError));
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(123, expectedError));
 
       WHEN("resolve is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           try {
-            manager->resolve(ref, traits, context,
+            manager->resolve(ref, traits, resolveAccess, context,
                              hostApi::Manager::BatchElementErrorPolicyTag::kException);
             FAIL_CHECK("Exception not thrown");
           } catch (const ExpectedExceptionType& exc) {
@@ -383,18 +395,20 @@ TEMPLATE_TEST_CASE(
         "references") {
       const openassetio::EntityReferences refs = {openassetio::EntityReference{"testReference1"},
                                                   openassetio::EntityReference{"testReference2"}};
+      const auto resolveAccess = openassetio::access::ResolveAccess::kRead;
 
       const openassetio::BatchElementError expectedError{kExpectedErrorCode, "Some error"};
 
       // With error callback side effect
-      REQUIRE_CALL(mockManagerInterface, resolve(refs, traits, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(123, expectedError))
+      REQUIRE_CALL(mockManagerInterface,
+                   resolve(refs, traits, resolveAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(123, expectedError))
           .LR_SIDE_EFFECT(FAIL_CHECK("Exception should have short-circuited this"));
 
       WHEN("resolve is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           try {
-            manager->resolve(refs, traits, context,
+            manager->resolve(refs, traits, resolveAccess, context,
                              hostApi::Manager::BatchElementErrorPolicyTag::kException);
             FAIL_CHECK("Exception not thrown");
           } catch (const ExpectedExceptionType& exc) {
@@ -428,6 +442,7 @@ SCENARIO("Preflighting entities") {
     auto& mockManagerInterface = fixture.mockManagerInterface;
     const auto& context = fixture.context;
     const auto& hostSession = fixture.hostSession;
+    const auto publishingAccess = openassetio::access::PublishingAccess::kWrite;
 
     GIVEN("manager plugin successfully preflights a single entity reference") {
       const openassetio::EntityReference expected{"preflightedRef"};
@@ -435,21 +450,23 @@ SCENARIO("Preflighting entities") {
       // With success callback side effect
       REQUIRE_CALL(mockManagerInterface, preflight(openassetio::EntityReferences{ref},
                                                    openassetio::trait::TraitsDatas{traitsData},
-                                                   context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(0, expected));
+                                                   publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(0, expected));
 
       WHEN("singular preflight is called with default errorPolicyTag") {
-        const openassetio::EntityReference actual = manager->preflight(ref, traitsData, context);
+        const openassetio::EntityReference actual =
+            manager->preflight(ref, traitsData, publishingAccess, context);
         THEN("returned entity reference is as expected") { CHECK(expected == actual); }
       }
       WHEN("singular preflight is called with kException errorPolicyTag") {
-        const openassetio::EntityReference actual = manager->preflight(
-            ref, traitsData, context, hostApi::Manager::BatchElementErrorPolicyTag::kException);
+        const openassetio::EntityReference actual =
+            manager->preflight(ref, traitsData, publishingAccess, context,
+                               hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned entity reference is as expected") { CHECK(expected == actual); }
       }
       WHEN("singular preflight is called with kVariant errorPolicyTag") {
         std::variant<openassetio::BatchElementError, openassetio::EntityReference> actual =
-            manager->preflight(ref, traitsData, context,
+            manager->preflight(ref, traitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned variant contains the expected entity reference") {
           CHECK(std::holds_alternative<openassetio::EntityReference>(actual));
@@ -465,22 +482,22 @@ SCENARIO("Preflighting entities") {
       std::vector<openassetio::EntityReference> expectedVec{expected1, expected2, expected3};
 
       // With success callback side effect
-      REQUIRE_CALL(mockManagerInterface,
-                   preflight(threeRefs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(0, expectedVec[0]))
-          .LR_SIDE_EFFECT(_5(1, expectedVec[1]))
-          .LR_SIDE_EFFECT(_5(2, expectedVec[2]));
+      REQUIRE_CALL(mockManagerInterface, preflight(threeRefs, threeTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(0, expectedVec[0]))
+          .LR_SIDE_EFFECT(_6(1, expectedVec[1]))
+          .LR_SIDE_EFFECT(_6(2, expectedVec[2]));
 
       WHEN("batch preflight is called with default errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->preflight(threeRefs, threeTraitsDatas, context);
+            manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context);
         THEN("returned list of entity references is as expected") {
           CHECK(expectedVec == actualVec);
         }
       }
       WHEN("batch preflight is called with kException errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->preflight(threeRefs, threeTraitsDatas, context,
+            manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned list of entity references is as expected") {
           CHECK(expectedVec == actualVec);
@@ -488,7 +505,7 @@ SCENARIO("Preflighting entities") {
       }
       WHEN("batch preflight is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::EntityReference>>
-            actualVec = manager->preflight(threeRefs, threeTraitsDatas, context,
+            actualVec = manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                            hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants contains the expected entity references") {
           CHECK(expectedVec.size() == actualVec.size());
@@ -508,22 +525,22 @@ SCENARIO("Preflighting entities") {
       std::vector<openassetio::EntityReference> expectedVec{expected1, expected2, expected3};
 
       // With success callback side effect, given out of order.
-      REQUIRE_CALL(mockManagerInterface,
-                   preflight(threeRefs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(2, expectedVec[2]))
-          .LR_SIDE_EFFECT(_5(0, expectedVec[0]))
-          .LR_SIDE_EFFECT(_5(1, expectedVec[1]));
+      REQUIRE_CALL(mockManagerInterface, preflight(threeRefs, threeTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(2, expectedVec[2]))
+          .LR_SIDE_EFFECT(_6(0, expectedVec[0]))
+          .LR_SIDE_EFFECT(_6(1, expectedVec[1]));
 
       WHEN("batch preflight is called with default errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->preflight(threeRefs, threeTraitsDatas, context);
+            manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context);
         THEN("returned list of entity references is ordered in index order") {
           CHECK(expectedVec == actualVec);
         }
       }
       WHEN("batch preflight is called with kException errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->preflight(threeRefs, threeTraitsDatas, context,
+            manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned list of entity references is ordered in index order") {
           CHECK(expectedVec == actualVec);
@@ -531,7 +548,7 @@ SCENARIO("Preflighting entities") {
       }
       WHEN("batch preflight is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::EntityReference>>
-            actualVec = manager->preflight(threeRefs, threeTraitsDatas, context,
+            actualVec = manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                            hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants is ordered in index order") {
           CHECK(expectedVec.size() == actualVec.size());
@@ -552,12 +569,12 @@ SCENARIO("Preflighting entities") {
       // With error callback side effect
       REQUIRE_CALL(mockManagerInterface, preflight(openassetio::EntityReferences{ref},
                                                    openassetio::trait::TraitsDatas{traitsData},
-                                                   context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(0, expected));
+                                                   publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(0, expected));
 
       WHEN("singular preflight is called with default errorPolicyTag") {
         THEN("an exception is thrown") {
-          CHECK_THROWS_MATCHES(manager->preflight(ref, traitsData, context),
+          CHECK_THROWS_MATCHES(manager->preflight(ref, traitsData, publishingAccess, context),
                                openassetio::MalformedEntityReferenceBatchElementException,
                                Catch::Message("Error Message"));
         }
@@ -565,7 +582,7 @@ SCENARIO("Preflighting entities") {
       WHEN("singular preflight is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           CHECK_THROWS_MATCHES(
-              manager->preflight(ref, traitsData, context,
+              manager->preflight(ref, traitsData, publishingAccess, context,
                                  hostApi::Manager::BatchElementErrorPolicyTag::kException),
               openassetio::MalformedEntityReferenceBatchElementException,
               Catch::Message("Error Message"));
@@ -573,7 +590,7 @@ SCENARIO("Preflighting entities") {
       }
       WHEN("singular preflight is called with kVariant errorPolicyTag") {
         std::variant<openassetio::BatchElementError, openassetio::EntityReference> actual =
-            manager->preflight(ref, traitsData, context,
+            manager->preflight(ref, traitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned variant contains the expected BatchElementError") {
           CHECK(std::holds_alternative<openassetio::BatchElementError>(actual));
@@ -594,23 +611,24 @@ SCENARIO("Preflighting entities") {
           "Entity Access Error Message"};
 
       // With mixed callback side effect
-      REQUIRE_CALL(mockManagerInterface,
-                   preflight(threeRefs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(2, expectedValue2))
-          .LR_SIDE_EFFECT(_6(0, expectedError0))
-          .LR_SIDE_EFFECT(_6(1, expectedError1));
+      REQUIRE_CALL(mockManagerInterface, preflight(threeRefs, threeTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(2, expectedValue2))
+          .LR_SIDE_EFFECT(_7(0, expectedError0))
+          .LR_SIDE_EFFECT(_7(1, expectedError1));
 
       WHEN("batch preflight is called with default errorPolicyTag") {
         THEN("an exception is thrown") {
-          CHECK_THROWS_MATCHES(manager->preflight(threeRefs, threeTraitsDatas, context),
-                               openassetio::MalformedEntityReferenceBatchElementException,
-                               Catch::Message("Malformed Mock Error🤖"));
+          CHECK_THROWS_MATCHES(
+              manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context),
+              openassetio::MalformedEntityReferenceBatchElementException,
+              Catch::Message("Malformed Mock Error🤖"));
         }
       }
       WHEN("batch preflight is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           CHECK_THROWS_MATCHES(
-              manager->preflight(threeRefs, threeTraitsDatas, context,
+              manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                  hostApi::Manager::BatchElementErrorPolicyTag::kException),
               openassetio::MalformedEntityReferenceBatchElementException,
               Catch::Message("Malformed Mock Error🤖"));
@@ -618,7 +636,7 @@ SCENARIO("Preflighting entities") {
       }
       WHEN("batch preflight is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::EntityReference>>
-            actualVec = manager->preflight(threeRefs, threeTraitsDatas, context,
+            actualVec = manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                            hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants contains the expected objects") {
           auto error0 = std::get<openassetio::BatchElementError>(actualVec[0]);
@@ -671,6 +689,7 @@ TEMPLATE_TEST_CASE(
     auto& mockManagerInterface = fixture.mockManagerInterface;
     const auto& context = fixture.context;
     const auto& hostSession = fixture.hostSession;
+    const auto publishingAccess = openassetio::access::PublishingAccess::kWrite;
 
     AND_GIVEN(
         "manager plugin will encounter an entity-specific error when next preflighting a "
@@ -680,13 +699,13 @@ TEMPLATE_TEST_CASE(
       // With error callback side effect
       REQUIRE_CALL(mockManagerInterface, preflight(openassetio::EntityReferences{ref},
                                                    openassetio::trait::TraitsDatas{traitsData},
-                                                   context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(123, expectedError));
+                                                   publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(123, expectedError));
 
       WHEN("preflight is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           try {
-            manager->preflight(ref, traitsData, context,
+            manager->preflight(ref, traitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
             FAIL_CHECK("Exception not thrown");
           } catch (const ExpectedExceptionType& exc) {
@@ -704,15 +723,15 @@ TEMPLATE_TEST_CASE(
       const openassetio::BatchElementError expectedError{kExpectedErrorCode, "Some error"};
 
       // With error callback side effect
-      REQUIRE_CALL(mockManagerInterface,
-                   preflight(threeRefs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(123, expectedError))
+      REQUIRE_CALL(mockManagerInterface, preflight(threeRefs, threeTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(123, expectedError))
           .LR_SIDE_EFFECT(FAIL_CHECK("Exception should have short-circuited this"));
 
       WHEN("preflight is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           try {
-            manager->preflight(threeRefs, threeTraitsDatas, context,
+            manager->preflight(threeRefs, threeTraitsDatas, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
             FAIL_CHECK("Exception not thrown");
           } catch (const ExpectedExceptionType& exc) {
@@ -736,6 +755,7 @@ SCENARIO("Registering entities") {
     auto& mockManagerInterface = fixture.mockManagerInterface;
     const auto& context = fixture.context;
     const auto& hostSession = fixture.hostSession;
+    const auto publishingAccess = openassetio::access::PublishingAccess::kWrite;
 
     // Create TraitSets used in the register method calls
     const openassetio::trait::TraitSet traits = {"fakeTrait", "secondFakeTrait"};
@@ -754,24 +774,24 @@ SCENARIO("Registering entities") {
           openassetio::EntityReference{"expectedReference"};
 
       // With success callback side effect
-      REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, singleTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(0, expected));
+      REQUIRE_CALL(mockManagerInterface, register_(refs, singleTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(0, expected));
 
       WHEN("singular register is called with default errorPolicyTag") {
         const openassetio::EntityReference actual =
-            manager->register_(ref, singleTraitsData, context);
+            manager->register_(ref, singleTraitsData, publishingAccess, context);
         THEN("returned EntityReference is as expected") { CHECK(expected == actual); }
       }
       WHEN("singular register is called with kException errorPolicyTag") {
         const openassetio::EntityReference actual =
-            manager->register_(ref, singleTraitsData, context,
+            manager->register_(ref, singleTraitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned EntityReference is as expected") { CHECK(expected == actual); }
       }
       WHEN("singular register is called with kVariant errorPolicyTag") {
         std::variant<openassetio::BatchElementError, openassetio::EntityReference> actual =
-            manager->register_(ref, singleTraitsData, context,
+            manager->register_(ref, singleTraitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned variant contains the expected EntityReference") {
           CHECK(std::holds_alternative<openassetio::EntityReference>(actual));
@@ -792,21 +812,21 @@ SCENARIO("Registering entities") {
 
       // With success callback side effect
       REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(0, expectedVec[0]))
-          .LR_SIDE_EFFECT(_5(1, expectedVec[1]))
-          .LR_SIDE_EFFECT(_5(2, expectedVec[2]));
+                   register_(refs, threeTraitsDatas, publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(0, expectedVec[0]))
+          .LR_SIDE_EFFECT(_6(1, expectedVec[1]))
+          .LR_SIDE_EFFECT(_6(2, expectedVec[2]));
 
       WHEN("batch register is called with default errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->register_(refs, threeTraitsDatas, context);
+            manager->register_(refs, threeTraitsDatas, publishingAccess, context);
         THEN("returned list of EntityReferences is as expected") {
           CHECK(expectedVec == actualVec);
         }
       }
       WHEN("batch register is called with kException errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->register_(refs, threeTraitsDatas, context,
+            manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned list of EntityReferences is as expected") {
           CHECK(expectedVec == actualVec);
@@ -814,7 +834,7 @@ SCENARIO("Registering entities") {
       }
       WHEN("batch register is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::EntityReference>>
-            actualVec = manager->register_(refs, threeTraitsDatas, context,
+            actualVec = manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                            hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants contains the expected EntityReferences") {
           CHECK(expectedVec.size() == actualVec.size());
@@ -839,21 +859,21 @@ SCENARIO("Registering entities") {
 
       // With success callback side effect, given out of order.
       REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(2, expectedVec[2]))
-          .LR_SIDE_EFFECT(_5(0, expectedVec[0]))
-          .LR_SIDE_EFFECT(_5(1, expectedVec[1]));
+                   register_(refs, threeTraitsDatas, publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(2, expectedVec[2]))
+          .LR_SIDE_EFFECT(_6(0, expectedVec[0]))
+          .LR_SIDE_EFFECT(_6(1, expectedVec[1]));
 
       WHEN("batch register is called with default errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->register_(refs, threeTraitsDatas, context);
+            manager->register_(refs, threeTraitsDatas, publishingAccess, context);
         THEN("returned list of EntityReferences is ordered in index order") {
           CHECK(expectedVec == actualVec);
         }
       }
       WHEN("batch register is called with kException errorPolicyTag") {
         const std::vector<openassetio::EntityReference> actualVec =
-            manager->register_(refs, threeTraitsDatas, context,
+            manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
         THEN("returned list of EntityReferences is ordered in index order") {
           CHECK(expectedVec == actualVec);
@@ -861,7 +881,7 @@ SCENARIO("Registering entities") {
       }
       WHEN("batch register is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::EntityReference>>
-            actualVec = manager->register_(refs, threeTraitsDatas, context,
+            actualVec = manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                            hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants is ordered in index order") {
           CHECK(expectedVec.size() == actualVec.size());
@@ -883,21 +903,22 @@ SCENARIO("Registering entities") {
           openassetio::BatchElementError::ErrorCode::kMalformedEntityReference, "Error Message"};
 
       // With error callback side effect
-      REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, singleTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(0, expected));
+      REQUIRE_CALL(mockManagerInterface, register_(refs, singleTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(0, expected));
 
       WHEN("singular register is called with default errorPolicyTag") {
         THEN("an exception is thrown") {
-          CHECK_THROWS_MATCHES(manager->register_(ref, singleTraitsData, context),
-                               openassetio::MalformedEntityReferenceBatchElementException,
-                               Catch::Message("Error Message"));
+          CHECK_THROWS_MATCHES(
+              manager->register_(ref, singleTraitsData, publishingAccess, context),
+              openassetio::MalformedEntityReferenceBatchElementException,
+              Catch::Message("Error Message"));
         }
       }
       WHEN("singular register is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           CHECK_THROWS_MATCHES(
-              manager->register_(ref, singleTraitsData, context,
+              manager->register_(ref, singleTraitsData, publishingAccess, context,
                                  hostApi::Manager::BatchElementErrorPolicyTag::kException),
               openassetio::MalformedEntityReferenceBatchElementException,
               Catch::Message("Error Message"));
@@ -905,7 +926,7 @@ SCENARIO("Registering entities") {
       }
       WHEN("singular register is called with kVariant errorPolicyTag") {
         std::variant<openassetio::BatchElementError, openassetio::EntityReference> actual =
-            manager->register_(ref, singleTraitsData, context,
+            manager->register_(ref, singleTraitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned variant contains the expected BatchElementError") {
           CHECK(std::holds_alternative<openassetio::BatchElementError>(actual));
@@ -931,22 +952,23 @@ SCENARIO("Registering entities") {
 
       // With mixed callback side effect
       REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_5(2, expectedValue2))
-          .LR_SIDE_EFFECT(_6(0, expectedError0))
-          .LR_SIDE_EFFECT(_6(1, expectedError1));
+                   register_(refs, threeTraitsDatas, publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_6(2, expectedValue2))
+          .LR_SIDE_EFFECT(_7(0, expectedError0))
+          .LR_SIDE_EFFECT(_7(1, expectedError1));
 
       WHEN("batch register is called with default errorPolicyTag") {
         THEN("an exception is thrown") {
-          CHECK_THROWS_MATCHES(manager->register_(refs, threeTraitsDatas, context),
-                               openassetio::MalformedEntityReferenceBatchElementException,
-                               Catch::Message("Malformed Mock Error🤖"));
+          CHECK_THROWS_MATCHES(
+              manager->register_(refs, threeTraitsDatas, publishingAccess, context),
+              openassetio::MalformedEntityReferenceBatchElementException,
+              Catch::Message("Malformed Mock Error🤖"));
         }
       }
       WHEN("batch register is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           CHECK_THROWS_MATCHES(
-              manager->register_(refs, threeTraitsDatas, context,
+              manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                  hostApi::Manager::BatchElementErrorPolicyTag::kException),
               openassetio::MalformedEntityReferenceBatchElementException,
               Catch::Message("Malformed Mock Error🤖"));
@@ -954,7 +976,7 @@ SCENARIO("Registering entities") {
       }
       WHEN("batch register is called with kVariant errorPolicyTag") {
         std::vector<std::variant<openassetio::BatchElementError, openassetio::EntityReference>>
-            actualVec = manager->register_(refs, threeTraitsDatas, context,
+            actualVec = manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                            hostApi::Manager::BatchElementErrorPolicyTag::kVariant);
         THEN("returned lists of variants contains the expected objects") {
           auto error0 = std::get<openassetio::BatchElementError>(actualVec[0]);
@@ -998,6 +1020,7 @@ TEMPLATE_TEST_CASE(
     auto& mockManagerInterface = fixture.mockManagerInterface;
     const auto& context = fixture.context;
     const auto& hostSession = fixture.hostSession;
+    const auto publishingAccess = openassetio::access::PublishingAccess::kWrite;
 
     AND_GIVEN(
         "manager plugin will encounter an entity-specific error when next registering a "
@@ -1011,14 +1034,14 @@ TEMPLATE_TEST_CASE(
       const openassetio::BatchElementError expectedError{kExpectedErrorCode, "Some error message"};
 
       // With error callback side effect
-      REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, singleTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(123, expectedError));
+      REQUIRE_CALL(mockManagerInterface, register_(refs, singleTraitsDatas, publishingAccess,
+                                                   context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(123, expectedError));
 
       WHEN("register is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           try {
-            manager->register_(ref, singleTraitsData, context,
+            manager->register_(ref, singleTraitsData, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
             FAIL_CHECK("Exception not thrown");
           } catch (const ExpectedExceptionType& exc) {
@@ -1045,14 +1068,14 @@ TEMPLATE_TEST_CASE(
 
       // With error callback side effect
       REQUIRE_CALL(mockManagerInterface,
-                   register_(refs, threeTraitsDatas, context, hostSession, _, _))
-          .LR_SIDE_EFFECT(_6(123, expectedError))
+                   register_(refs, threeTraitsDatas, publishingAccess, context, hostSession, _, _))
+          .LR_SIDE_EFFECT(_7(123, expectedError))
           .LR_SIDE_EFFECT(FAIL_CHECK("Exception should have short-circuited this"));
 
       WHEN("register is called with kException errorPolicyTag") {
         THEN("an exception is thrown") {
           try {
-            manager->register_(refs, threeTraitsDatas, context,
+            manager->register_(refs, threeTraitsDatas, publishingAccess, context,
                                hostApi::Manager::BatchElementErrorPolicyTag::kException);
             FAIL_CHECK("Exception not thrown");
           } catch (const ExpectedExceptionType& exc) {
diff --git a/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py b/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py
index 074cb9d96..0ce04f1c4 100644
--- a/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py
+++ b/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py
@@ -37,7 +37,8 @@
 # pylint: disable=too-many-lines,unbalanced-tuple-unpacking
 
 from .harness import FixtureAugmentedTestCase
-from ... import BatchElementError, Context, EntityReference, TraitsData
+from ... import BatchElementError, EntityReference, TraitsData
+from ...access import PolicyAccess, ResolveAccess, RelationsAccess, PublishingAccess
 
 
 __all__ = []
@@ -178,41 +179,36 @@ class Test_managementPolicy(FixtureAugmentedTestCase):
 
     def test_when_called_with_single_trait_set_returns_single_result(self):
         context = self.createTestContext()
-        context.access = context.Access.kRead
         self.__assertPolicyResults(1, context)
 
     def test_when_called_with_ten_trait_sets_returns_ten_results(self):
         context = self.createTestContext()
-        context.access = context.Access.kRead
         self.__assertPolicyResults(10, context)
 
     def test_calling_with_read_context(self):
         context = self.createTestContext()
-        context.access = context.Access.kRead
         self.__assertPolicyResults(1, context)
 
     def test_calling_with_write_context(self):
         context = self.createTestContext()
-        context.access = context.Access.kWrite
-        self.__assertPolicyResults(1, context)
+        self.__assertPolicyResults(1, context, policyAccess=PolicyAccess.kWrite)
 
     def test_calling_with_createRelated_context(self):
         context = self.createTestContext()
-        context.access = context.Access.kCreateRelated
-        self.__assertPolicyResults(1, context)
+        self.__assertPolicyResults(1, context, policyAccess=PolicyAccess.kCreateRelated)
 
     def test_calling_with_empty_trait_set_does_not_error(self):
         context = self.createTestContext()
-        context.access = context.Access.kRead
         self.__assertPolicyResults(1, context, traitSet=set())
 
     def test_calling_with_unknown_complex_trait_set_does_not_error(self):
         context = self.createTestContext()
-        context.access = context.Access.kRead
         traits = {"🐟🐠🐟🐠", "asdfsdfasdf", "⿂"}
         self.__assertPolicyResults(1, context, traitSet=traits)
 
-    def __assertPolicyResults(self, numTraitSets, context, traitSet={"entity"}):
+    def __assertPolicyResults(
+        self, numTraitSets, context, policyAccess=PolicyAccess.kRead, traitSet={"entity"}
+    ):
         """
         Tests the validity and coherency of the results of a call to
         `managementPolicy` for a given number of trait sets and
@@ -225,7 +221,7 @@ def __assertPolicyResults(self, numTraitSets, context, traitSet={"entity"}):
         # pylint: disable=dangerous-default-value
         traitSets = [traitSet for _ in range(numTraitSets)]
 
-        policies = self._manager.managementPolicy(traitSets, context)
+        policies = self._manager.managementPolicy(traitSets, policyAccess, context)
 
         self.assertValuesOfType(policies, TraitsData)
         self.assertEqual(len(policies), numTraitSets)
@@ -268,7 +264,7 @@ def setUp(self):
         )
 
     def test_when_querying_existing_reference_then_true_is_returned(self):
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
         result = [None]
         self._manager.entityExists(
             [self.a_reference_to_an_existing_entity],
@@ -282,7 +278,7 @@ def test_when_querying_nonexisting_reference_then_false_is_returned(self):
         a_reference_to_a_nonexisting_entity = self.requireEntityReferenceFixture(
             "a_reference_to_a_nonexisting_entity"
         )
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
         result = [None]
         self._manager.entityExists(
             [a_reference_to_a_nonexisting_entity],
@@ -298,7 +294,7 @@ def test_when_querying_existing_and_nonexisting_references_then_true_and_false_i
         a_reference_to_a_nonexisting_entity = self.requireEntityReferenceFixture(
             "a_reference_to_a_nonexisting_entity"
         )
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
         result = [None, None]
         self._manager.entityExists(
             [self.a_reference_to_an_existing_entity, a_reference_to_a_nonexisting_entity],
@@ -311,7 +307,7 @@ def test_when_querying_existing_and_nonexisting_references_then_true_and_false_i
     def test_when_querying_malformed_reference_then_malformed_reference_error_is_returned(self):
         a_malformed_reference = self.requireEntityReferenceFixture("a_malformed_reference")
         expected_error_message = self.requireFixture("expected_error_message")
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
         result = [None]
         self._manager.entityExists(
             [a_malformed_reference],
@@ -339,39 +335,39 @@ def setUp(self):
 
     def test_when_no_traits_then_returned_specification_is_empty(self):
         ref = self.a_reference_to_a_readable_entity
-        self.__testResolution([ref], set(), Context.Access.kRead, set())
+        self.__testResolution([ref], set(), ResolveAccess.kRead, set())
 
     def test_when_multiple_references_then_same_number_of_returned_specifications(self):
         ref = self.a_reference_to_a_readable_entity
-        self.__testResolution([ref, ref, ref, ref, ref], set(), Context.Access.kRead, set())
+        self.__testResolution([ref, ref, ref, ref, ref], set(), ResolveAccess.kRead, set())
 
     def test_when_unknown_traits_then_returned_specification_is_empty(self):
         ref = self.a_reference_to_a_readable_entity
-        self.__testResolution([ref], {"₲₪₡🤯"}, Context.Access.kRead, set())
+        self.__testResolution([ref], {"₲₪₡🤯"}, ResolveAccess.kRead, set())
 
     def test_when_valid_traits_then_returned_specification_has_those_traits(self):
         ref = self.a_reference_to_a_readable_entity
         traits = self.a_set_of_valid_traits
-        self.__testResolution([ref], traits, Context.Access.kRead, traits)
+        self.__testResolution([ref], traits, ResolveAccess.kRead, traits)
 
     def test_when_valid_and_unknown_traits_then_returned_specification_only_has_valid_traits(self):
         ref = self.a_reference_to_a_readable_entity
         traits = self.a_set_of_valid_traits
         mixed_traits = set(traits)
         mixed_traits.add("₲₪₡🤯")
-        self.__testResolution([ref], mixed_traits, Context.Access.kRead, traits)
+        self.__testResolution([ref], mixed_traits, ResolveAccess.kRead, traits)
 
     def test_when_resolving_read_only_reference_for_write_then_access_error_is_returned(self):
         self.__testResolutionError(
             "a_reference_to_a_readonly_entity",
-            access=Context.Access.kWrite,
+            resolve_access=ResolveAccess.kWrite,
             errorCode=BatchElementError.ErrorCode.kEntityAccessError,
         )
 
     def test_when_resolving_write_only_reference_for_read_then_access_error_is_returned(self):
         self.__testResolutionError(
             "a_reference_to_a_writeonly_entity",
-            access=Context.Access.kRead,
+            resolve_access=ResolveAccess.kRead,
             errorCode=BatchElementError.ErrorCode.kEntityAccessError,
         )
 
@@ -384,14 +380,14 @@ def test_when_resolving_malformed_reference_then_malformed_reference_error_is_re
             errorCode=BatchElementError.ErrorCode.kMalformedEntityReference,
         )
 
-    def __testResolution(self, references, traits, access, expected_traits):
+    def __testResolution(self, references, traits, resolve_access, expected_traits):
         context = self.createTestContext()
-        context.access = access
         results = []
 
         self._manager.resolve(
             references,
             traits,
+            resolve_access,
             context,
             lambda _idx, traits_data: results.append(traits_data),
             lambda idx, batch_element_error: self.fail(
@@ -406,7 +402,7 @@ def __testResolution(self, references, traits, access, expected_traits):
     def __testResolutionError(
         self,
         fixture_name,
-        access=Context.Access.kRead,
+        resolve_access=ResolveAccess.kRead,
         errorCode=BatchElementError.ErrorCode.kEntityResolutionError,
     ):
         reference = self._manager.createEntityReference(
@@ -417,12 +413,12 @@ def __testResolutionError(
         expected_error = BatchElementError(errorCode, expected_msg)
 
         context = self.createTestContext()
-        context.access = access
 
         results = []
         self._manager.resolve(
             [reference],
             self.a_set_of_valid_traits,
+            resolve_access,
             context,
             lambda _idx, _traits_data: self.fail("Unexpected success callback"),
             lambda _idx, batch_element_error: results.append(batch_element_error),
@@ -448,7 +444,8 @@ def test_when_multiple_references_then_same_number_of_returned_references(self):
         self._manager.preflight(
             [entity_reference, entity_reference],
             [traits_data, traits_data],
-            self.createTestContext(Context.Access.kWrite),
+            PublishingAccess.kWrite,
+            self.createTestContext(),
             lambda _, ref: results.append(ref),
             lambda _, err: self.fail(err.message),
         )
@@ -498,12 +495,12 @@ def __testPreflightError(
         expected_error = BatchElementError(expected_error_code, expected_error_message)
 
         context = self.createTestContext()
-        context.access = Context.Access.kWrite
 
         errors = []
         self._manager.preflight(
             [entity_reference],
             [traits_data],
+            PublishingAccess.kWrite,
             context,
             lambda _idx, _ref: self.fail("Preflight should not succeed"),
             lambda _idx, error: errors.append(error),
@@ -535,7 +532,8 @@ def test_when_multiple_references_then_same_number_of_returned_references(self):
         self._manager.register(
             [ref, ref],
             [data, data],
-            self.createTestContext(Context.Access.kWrite),
+            PublishingAccess.kWrite,
+            self.createTestContext(),
             lambda _, ref: results.append(ref),
             lambda _, err: self.fail(err.message),
         )
@@ -566,7 +564,8 @@ def __testRegisterError(self, fixture_name, errorCode):
         self._manager.register(
             [reference],
             [self.a_traitsdata_for_a_reference_to_a_writable_entity],
-            self.createTestContext(Context.Access.kWrite),
+            PublishingAccess.kWrite,
+            self.createTestContext(),
             lambda _idx, _ref: self.fail("Preflight should not succeed"),
             lambda _idx, error: errors.append(error),
         )
@@ -861,7 +860,7 @@ def get_pager_and_weakref_args(get_pagers):
             relationships = self.weaklist([relationship])
             ref = self.requireEntityReferenceFixture("a_reference")
             refs = self.weaklist([ref])
-            context = self.createTestContext(access=Context.Access.kRead)
+            context = self.createTestContext()
             context.locale = TraitsData(context.locale)  # Force a copy.
             result_trait_set = copy.copy(self.requireFixture("an_entity_trait_set_to_filter_by"))
 
@@ -907,12 +906,13 @@ def get_pager_and_weakref_args(get_pagers):
     def __test_getWithRelationship_success(self, references, relationship, resultTraitSet=None):
         if resultTraitSet is None:
             resultTraitSet = set()
-        context = self.createTestContext(access=Context.Access.kRead)
+        context = self.createTestContext()
         results = []
 
         self._manager.getWithRelationship(
             references,
             relationship,
+            RelationsAccess.kRead,
             context,
             lambda _idx, traits_data: results.append(traits_data),
             lambda idx, batch_element_error: self.fail(
@@ -926,12 +926,13 @@ def __test_getWithRelationship_success(self, references, relationship, resultTra
     def __test_getWithRelationships_success(self, reference, relationships, resultTraitSet=None):
         if resultTraitSet is None:
             resultTraitSet = set()
-        context = self.createTestContext(access=Context.Access.kRead)
+        context = self.createTestContext()
         results = []
 
         self._manager.getWithRelationships(
             reference,
             relationships,
+            RelationsAccess.kRead,
             context,
             lambda _idx, traits_data: results.append(traits_data),
             lambda idx, batch_element_error: self.fail(
@@ -947,7 +948,7 @@ def __test_getWithRelationshipPaged_success(
         self, references, relationship, pageSize=10, context=None, resultTraitSet=None
     ):
         if context is None:
-            context = self.createTestContext(access=Context.Access.kRead)
+            context = self.createTestContext()
 
         if resultTraitSet is None:
             resultTraitSet = set()
@@ -958,6 +959,7 @@ def __test_getWithRelationshipPaged_success(
             references,
             relationship,
             pageSize,
+            RelationsAccess.kRead,
             context,
             lambda idx, pager: operator.setitem(pagers, idx, pager),
             lambda idx, batch_element_error: self.fail(
@@ -974,7 +976,7 @@ def __test_getWithRelationshipsPaged_success(
         self, reference, relationships, pageSize=10, context=None, resultTraitSet=None
     ):
         if context is None:
-            context = self.createTestContext(access=Context.Access.kRead)
+            context = self.createTestContext()
 
         if resultTraitSet is None:
             resultTraitSet = set()
@@ -985,6 +987,7 @@ def __test_getWithRelationshipsPaged_success(
             reference,
             relationships,
             pageSize,
+            RelationsAccess.kRead,
             context,
             lambda idx, pager: operator.setitem(pagers, idx, pager),
             lambda idx, batch_element_error: self.fail(
@@ -1006,7 +1009,7 @@ def __test_getWithRelationship_error(
     ):
         expected_error = BatchElementError(expected_error_code, expected_error_message)
 
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
 
         relationship = TraitsData(relationship_trait_set)
 
@@ -1014,6 +1017,7 @@ def __test_getWithRelationship_error(
         self._manager.getWithRelationship(
             [entity_reference],
             relationship,
+            RelationsAccess.kRead,
             context,
             lambda _idx, _refs: self.fail("Unexpected success callback"),
             lambda _idx, batch_element_error: results.append(batch_element_error),
@@ -1031,7 +1035,7 @@ def __test_getWithRelationships_error(
     ):
         expected_error = BatchElementError(expected_error_code, expected_error_message)
 
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
 
         relationship = TraitsData(relationship_trait_set)
 
@@ -1039,6 +1043,7 @@ def __test_getWithRelationships_error(
         self._manager.getWithRelationships(
             entity_reference,
             [relationship],
+            RelationsAccess.kRead,
             context,
             lambda _idx, _refs: self.fail("Unexpected success callback"),
             lambda _idx, batch_element_error: results.append(batch_element_error),
@@ -1056,7 +1061,7 @@ def __test_getWithRelationshipPaged_error(
     ):
         expected_error = BatchElementError(expected_error_code, expected_error_message)
 
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
 
         relationship = TraitsData(relationship_trait_set)
 
@@ -1066,6 +1071,7 @@ def __test_getWithRelationshipPaged_error(
             [entity_reference],
             relationship,
             1,
+            RelationsAccess.kRead,
             context,
             lambda _idx, _pager: self.fail("Unexpected success callback"),
             lambda _idx, batch_element_error: results.append(batch_element_error),
@@ -1083,7 +1089,7 @@ def __test_getWithRelationshipsPaged_error(
     ):
         expected_error = BatchElementError(expected_error_code, expected_error_message)
 
-        context = self.createTestContext(Context.Access.kRead)
+        context = self.createTestContext()
 
         relationship = TraitsData(relationship_trait_set)
 
@@ -1093,6 +1099,7 @@ def __test_getWithRelationshipsPaged_error(
             entity_reference,
             [relationship],
             1,
+            RelationsAccess.kRead,
             context,
             lambda _idx, _pager: self.fail("Unexpected success callback"),
             lambda _idx, batch_element_error: results.append(batch_element_error),
diff --git a/src/openassetio-python/package/openassetio/test/manager/harness.py b/src/openassetio-python/package/openassetio/test/manager/harness.py
index 978d799c3..4e15efef5 100644
--- a/src/openassetio-python/package/openassetio/test/manager/harness.py
+++ b/src/openassetio-python/package/openassetio/test/manager/harness.py
@@ -206,18 +206,13 @@ def __init__(self, fixtures, manager, locale, *args, **kwargs):
         # during setUp.
         self.addCleanup(self._manager.flushCaches)
 
-    def createTestContext(self, access=None):
+    def createTestContext(self):
         """
         A convenience method to create a context with the
         test locale, as provided by the test harness mechanism.
-
-        @param access `int` One of the context access policies (or None),
-        if provided, the context's access will be set to this.
         """
         context = self._manager.createContext()
         context.locale = self._locale
-        if access is not None:
-            context.access = access
         return context
 
     def assertIsStringKeyPrimitiveValueDict(self, dictionary):
diff --git a/src/openassetio-python/tests/bridge/CMakeLists.txt b/src/openassetio-python/tests/bridge/CMakeLists.txt
index 1fc675d1b..461752f63 100644
--- a/src/openassetio-python/tests/bridge/CMakeLists.txt
+++ b/src/openassetio-python/tests/bridge/CMakeLists.txt
@@ -110,7 +110,7 @@ add_custom_target(
     # Note: as above, we must not have trailing whitespace after setting
     # an env var on Windows.
     ${_envvars}&&
-"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/\
+    "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/\
 $<TARGET_FILE_NAME:openassetio-python-bridge-test-exe>"
 )
 
@@ -138,7 +138,7 @@ add_custom_target(
     # Note: as above, we must not have trailing whitespace after setting
     # an env var on Windows.
     ${_envvars}&&
-"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/\
+    "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/\
 $<TARGET_FILE_NAME:openassetio-python-bridge-test-exe>" "[no_openassetio_module]"
 )
 
diff --git a/src/openassetio-python/tests/conftest.py b/src/openassetio-python/tests/conftest.py
index 2834d1218..df96d4106 100644
--- a/src/openassetio-python/tests/conftest.py
+++ b/src/openassetio-python/tests/conftest.py
@@ -24,6 +24,13 @@
 import pytest
 
 from openassetio import Context, EntityReference, TraitsData
+from openassetio.access import (
+    PolicyAccess,
+    ResolveAccess,
+    RelationsAccess,
+    PublishingAccess,
+    DefaultEntityAccess,
+)
 from openassetio.log import LoggerInterface
 from openassetio.managerApi import (
     ManagerInterface,
@@ -164,28 +171,43 @@ def flushCaches(self, hostSession):
         return self.mock.flushCaches(hostSession)
 
     def defaultEntityReference(
-        self, traitSets, context, hostSession, successCallback, errorCallback
+        self, traitSets, defaultEntityAccess, context, hostSession, successCallback, errorCallback
     ):
         self.__assertIsIterableOf(traitSets, set)
         for traitSet in traitSets:
             self.__assertIsIterableOf(traitSet, str)
+        assert isinstance(defaultEntityAccess, DefaultEntityAccess)
         self.__assertCallingContext(context, hostSession)
         assert callable(successCallback)
         assert callable(errorCallback)
         return self.mock.defaultEntityReference(
-            traitSets, context, hostSession, successCallback, errorCallback
+            traitSets, defaultEntityAccess, context, hostSession, successCallback, errorCallback
         )
 
     def preflight(
-        self, targetEntityRefs, traitsDatas, context, hostSession, successCallback, errorCallback
+        self,
+        targetEntityRefs,
+        traitsDatas,
+        publishingAccess,
+        context,
+        hostSession,
+        successCallback,
+        errorCallback,
     ):
         self.__assertIsIterableOf(targetEntityRefs, EntityReference)
         self.__assertIsIterableOf(traitsDatas, TraitsData)
+        assert isinstance(publishingAccess, PublishingAccess)
         self.__assertCallingContext(context, hostSession)
         assert callable(successCallback)
         assert callable(errorCallback)
         return self.mock.preflight(
-            targetEntityRefs, traitsDatas, context, hostSession, successCallback, errorCallback
+            targetEntityRefs,
+            traitsDatas,
+            publishingAccess,
+            context,
+            hostSession,
+            successCallback,
+            errorCallback,
         )
 
     def createState(self, hostSession):
@@ -209,13 +231,14 @@ def displayName(self):
     def initialize(self, managerSettings, hostSession):
         return self.mock.initialize(managerSettings, hostSession)
 
-    def managementPolicy(self, traitSets, context, hostSession):
+    def managementPolicy(self, traitSets, policyAccess, context, hostSession):
         self.__assertIsIterableOf(traitSets, set)
+        assert isinstance(policyAccess, PolicyAccess)
         for traitSet in traitSets:
             self.__assertIsIterableOf(traitSet, str)
         self.__assertCallingContext(context, hostSession)
 
-        return self.mock.managementPolicy(traitSets, context, hostSession)
+        return self.mock.managementPolicy(traitSets, policyAccess, context, hostSession)
 
     def isEntityReferenceString(self, someString, hostSession):
         assert isinstance(someString, str)
@@ -231,14 +254,30 @@ def entityExists(self, entityRefs, context, hostSession, successCallback, errorC
             entityRefs, context, hostSession, successCallback, errorCallback
         )
 
-    def resolve(self, entityRefs, traitSet, context, hostSession, successCallback, errorCallback):
+    def resolve(
+        self,
+        entityRefs,
+        traitSet,
+        resolveAccess,
+        context,
+        hostSession,
+        successCallback,
+        errorCallback,
+    ):
         self.__assertIsIterableOf(entityRefs, EntityReference)
         self.__assertIsIterableOf(traitSet, str)
+        assert isinstance(resolveAccess, ResolveAccess)
         self.__assertCallingContext(context, hostSession)
         assert callable(successCallback)
         assert callable(errorCallback)
         return self.mock.resolve(
-            entityRefs, traitSet, context, hostSession, successCallback, errorCallback
+            entityRefs,
+            traitSet,
+            resolveAccess,
+            context,
+            hostSession,
+            successCallback,
+            errorCallback,
         )
 
     def getWithRelationship(
@@ -246,6 +285,7 @@ def getWithRelationship(
         entityReferences,
         relationshipTraitsData,
         resultTraitSet,
+        relationsAccess,
         context,
         hostSession,
         successCallback,
@@ -253,6 +293,7 @@ def getWithRelationship(
     ):
         assert isinstance(relationshipTraitsData, TraitsData)
         self.__assertIsIterableOf(entityReferences, EntityReference)
+        assert isinstance(relationsAccess, RelationsAccess)
         self.__assertCallingContext(context, hostSession)
         assert isinstance(resultTraitSet, set)
         self.__assertIsIterableOf(resultTraitSet, str)
@@ -262,6 +303,7 @@ def getWithRelationship(
             entityReferences,
             relationshipTraitsData,
             resultTraitSet,
+            relationsAccess,
             context,
             hostSession,
             successCallback,
@@ -273,6 +315,7 @@ def getWithRelationships(
         entityReference,
         relationshipTraitsDatas,
         resultTraitSet,
+        relationsAccess,
         context,
         hostSession,
         successCallback,
@@ -280,6 +323,7 @@ def getWithRelationships(
     ):
         self.__assertIsIterableOf(relationshipTraitsDatas, TraitsData)
         assert isinstance(entityReference, EntityReference)
+        assert isinstance(relationsAccess, RelationsAccess)
         self.__assertCallingContext(context, hostSession)
         assert callable(successCallback)
         assert callable(errorCallback)
@@ -289,6 +333,7 @@ def getWithRelationships(
             entityReference,
             relationshipTraitsDatas,
             resultTraitSet,
+            relationsAccess,
             context,
             hostSession,
             successCallback,
@@ -302,6 +347,7 @@ def getWithRelationshipPaged(
         relationshipTraitsData,
         resultTraitSet,
         pageSize,
+        relationsAccess,
         context,
         hostSession,
         successCallback,
@@ -309,6 +355,7 @@ def getWithRelationshipPaged(
     ):
         assert isinstance(relationshipTraitsData, TraitsData)
         self.__assertIsIterableOf(entityReferences, EntityReference)
+        assert isinstance(relationsAccess, RelationsAccess)
         self.__assertCallingContext(context, hostSession)
         assert isinstance(resultTraitSet, set)
         self.__assertIsIterableOf(resultTraitSet, str)
@@ -321,6 +368,7 @@ def getWithRelationshipPaged(
             relationshipTraitsData,
             resultTraitSet,
             pageSize,
+            relationsAccess,
             context,
             hostSession,
             successCallback,
@@ -334,6 +382,7 @@ def getWithRelationshipsPaged(
         relationshipTraitsDatas,
         resultTraitSet,
         pageSize,
+        relationsAccess,
         context,
         hostSession,
         successCallback,
@@ -341,6 +390,7 @@ def getWithRelationshipsPaged(
     ):
         self.__assertIsIterableOf(relationshipTraitsDatas, TraitsData)
         assert isinstance(entityReference, EntityReference)
+        assert isinstance(relationsAccess, RelationsAccess)
         self.__assertCallingContext(context, hostSession)
         assert isinstance(pageSize, int)
         assert pageSize > 0
@@ -353,6 +403,7 @@ def getWithRelationshipsPaged(
             relationshipTraitsDatas,
             resultTraitSet,
             pageSize,
+            relationsAccess,
             context,
             hostSession,
             successCallback,
@@ -363,6 +414,7 @@ def register(
         self,
         targetEntityRefs,
         entityTraitsDatas,
+        publishingAccess,
         context,
         hostSession,
         successCallback,
@@ -370,6 +422,7 @@ def register(
     ):
         self.__assertIsIterableOf(targetEntityRefs, EntityReference)
         self.__assertIsIterableOf(entityTraitsDatas, TraitsData)
+        assert isinstance(publishingAccess, PublishingAccess)
         self.__assertCallingContext(context, hostSession)
         assert len(targetEntityRefs) == len(entityTraitsDatas)
         assert callable(successCallback)
@@ -377,6 +430,7 @@ def register(
         return self.mock.register(
             targetEntityRefs,
             entityTraitsDatas,
+            publishingAccess,
             context,
             hostSession,
             successCallback,
diff --git a/src/openassetio-python/tests/package/hostApi/test_manager.py b/src/openassetio-python/tests/package/hostApi/test_manager.py
index a8afffd9f..9c7bfd407 100644
--- a/src/openassetio-python/tests/package/hostApi/test_manager.py
+++ b/src/openassetio-python/tests/package/hostApi/test_manager.py
@@ -38,6 +38,7 @@
     TraitsData,
     managerApi,
     constants,
+    access,
 )
 from openassetio.hostApi import Manager, EntityReferencePager
 from openassetio.managerApi import EntityReferencePagerInterface
@@ -428,10 +429,19 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         manager.defaultEntityReference(
-            some_entity_trait_sets, a_context, success_callback, error_callback
+            some_entity_trait_sets,
+            access.DefaultEntityAccess.kCreateRelated,
+            a_context,
+            success_callback,
+            error_callback,
         )
         method.assert_called_once_with(
-            some_entity_trait_sets, a_context, a_host_session, mock.ANY, mock.ANY
+            some_entity_trait_sets,
+            access.DefaultEntityAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         success_callback.assert_called_once_with(0, a_ref)
@@ -474,13 +484,19 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         manager.getWithRelationship(
-            two_refs, an_empty_traitsdata, a_context, success_callback, error_callback
+            two_refs,
+            an_empty_traitsdata,
+            access.RelationsAccess.kWrite,
+            a_context,
+            success_callback,
+            error_callback,
         )
 
         method.assert_called_once_with(
             two_refs,
             an_empty_traitsdata,
             set(),
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,
@@ -497,6 +513,7 @@ def call_callbacks(*_args):
         manager.getWithRelationship(
             two_refs,
             an_empty_traitsdata,
+            access.RelationsAccess.kWrite,
             a_context,
             success_callback,
             error_callback,
@@ -507,6 +524,7 @@ def call_callbacks(*_args):
             two_refs,
             an_empty_traitsdata,
             an_entity_trait_set,
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,
@@ -530,14 +548,24 @@ def assert_resultTraitSet_empty(_, __, result_trait_set, *_args):
         # First call mutates the defaulted resultTraitSet parameter.
         mock_manager_interface.mock.getWithRelationship.side_effect = mutate_resultTraitSet
         manager.getWithRelationship(
-            [a_ref], an_empty_traitsdata, a_context, mock.Mock(), mock.Mock()
+            [a_ref],
+            an_empty_traitsdata,
+            access.RelationsAccess.kWrite,
+            a_context,
+            mock.Mock(),
+            mock.Mock(),
         )
 
         # Second call asserts that the mutation of the first call didn't
         # persist in the default value.
         mock_manager_interface.mock.getWithRelationship.side_effect = assert_resultTraitSet_empty
         manager.getWithRelationship(
-            [a_ref], an_empty_traitsdata, a_context, mock.Mock(), mock.Mock()
+            [a_ref],
+            an_empty_traitsdata,
+            access.RelationsAccess.kWrite,
+            a_context,
+            mock.Mock(),
+            mock.Mock(),
         )
 
         # Confidence check: ensure we actually called the manager plugin.
@@ -577,12 +605,20 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        manager.getWithRelationships(a_ref, two_datas, a_context, success_callback, error_callback)
+        manager.getWithRelationships(
+            a_ref,
+            two_datas,
+            access.RelationsAccess.kWrite,
+            a_context,
+            success_callback,
+            error_callback,
+        )
 
         method.assert_called_once_with(
             a_ref,
             two_datas,
             set(),
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,
@@ -599,6 +635,7 @@ def call_callbacks(*_args):
         manager.getWithRelationships(
             a_ref,
             two_datas,
+            access.RelationsAccess.kWrite,
             a_context,
             success_callback,
             error_callback,
@@ -609,6 +646,7 @@ def call_callbacks(*_args):
             a_ref,
             two_datas,
             an_entity_trait_set,
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,
@@ -620,7 +658,12 @@ def test_when_relationship_is_None_then_exception_raised(
     ):
         with pytest.raises(TypeError):
             manager.getWithRelationships(
-                [TraitsData(), None], a_ref, a_context, mock.Mock(), mock.Mock()
+                [TraitsData(), None],
+                a_ref,
+                access.RelationsAccess.kWrite,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
             )
 
         assert not mock_manager_interface.mock.getWithRelationships.called
@@ -642,14 +685,24 @@ def assert_resultTraitSet_empty(_, __, result_trait_set, *_args):
         # First call mutates the defaulted resultTraitSet parameter.
         mock_manager_interface.mock.getWithRelationships.side_effect = mutate_resultTraitSet
         manager.getWithRelationships(
-            a_ref, [an_empty_traitsdata], a_context, mock.Mock(), mock.Mock()
+            a_ref,
+            [an_empty_traitsdata],
+            access.RelationsAccess.kWrite,
+            a_context,
+            mock.Mock(),
+            mock.Mock(),
         )
 
         # Second call asserts that the mutation of the first call didn't
         # persist in the default value.
         mock_manager_interface.mock.getWithRelationships.side_effect = assert_resultTraitSet_empty
         manager.getWithRelationships(
-            a_ref, [an_empty_traitsdata], a_context, mock.Mock(), mock.Mock()
+            a_ref,
+            [an_empty_traitsdata],
+            access.RelationsAccess.kWrite,
+            a_context,
+            mock.Mock(),
+            mock.Mock(),
         )
 
         # Confidence check: ensure we actually called the manager plugin.
@@ -713,6 +766,7 @@ def call_callbacks(*_args):
             two_refs,
             an_empty_traitsdata,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             success_callback,
             error_callback,
@@ -724,6 +778,7 @@ def call_callbacks(*_args):
             an_empty_traitsdata,
             an_entity_trait_set,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,
@@ -748,6 +803,7 @@ def call_callbacks(*_args):
             two_refs,
             an_empty_traitsdata,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             success_callback,
             error_callback,
@@ -759,6 +815,7 @@ def call_callbacks(*_args):
             an_empty_traitsdata,
             an_entity_trait_set,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,  # success
@@ -798,6 +855,7 @@ def call_callbacks(*_args):
             two_refs,
             an_empty_traitsdata,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             lambda _idx, pager: pagers.append(pager),
             error_callback,
@@ -823,6 +881,7 @@ def test_when_zero_pageSize_then_indexError_is_raised(
                 two_refs,
                 an_empty_traitsdata,
                 page_size,
+                access.RelationsAccess.kWrite,
                 a_context,
                 success_callback,
                 error_callback,
@@ -864,6 +923,7 @@ def call_callbacks(*_args):
             a_ref,
             two_datas,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             success_callback,
             error_callback,
@@ -875,6 +935,7 @@ def call_callbacks(*_args):
             two_datas,
             an_entity_trait_set,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,  # success
@@ -899,6 +960,7 @@ def call_callbacks(*_args):
             a_ref,
             two_datas,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             success_callback,
             error_callback,
@@ -910,6 +972,7 @@ def call_callbacks(*_args):
             two_datas,
             an_entity_trait_set,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             a_host_session,
             mock.ANY,  # success
@@ -946,6 +1009,7 @@ def call_callbacks(*_args):
             a_ref,
             two_datas,
             page_size,
+            access.RelationsAccess.kWrite,
             a_context,
             lambda _idx, pager: pagers.append(pager),
             error_callback,
@@ -971,6 +1035,7 @@ def test_when_zero_pageSize_then_indexError_is_raised(
                 a_ref,
                 two_datas,
                 page_size,
+                access.RelationsAccess.kWrite,
                 a_context,
                 success_callback,
                 error_callback,
@@ -1016,11 +1081,22 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         manager.resolve(
-            some_refs, an_entity_trait_set, a_context, success_callback, error_callback
+            some_refs,
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            success_callback,
+            error_callback,
         )
 
         method.assert_called_once_with(
-            some_refs, an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            some_refs,
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         success_callback.assert_called_once_with(123, a_traitsdata)
@@ -1071,10 +1147,18 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_traitsdata = manager.resolve(a_ref, an_entity_trait_set, a_context)
+        actual_traitsdata = manager.resolve(
+            a_ref, an_entity_trait_set, access.ResolveAccess.kRead, a_context
+        )
 
         method.assert_called_once_with(
-            [a_ref], an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual_traitsdata is a_traitsdata
@@ -1106,10 +1190,16 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         with pytest.raises(expected_exception, match=batch_element_error.message) as exc:
-            manager.resolve(a_ref, an_entity_trait_set, a_context)
+            manager.resolve(a_ref, an_entity_trait_set, access.ResolveAccess.kRead, a_context)
 
         method.assert_called_once_with(
-            [a_ref], an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert exc.value.index == expected_index
@@ -1138,12 +1228,19 @@ def call_callbacks(*_args):
         actual_traitsdata = manager.resolve(
             a_ref,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
 
         method.assert_called_once_with(
-            [a_ref], an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual_traitsdata is a_traitsdata
@@ -1178,12 +1275,19 @@ def call_callbacks(*_args):
             manager.resolve(
                 a_ref,
                 an_entity_trait_set,
+                access.ResolveAccess.kRead,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
 
         method.assert_called_once_with(
-            [a_ref], an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert exc.value.index == expected_index
@@ -1212,12 +1316,19 @@ def call_callbacks(*_args):
         actual_traitsdata = manager.resolve(
             a_ref,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
 
         method.assert_called_once_with(
-            [a_ref], an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual_traitsdata is a_traitsdata
@@ -1247,12 +1358,19 @@ def call_callbacks(*_args):
         actual = manager.resolve(
             a_ref,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
 
         method.assert_called_once_with(
-            [a_ref], an_entity_trait_set, a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            an_entity_trait_set,
+            access.ResolveAccess.kRead,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual == batch_element_error
@@ -1278,11 +1396,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_traitsdatas = manager.resolve(some_refs, an_entity_trait_set, a_context)
+        actual_traitsdatas = manager.resolve(
+            some_refs, an_entity_trait_set, access.ResolveAccess.kRead, a_context
+        )
 
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1312,11 +1433,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_traitsdatas = manager.resolve(some_refs, an_entity_trait_set, a_context)
+        actual_traitsdatas = manager.resolve(
+            some_refs, an_entity_trait_set, access.ResolveAccess.kRead, a_context
+        )
 
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1357,11 +1481,12 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         with pytest.raises(expected_exception, match=batch_element_error.message) as exc:
-            manager.resolve(some_refs, an_entity_trait_set, a_context)
+            manager.resolve(some_refs, an_entity_trait_set, access.ResolveAccess.kRead, a_context)
 
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1395,6 +1520,7 @@ def call_callbacks(*_args):
         actual_traitsdatas = manager.resolve(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -1402,6 +1528,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1435,6 +1562,7 @@ def call_callbacks(*_args):
         actual_traitsdatas = manager.resolve(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -1442,6 +1570,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1485,6 +1614,7 @@ def call_callbacks(*_args):
             manager.resolve(
                 some_refs,
                 an_entity_trait_set,
+                access.ResolveAccess.kRead,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
@@ -1492,6 +1622,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1529,6 +1660,7 @@ def call_callbacks(*_args):
         actual_traitsdata_and_error = manager.resolve(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -1536,6 +1668,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1581,6 +1714,7 @@ def call_callbacks(*_args):
         actual_traitsdata_and_error = manager.resolve(
             entity_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -1588,6 +1722,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             entity_refs,
             an_entity_trait_set,
+            access.ResolveAccess.kRead,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1617,10 +1752,14 @@ def test_wraps_the_corresponding_method_of_the_held_interface(
         method = mock_manager_interface.mock.managementPolicy
         method.return_value = expected
 
-        actual = manager.managementPolicy(some_entity_trait_sets, a_context)
+        actual = manager.managementPolicy(
+            some_entity_trait_sets, access.PolicyAccess.kWrite, a_context
+        )
 
         assert actual == expected
-        method.assert_called_once_with(some_entity_trait_sets, a_context, a_host_session)
+        method.assert_called_once_with(
+            some_entity_trait_sets, access.PolicyAccess.kWrite, a_context, a_host_session
+        )
 
 
 class Test_Manager_preflight_callback_signature:
@@ -1653,11 +1792,22 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         manager.preflight(
-            some_refs, some_entity_traitsdatas, a_context, success_callback, error_callback
+            some_refs,
+            some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            success_callback,
+            error_callback,
         )
 
         method.assert_called_once_with(
-            some_refs, some_entity_traitsdatas, a_context, a_host_session, mock.ANY, mock.ANY
+            some_refs,
+            some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         success_callback.assert_called_once_with(123, some_refs[0])
@@ -1673,14 +1823,24 @@ def test_when_called_with_mixed_array_lengths_then_IndexError_is_raised(
             IndexError, match=expected_message.format(len(some_refs) - 1, len(some_refs))
         ):
             manager.preflight(
-                some_refs[1:], some_entity_traitsdatas, a_context, mock.Mock(), mock.Mock()
+                some_refs[1:],
+                some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
             )
 
         with pytest.raises(
             IndexError, match=expected_message.format(len(some_refs), len(some_refs) - 1)
         ):
             manager.preflight(
-                some_refs, some_entity_traitsdatas[1:], a_context, mock.Mock(), mock.Mock()
+                some_refs,
+                some_entity_traitsdatas[1:],
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
             )
 
     def test_when_traits_data_is_None_then_TypeError_is_raised(
@@ -1690,14 +1850,19 @@ def test_when_traits_data_is_None_then_TypeError_is_raised(
 
         with pytest.raises(TypeError):
             manager.preflight(
-                some_refs, some_entity_traitsdatas, a_context, mock.Mock(), mock.Mock()
+                some_refs,
+                some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
             )
 
 
 class Test_Manager_preflight_with_singular_default_overload:
     def test_when_traits_data_is_None_then_TypeError_is_raised(self, manager, a_ref, a_context):
         with pytest.raises(TypeError):
-            manager.preflight(a_ref, None, a_context)
+            manager.preflight(a_ref, None, access.PublishingAccess.kCreateRelated, a_context)
 
     def test_when_success_then_single_EntityReference_returned(
         self,
@@ -1717,10 +1882,18 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_ref = manager.preflight(a_ref, a_traitsdata, a_context)
+        actual_ref = manager.preflight(
+            a_ref, a_traitsdata, access.PublishingAccess.kCreateRelated, a_context
+        )
 
         method.assert_called_once_with(
-            [a_ref], [a_traitsdata], a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual_ref == a_different_ref
@@ -1752,10 +1925,18 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         with pytest.raises(expected_exception, match=batch_element_error.message) as exc:
-            manager.preflight(a_ref, a_traitsdata, a_context)
+            manager.preflight(
+                a_ref, a_traitsdata, access.PublishingAccess.kCreateRelated, a_context
+            )
 
         method.assert_called_once_with(
-            [a_ref], [a_traitsdata], a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert exc.value.index == expected_index
@@ -1766,7 +1947,11 @@ class Test_Manager_preflight_with_singular_throwing_overload:
     def test_when_traits_data_is_None_then_TypeError_is_raised(self, manager, a_ref, a_context):
         with pytest.raises(TypeError):
             manager.preflight(
-                a_ref, None, a_context, Manager.BatchElementErrorPolicyTag.kException
+                a_ref,
+                None,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                Manager.BatchElementErrorPolicyTag.kException,
             )
 
     def test_when_preflight_success_then_single_EntityReference_returned(
@@ -1790,12 +1975,19 @@ def call_callbacks(*_args):
         actual_ref = manager.preflight(
             a_ref,
             a_traitsdata,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
 
         method.assert_called_once_with(
-            [a_ref], [a_traitsdata], a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual_ref == a_different_ref
@@ -1831,12 +2023,19 @@ def call_callbacks(*_args):
             manager.preflight(
                 a_ref,
                 a_traitsdata,
+                access.PublishingAccess.kCreateRelated,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
 
         method.assert_called_once_with(
-            [a_ref], [a_traitsdata], a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert exc.value.index == expected_index
@@ -1846,7 +2045,14 @@ def call_callbacks(*_args):
 class Test_Manager_preflight_with_singular_variant_overload:
     def test_when_traits_data_is_None_then_TypeError_is_raised(self, manager, a_ref, a_context):
         with pytest.raises(TypeError):
-            manager.preflight(a_ref, None, a_context, Manager.BatchElementErrorPolicyTag.kVariant)
+            manager.preflight(
+                a_ref,
+                None,
+                access.PublishingAccess.kCreateRelated,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                Manager.BatchElementErrorPolicyTag.kVariant,
+            )
 
     def test_when_preflight_success_then_single_EntityReference_returned(
         self,
@@ -1869,12 +2075,19 @@ def call_callbacks(*_args):
         actual_ref = manager.preflight(
             a_ref,
             a_traitsdata,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
 
         method.assert_called_once_with(
-            [a_ref], [a_traitsdata], a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual_ref == a_different_ref
@@ -1904,12 +2117,19 @@ def call_callbacks(*_args):
         actual = manager.preflight(
             a_ref,
             a_traitsdata,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
 
         method.assert_called_once_with(
-            [a_ref], [a_traitsdata], a_context, a_host_session, mock.ANY, mock.ANY
+            [a_ref],
+            [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         assert actual == batch_element_error
@@ -1921,7 +2141,12 @@ def test_when_traits_data_is_None_then_TypeError_is_raised(
     ):
         some_entity_traitsdatas[-1] = None
         with pytest.raises(TypeError):
-            manager.preflight(some_refs, some_entity_traitsdatas, a_context)
+            manager.preflight(
+                some_refs,
+                some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+            )
 
     def test_when_success_then_multiple_EntityReferences_returned(
         self,
@@ -1942,11 +2167,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_refs = manager.preflight(some_refs, some_entity_traitsdatas, a_context)
+        actual_refs = manager.preflight(
+            some_refs, some_entity_traitsdatas, access.PublishingAccess.kCreateRelated, a_context
+        )
 
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -1974,11 +2202,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_refs = manager.preflight(some_refs, some_entity_traitsdatas, a_context)
+        actual_refs = manager.preflight(
+            some_refs, some_entity_traitsdatas, access.PublishingAccess.kCreateRelated, a_context
+        )
 
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2017,11 +2248,17 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         with pytest.raises(expected_exception, match=batch_element_error.message) as exc:
-            manager.preflight(some_refs, some_entity_traitsdatas, a_context)
+            manager.preflight(
+                some_refs,
+                some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+            )
 
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2041,6 +2278,7 @@ def test_when_traits_data_is_None_then_TypeError_is_raised(
             manager.preflight(
                 some_refs,
                 some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
@@ -2067,6 +2305,7 @@ def call_callbacks(*_args):
         actual_refs = manager.preflight(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -2074,6 +2313,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2104,6 +2344,7 @@ def call_callbacks(*_args):
         actual_refs = manager.preflight(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -2111,6 +2352,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2152,6 +2394,7 @@ def call_callbacks(*_args):
             manager.preflight(
                 some_refs,
                 some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
@@ -2159,6 +2402,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2178,6 +2422,7 @@ def test_when_traits_data_is_None_then_TypeError_is_raised(
             manager.preflight(
                 some_refs,
                 some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kVariant,
             )
@@ -2208,6 +2453,7 @@ def call_callbacks(*_args):
         actual_ref_or_error = manager.preflight(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -2215,6 +2461,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2261,6 +2508,7 @@ def call_callbacks(*_args):
         actual_ref_or_error = manager.preflight(
             entity_refs,
             traits_datas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -2268,6 +2516,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             entity_refs,
             traits_datas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2311,11 +2560,22 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         manager.register(
-            some_refs, some_entity_traitsdatas, a_context, success_callback, error_callback
+            some_refs,
+            some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            success_callback,
+            error_callback,
         )
 
         method.assert_called_once_with(
-            some_refs, some_entity_traitsdatas, a_context, a_host_session, mock.ANY, mock.ANY
+            some_refs,
+            some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
+            a_context,
+            a_host_session,
+            mock.ANY,
+            mock.ANY,
         )
 
         success_callback.assert_called_once_with(123, some_refs[0])
@@ -2331,21 +2591,38 @@ def test_when_called_with_mixed_array_lengths_then_IndexError_is_raised(
             IndexError, match=expected_message.format(len(some_refs) - 1, len(some_refs))
         ):
             manager.register(
-                some_refs[1:], some_entity_traitsdatas, a_context, mock.Mock(), mock.Mock()
+                some_refs[1:],
+                some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
             )
 
         with pytest.raises(
             IndexError, match=expected_message.format(len(some_refs), len(some_refs) - 1)
         ):
             manager.register(
-                some_refs, some_entity_traitsdatas[1:], a_context, mock.Mock(), mock.Mock()
+                some_refs,
+                some_entity_traitsdatas[1:],
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
             )
 
     def test_when_called_with_None_data_then_TypeError_is_raised(
         self, manager, some_refs, a_context, a_traitsdata
     ):
         with pytest.raises(TypeError):
-            manager.register(some_refs, [a_traitsdata, None], a_context, mock.Mock(), mock.Mock())
+            manager.register(
+                some_refs,
+                [a_traitsdata, None],
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+                mock.Mock(),
+                mock.Mock(),
+            )
 
 
 class Test_Manager_register_with_singular_default_overload:
@@ -2367,11 +2644,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_ref = manager.register(a_ref, a_traitsdata, a_context)
+        actual_ref = manager.register(
+            a_ref, a_traitsdata, access.PublishingAccess.kCreateRelated, a_context
+        )
 
         method.assert_called_once_with(
             [a_ref],
             [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2407,11 +2687,14 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         with pytest.raises(expected_exception, match=batch_element_error.message) as exc:
-            manager.register(a_ref, a_traitsdata, a_context)
+            manager.register(
+                a_ref, a_traitsdata, access.PublishingAccess.kCreateRelated, a_context
+            )
 
         method.assert_called_once_with(
             [a_ref],
             [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2444,6 +2727,7 @@ def call_callbacks(*_args):
         actual_ref = manager.register(
             a_ref,
             a_traitsdata,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -2451,6 +2735,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             [a_ref],
             [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2489,6 +2774,7 @@ def call_callbacks(*_args):
             manager.register(
                 a_ref,
                 a_traitsdata,
+                access.PublishingAccess.kCreateRelated,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
@@ -2496,6 +2782,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             [a_ref],
             [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2528,6 +2815,7 @@ def call_callbacks(*_args):
         actual_ref = manager.register(
             a_ref,
             a_traitsdata,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -2535,6 +2823,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             [a_ref],
             [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2568,6 +2857,7 @@ def call_callbacks(*_args):
         actual = manager.register(
             a_ref,
             a_traitsdata,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -2575,6 +2865,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             [a_ref],
             [a_traitsdata],
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2604,11 +2895,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_refs = manager.register(some_refs, some_entity_traitsdatas, a_context)
+        actual_refs = manager.register(
+            some_refs, some_entity_traitsdatas, access.PublishingAccess.kCreateRelated, a_context
+        )
 
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2636,11 +2930,14 @@ def call_callbacks(*_args):
 
         method.side_effect = call_callbacks
 
-        actual_refs = manager.register(some_refs, some_entity_traitsdatas, a_context)
+        actual_refs = manager.register(
+            some_refs, some_entity_traitsdatas, access.PublishingAccess.kCreateRelated, a_context
+        )
 
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2679,11 +2976,17 @@ def call_callbacks(*_args):
         method.side_effect = call_callbacks
 
         with pytest.raises(expected_exception, match=batch_element_error.message) as exc:
-            manager.register(some_refs, some_entity_traitsdatas, a_context)
+            manager.register(
+                some_refs,
+                some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
+                a_context,
+            )
 
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2717,6 +3020,7 @@ def call_callbacks(*_args):
         actual_refs = manager.register(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -2724,6 +3028,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2754,6 +3059,7 @@ def call_callbacks(*_args):
         actual_refs = manager.register(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kException,
         )
@@ -2761,6 +3067,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2803,6 +3110,7 @@ def call_callbacks(*_args):
             manager.register(
                 some_refs,
                 some_entity_traitsdatas,
+                access.PublishingAccess.kCreateRelated,
                 a_context,
                 Manager.BatchElementErrorPolicyTag.kException,
             )
@@ -2810,6 +3118,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2847,6 +3156,7 @@ def call_callbacks(*_args):
         actual_ref_or_error = manager.register(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -2854,6 +3164,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             some_refs,
             some_entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2900,6 +3211,7 @@ def call_callbacks(*_args):
         actual_ref_or_error = manager.register(
             entity_refs,
             entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             Manager.BatchElementErrorPolicyTag.kVariant,
         )
@@ -2907,6 +3219,7 @@ def call_callbacks(*_args):
         method.assert_called_once_with(
             entity_refs,
             entity_traitsdatas,
+            access.PublishingAccess.kCreateRelated,
             a_context,
             a_host_session,
             mock.ANY,
@@ -2933,7 +3246,6 @@ def test_context_is_created_with_expected_properties(
 
         context_a = manager.createContext()
 
-        assert context_a.access == Context.Access.kUnknown
         assert context_a.managerState is state_a
         assert isinstance(context_a.locale, TraitsData)
         assert context_a.locale.traitSet() == set()
@@ -2951,7 +3263,6 @@ def test_when_called_with_parent_then_props_copied_and_createState_called_with_p
         state_a = managerApi.ManagerStateBase()
         mock_manager_interface.mock.createState.return_value = state_a
         context_a = manager.createContext()
-        context_a.access = Context.Access.kWrite
         context_a.locale = TraitsData()
         mock_manager_interface.mock.reset_mock()
 
@@ -2962,7 +3273,6 @@ def test_when_called_with_parent_then_props_copied_and_createState_called_with_p
 
         assert context_b is not context_a
         assert context_b.managerState is state_b
-        assert context_b.access == context_a.access
         assert context_b.locale == context_a.locale
         mock_manager_interface.mock.createChildState.assert_called_once_with(
             state_a, a_host_session
@@ -2979,7 +3289,6 @@ def test_when_called_with_parent_then_locale_is_deep_copied(
         mock_manager_interface.mock.createState.return_value = state_a
         mock_manager_interface.mock.createChildState.return_value = state_a
         context_a = manager.createContext()
-        context_a.access = Context.Access.kWrite
         context_a.locale = original_locale
 
         context_b = manager.createChildContext(context_a)
@@ -2992,11 +3301,9 @@ def test_when_called_with_parent_with_no_managerState_then_createChildState_is_n
         self, manager, mock_manager_interface
     ):
         context_a = Context()
-        context_a.access = Context.Access.kWrite
         context_a.locale = TraitsData()
         context_b = manager.createChildContext(context_a)
 
-        assert context_b.access == context_a.access
         assert context_b.locale == context_b.locale
         mock_manager_interface.mock.createChildState.assert_not_called()
 
@@ -3159,7 +3466,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_defaultEntityReference_success_cb(mock_manager_interface):
     def invoke(idx, entity_ref):
-        callback = mock_manager_interface.mock.defaultEntityReference.call_args[0][3]
+        callback = mock_manager_interface.mock.defaultEntityReference.call_args[0][4]
         callback(idx, entity_ref)
 
     return invoke
@@ -3168,7 +3475,7 @@ def invoke(idx, entity_ref):
 @pytest.fixture
 def invoke_defaultEntityReference_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.defaultEntityReference.call_args[0][4]
+        callback = mock_manager_interface.mock.defaultEntityReference.call_args[0][5]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3177,7 +3484,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_getWithRelationshipPaged_success_cb(mock_manager_interface):
     def invoke(idx, entityReferencesPagerInterface):
-        callback = mock_manager_interface.mock.getWithRelationshipPaged.call_args[0][6]
+        callback = mock_manager_interface.mock.getWithRelationshipPaged.call_args[0][7]
         callback(idx, entityReferencesPagerInterface)
 
     return invoke
@@ -3186,7 +3493,7 @@ def invoke(idx, entityReferencesPagerInterface):
 @pytest.fixture
 def invoke_getWithRelationshipsPaged_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.getWithRelationshipsPaged.call_args[0][7]
+        callback = mock_manager_interface.mock.getWithRelationshipsPaged.call_args[0][8]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3195,7 +3502,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_getWithRelationshipsPaged_success_cb(mock_manager_interface):
     def invoke(idx, entityReferencesPagerInterface):
-        callback = mock_manager_interface.mock.getWithRelationshipsPaged.call_args[0][6]
+        callback = mock_manager_interface.mock.getWithRelationshipsPaged.call_args[0][7]
         callback(idx, entityReferencesPagerInterface)
 
     return invoke
@@ -3204,7 +3511,7 @@ def invoke(idx, entityReferencesPagerInterface):
 @pytest.fixture
 def invoke_getWithRelationshipPaged_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.getWithRelationshipPaged.call_args[0][7]
+        callback = mock_manager_interface.mock.getWithRelationshipPaged.call_args[0][8]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3213,7 +3520,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_getWithRelationship_success_cb(mock_manager_interface):
     def invoke(idx, entityReferences):
-        callback = mock_manager_interface.mock.getWithRelationship.call_args[0][5]
+        callback = mock_manager_interface.mock.getWithRelationship.call_args[0][6]
         callback(idx, entityReferences)
 
     return invoke
@@ -3222,7 +3529,7 @@ def invoke(idx, entityReferences):
 @pytest.fixture
 def invoke_getWithRelationship_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.getWithRelationship.call_args[0][6]
+        callback = mock_manager_interface.mock.getWithRelationship.call_args[0][7]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3231,7 +3538,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_getWithRelationships_success_cb(mock_manager_interface):
     def invoke(idx, entityReferences):
-        callback = mock_manager_interface.mock.getWithRelationships.call_args[0][5]
+        callback = mock_manager_interface.mock.getWithRelationships.call_args[0][6]
         callback(idx, entityReferences)
 
     return invoke
@@ -3240,7 +3547,7 @@ def invoke(idx, entityReferences):
 @pytest.fixture
 def invoke_getWithRelationships_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.getWithRelationships.call_args[0][6]
+        callback = mock_manager_interface.mock.getWithRelationships.call_args[0][7]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3249,7 +3556,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_resolve_success_cb(mock_manager_interface):
     def invoke(idx, traits_data):
-        callback = mock_manager_interface.mock.resolve.call_args[0][4]
+        callback = mock_manager_interface.mock.resolve.call_args[0][5]
         callback(idx, traits_data)
 
     return invoke
@@ -3258,7 +3565,7 @@ def invoke(idx, traits_data):
 @pytest.fixture
 def invoke_resolve_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.resolve.call_args[0][5]
+        callback = mock_manager_interface.mock.resolve.call_args[0][6]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3267,7 +3574,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_preflight_success_cb(mock_manager_interface):
     def invoke(idx, traits_data):
-        callback = mock_manager_interface.mock.preflight.call_args[0][4]
+        callback = mock_manager_interface.mock.preflight.call_args[0][5]
         callback(idx, traits_data)
 
     return invoke
@@ -3276,7 +3583,7 @@ def invoke(idx, traits_data):
 @pytest.fixture
 def invoke_preflight_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.preflight.call_args[0][5]
+        callback = mock_manager_interface.mock.preflight.call_args[0][6]
         callback(idx, batch_element_error)
 
     return invoke
@@ -3285,7 +3592,7 @@ def invoke(idx, batch_element_error):
 @pytest.fixture
 def invoke_register_success_cb(mock_manager_interface):
     def invoke(idx, traits_data):
-        callback = mock_manager_interface.mock.register.call_args[0][4]
+        callback = mock_manager_interface.mock.register.call_args[0][5]
         callback(idx, traits_data)
 
     return invoke
@@ -3294,7 +3601,7 @@ def invoke(idx, traits_data):
 @pytest.fixture
 def invoke_register_error_cb(mock_manager_interface):
     def invoke(idx, batch_element_error):
-        callback = mock_manager_interface.mock.register.call_args[0][5]
+        callback = mock_manager_interface.mock.register.call_args[0][6]
         callback(idx, batch_element_error)
 
     return invoke
diff --git a/src/openassetio-python/tests/package/managerApi/test_managerinterface.py b/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
index 06d2f2d1b..2abbec530 100644
--- a/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
+++ b/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
@@ -31,6 +31,7 @@
     ManagerStateBase,
     EntityReferencePagerInterface,
 )
+from openassetio.access import RelationsAccess, DefaultEntityAccess
 
 
 class Test_ManagerInterface_identifier:
@@ -167,7 +168,12 @@ def test_when_given_single_trait_set_then_calls_error_once(
         traitsSets = [set()]
 
         refs = manager_interface.defaultEntityReference(
-            traitsSets, Context(), a_host_session, success_callback, error_callback
+            traitsSets,
+            DefaultEntityAccess.kRead,
+            Context(),
+            a_host_session,
+            success_callback,
+            error_callback,
         )
 
         success_callback.assert_not_called()
@@ -187,7 +193,12 @@ def test_when_given_multiple_trait_set_then_calls_error_once_for_each_trait_set(
         traitsSets = [set(), set(), set()]
 
         refs = manager_interface.defaultEntityReference(
-            traitsSets, Context(), a_host_session, success_callback, error_callback
+            traitsSets,
+            DefaultEntityAccess.kRead,
+            Context(),
+            a_host_session,
+            success_callback,
+            error_callback,
         )
 
         success_callback.assert_not_called()
@@ -217,6 +228,7 @@ def test_returns_empty_list_for_each_input(self, manager_interface, a_host_sessi
                 refs,
                 TraitsData(),
                 set(),
+                RelationsAccess.kRead,
                 Context(),
                 a_host_session,
                 success_callback,
@@ -247,6 +259,7 @@ def test_returns_empty_list_for_each_input(self, manager_interface, a_host_sessi
                 EntityReference(""),
                 rels,
                 set(),
+                RelationsAccess.kRead,
                 Context(),
                 a_host_session,
                 success_callback,
@@ -282,6 +295,7 @@ def test_returns_dummy_pager_for_each_input(self, manager_interface, a_host_sess
                 TraitsData(),
                 set(),
                 1,
+                RelationsAccess.kRead,
                 Context(),
                 a_host_session,
                 success_callback,
@@ -299,10 +313,10 @@ def test_returns_dummy_pager_for_each_input(self, manager_interface, a_host_sess
             success_callback.reset_mock()
 
 
-class Test_ManagerInterface_getWithRelationships:
+class Test_ManagerInterface_getWithRelationshipsPaged:
     def test_method_defined_in_cpp(self, method_introspector):
         assert not method_introspector.is_defined_in_python(
-            ManagerInterface.getWithRelationshipPaged
+            ManagerInterface.getWithRelationshipsPaged
         )
         assert method_introspector.is_implemented_once(
             ManagerInterface, "getWithRelationshipsPaged"
@@ -322,6 +336,7 @@ def test_returns_dummy_pager_for_each_input(self, manager_interface, a_host_sess
                 rels,
                 set(),
                 1,
+                RelationsAccess.kRead,
                 Context(),
                 a_host_session,
                 success_callback,
diff --git a/src/openassetio-python/tests/package/test/manager/resources/plugins/StubManager.py b/src/openassetio-python/tests/package/test/manager/resources/plugins/StubManager.py
index eace24e1b..d0008c291 100644
--- a/src/openassetio-python/tests/package/test/manager/resources/plugins/StubManager.py
+++ b/src/openassetio-python/tests/package/test/manager/resources/plugins/StubManager.py
@@ -58,7 +58,7 @@ def initialize(self, managerSettings, hostSession):
         self.__settings = managerSettings
         self.__info["host_identifier"] = hostSession.host().identifier()
 
-    def managementPolicy(self, traitSets, context, hostSession):
+    def managementPolicy(self, traitSets, access, context, hostSession):
         # pylint: disable=unused-argument
         return [TraitsData() for _ in traitSets]
 
diff --git a/src/openassetio-python/tests/package/test/manager/test_harness.py b/src/openassetio-python/tests/package/test/manager/test_harness.py
index b9f3adaad..e7a461cf8 100644
--- a/src/openassetio-python/tests/package/test/manager/test_harness.py
+++ b/src/openassetio-python/tests/package/test/manager/test_harness.py
@@ -158,16 +158,6 @@ def test_has_test_harness_locale(self, a_test_case):
         context = a_test_case.createTestContext()
         assert context.locale is a_test_case._locale  # pylint: disable=protected-access
 
-    def test_when_supplied_with_access_then_the_context_access_is_set(self, a_test_case):
-        context = a_test_case.createTestContext(Context.Access.kWrite)
-        assert context.access == Context.Access.kWrite
-
-        context = a_test_case.createTestContext(Context.Access.kRead)
-        assert context.access == Context.Access.kRead
-
-        context = a_test_case.createTestContext(Context.Access.kCreateRelated)
-        assert context.access == Context.Access.kCreateRelated
-
 
 class Test_FixtureAugmentedTestCase_assertIsStringKeyPrimitiveValueDict:
     def test_when_not_dict_then_fails(self, a_test_case):
diff --git a/src/openassetio-python/tests/package/test_access.py b/src/openassetio-python/tests/package/test_access.py
new file mode 100644
index 000000000..c61ab7f6e
--- /dev/null
+++ b/src/openassetio-python/tests/package/test_access.py
@@ -0,0 +1,133 @@
+#
+#   Copyright 2023 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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+"""
+Tests that cover the openassetio.access namespace.
+"""
+import collections
+
+# pylint: disable=invalid-name,redefined-outer-name
+# pylint: disable=missing-class-docstring,missing-function-docstring
+import pytest
+
+from openassetio.access import (
+    PolicyAccess,
+    RelationsAccess,
+    ResolveAccess,
+    PublishingAccess,
+    DefaultEntityAccess,
+    kAccessNames,
+)
+
+
+class Test_root_enum:
+    def test_expected_constants_are_unique_and_exhaustive(self, root_enum):
+        assert_expected_enum_values(
+            root_enum,
+            root_enum.kRead,
+            root_enum.kWrite,
+            root_enum.kCreateRelated,
+        )
+
+        # The following is just a confidence check of pybind11 and our
+        # test fixtures.
+
+        all_enum_values = extract_comparable_values_from_enum_values(
+            root_enum.kRead, root_enum.kWrite, root_enum.kCreateRelated
+        )
+        assert len(all_enum_values) == 3
+        assert len(set(member.name for member in all_enum_values)) == 3
+        assert len(set(member.value for member in all_enum_values)) == 3
+
+        all_enum_values_from_class = extract_comparable_values_from_enum_class(root_enum)
+        assert len(all_enum_values_from_class) == 3
+        assert len(set(member.name for member in all_enum_values_from_class)) == 3
+        assert len(set(member.value for member in all_enum_values_from_class)) == 3
+
+
+class Test_kAccessNames:
+    def test_access_names_indices_match_constants(self, root_enum):
+        assert kAccessNames[int(root_enum.kRead)] == "read"
+        assert kAccessNames[int(root_enum.kWrite)] == "write"
+        assert kAccessNames[int(root_enum.kCreateRelated)] == "createRelated"
+
+
+class Test_PolicyAccess:
+    def test_has_expected_constants(self, root_enum):
+        assert_expected_enum_values(
+            PolicyAccess,
+            root_enum.kRead,
+            root_enum.kWrite,
+            root_enum.kCreateRelated,
+        )
+
+
+class Test_RelationsAccess:
+    def test_has_expected_constants(self, root_enum):
+        assert_expected_enum_values(
+            RelationsAccess,
+            root_enum.kRead,
+            root_enum.kWrite,
+            root_enum.kCreateRelated,
+        )
+
+
+class Test_ResolveAccess:
+    def test_has_expected_constants(self, root_enum):
+        assert_expected_enum_values(ResolveAccess, root_enum.kRead, root_enum.kWrite)
+
+
+class Test_PublishingAccess:
+    def test_has_expected_constants(self, root_enum):
+        assert_expected_enum_values(PublishingAccess, root_enum.kWrite, root_enum.kCreateRelated)
+
+
+class Test_DefaultEntityAccess:
+    def test_has_expected_constants(self, root_enum):
+        assert_expected_enum_values(
+            DefaultEntityAccess,
+            root_enum.kRead,
+            root_enum.kWrite,
+            root_enum.kCreateRelated,
+        )
+
+
+@pytest.fixture
+def root_enum():
+    """
+    An enum class that contains all possible values that we support.
+
+    Uses PolicyAccess as a representative enum with all values.
+    """
+    return PolicyAccess
+
+
+def assert_expected_enum_values(enum_under_test, *expected_values):
+    expected = extract_comparable_values_from_enum_values(*expected_values)
+    actual = extract_comparable_values_from_enum_class(enum_under_test)
+    assert actual == expected
+
+
+def extract_comparable_values_from_enum_class(enum_class):
+    return extract_comparable_values_from_enum_values(*enum_class.__members__.values())
+
+
+def extract_comparable_values_from_enum_values(*enum_values):
+    return set(ComparableEnumValue(member.name, member.value) for member in enum_values)
+
+
+# Required since enum values under different enum classes compare
+# non-equal, even if their string name and integer values are identical.
+ComparableEnumValue = collections.namedtuple("ComparableEnumValue", ("name", "value"))
diff --git a/src/openassetio-python/tests/package/test_context.py b/src/openassetio-python/tests/package/test_context.py
index 8a7f80e08..90da20b15 100644
--- a/src/openassetio-python/tests/package/test_context.py
+++ b/src/openassetio-python/tests/package/test_context.py
@@ -24,31 +24,9 @@
 from openassetio import Context, managerApi, TraitsData
 
 
-class Test_Context:
-    def test_access_constants_are_unique(self):
-        consts = (
-            Context.Access.kRead,
-            Context.Access.kWrite,
-            Context.Access.kCreateRelated,
-            Context.Access.kUnknown,
-        )
-        assert len(set(consts)) == len(consts)
-
-    def test_access_constants_are_not_exported(self):
-        with pytest.raises(AttributeError):
-            Context.kRead  # pylint: disable=pointless-statement
-
-    def test_access_names_indices_match_constants(self):
-        assert Context.kAccessNames[int(Context.Access.kRead)] == "read"
-        assert Context.kAccessNames[int(Context.Access.kWrite)] == "write"
-        assert Context.kAccessNames[int(Context.Access.kCreateRelated)] == "createRelated"
-        assert Context.kAccessNames[int(Context.Access.kUnknown)] == "unknown"
-
-
 class Test_Context_init:
     def test_when_constructed_with_no_args_then_has_default_configuration(self):
         context = Context()
-        assert context.access == Context.Access.kUnknown
         assert context.locale is None
         assert context.managerState is None
 
@@ -56,38 +34,16 @@ def test_when_constructed_with_args_then_has_configuration_from_args(self):
         class TestState(managerApi.ManagerStateBase):
             pass
 
-        expected_access = Context.Access.kRead
         expected_locale = TraitsData()
         expected_state = TestState()
 
-        a_context = Context(expected_access, expected_locale, expected_state)
+        a_context = Context(expected_locale, expected_state)
 
-        assert a_context.access == expected_access
         assert a_context.locale is expected_locale
         assert a_context.managerState is expected_state
         assert isinstance(a_context.managerState, TestState)
 
 
-class Test_Context_access:
-    def test_when_set_to_unknown_type_then_raises_ValueError(self, a_context):
-        expected_msg = (
-            r"incompatible function arguments.*\n"
-            r".*arg0: openassetio._openassetio.Context.Access"
-        )
-
-        with pytest.raises(TypeError, match=expected_msg):
-            a_context.access = 0
-
-    def test_when_set_to_known_value_then_stores_that_value(self, a_context):
-        for expected_access in (
-            Context.Access.kRead,
-            Context.Access.kWrite,
-            Context.Access.kCreateRelated,
-        ):
-            a_context.access = expected_access
-            assert a_context.access == expected_access
-
-
 class Test_Context_locale:
     def test_when_set_to_unknown_value_then_raises_ValueError(self, a_context):
         expected_msg = (
@@ -135,34 +91,6 @@ def test_when_set_to_valid_data_then_holds_reference_to_that_data(self, a_contex
         assert actual_data is expected_data
 
 
-class Test_Context_isForRead:
-    def test_when_called_with_read_context_then_returns_true(self):
-        assert Context(access=Context.Access.kRead).isForRead() is True
-
-    def test_when_called_with_write_context_then_returns_false(self):
-        assert Context(access=Context.Access.kWrite).isForRead() is False
-
-    def test_when_called_with_createRelated_context_then_returns_false(self):
-        assert Context(access=Context.Access.kCreateRelated).isForRead() is False
-
-    def test_when_called_with_unknown_access_context_then_returns_false(self):
-        assert Context(access=Context.Access.kUnknown).isForRead() is False
-
-
-class Test_Context_isForWrite:
-    def test_when_called_with_write_context_then_returns_true(self):
-        assert Context(access=Context.Access.kWrite).isForWrite() is True
-
-    def test_when_called_with_createRelated_context_then_returns_true(self):
-        assert Context(access=Context.Access.kCreateRelated).isForWrite() is True
-
-    def test_when_called_with_read_context_then_returns_false(self):
-        assert Context(access=Context.Access.kRead).isForWrite() is False
-
-    def test_when_called_with_unknown_access_context_then_returns_false(self):
-        assert Context(access=Context.Access.kUnknown).isForWrite() is False
-
-
 @pytest.fixture
 def a_context():
     return Context()