Repository: OpenAssetIO/OpenAssetIO (language: python)

Issue:
Add `kManagerDriven` and `kRequired` to `PolicyAccess`
(Capturing some nascent rumination on issues that came up working on #31) 

## What

- Add `kManagerDriven` and `kRequired` to `PolicyAccess` for when a Host wishes to determine which traits a Manager requires for publishing or can provide for a future entity.
- Remove `ResolvesFutureEntitiesTrait` from MediaCreation

Such that, from the Hosts perspective, `PolicyAccess` cases are defined as follows for _entity_ trait sets:

- `kRead`: What is your policy for existing entities with the supplied trait set, and which traits are you capable of resolving data for.
- `kWrite`: What is your policy when I wish to publish entities with the supplied trait set, that I already hold the data for, and which traits are you capable of storing data for?
- `kManagerDriven`: What is your policy when I wish to publish entities with the supplied trait set, and which of the traits are you capable of providing data for (via resolve) for the yet-to-be-published entity. 
- `kRequired`: What is your policy when I wish to publish entities with the supplied trait set, and which of the traits are required.
- `kCreateRelated`: As per `kWrite` (included for parity with `PublishAccess`.

## Why

Managers may required the data for certain properties to be present in order to publish an entity.  The core API also defines the `prefilght` -> `resolve` -> `register` workflow for when a Host wishes or requires to derive some of an entities properties from the Manager during publish. For example - the location to store a render, or what frame range to export.

There is however, no way for a Host to query ahead of time which traits the Manager requires or may be able to resolve for future entities. This means that the Host has no way to present a coherent user experience without "trying first". The only indication that a Manager is even capable of resolving future data is via the tenuous [`ResolvesFutureEntities`](https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/blob/4f73fb6aa062d5807c4eb8010ff792da391b7d4b/traits.yml#L221C7-L221C29) trait from the optional and distinct MediaCreation library, which provides no information about which specific traits are derivable.

We need a solution within the core API, given these are are core workflows.

Using `managementPolicy` to provide this data has the benefit that as the method does not take an Entity Reference, it is a broad application-level query which allows Hosts to preemptively manage user experience if they are not able to provide data a manager needs or the manager isn't able to provide data the host needs. The resulting low call count hopefully offsets that several calls may be needed to define final behaviour.

It also constrains this business logic to specific entity trait sets, which simplifies implementation as it removes the need (or possibility) for this to vary between entities of the same trait set, which feels beneficial in what is an already complex working environment.

It would be left to Trait definitions to define which particular properties are required or optional. A trait being indicated as supported/required/derivable by `mangementPolicy` would, by contract, required that Hosts provide those properties, and/or Managers would populate them during `resolve`.

## Notes

There are some other potential ideas to help with other areas:

### `ResolveAccess`

`ResolveAccess::kWrite` could be changed to `ResolveAccess::kManagerDriven`, as resolving for write is always the `kManagerDriven` policy case (as if the Host already has the data it wouldn't resolve it). This would be a breaking change though.

### Preflight help

An extension of this could be to add `kWrite_Collaborative` and `kCreateRelated_Collaborative` to `PublishingAccess` for when a Host will attempt to resolve derivable traits from the `preflight` reference. This would help inform the business logic in `preflight` to validate the input. It doesn't completely solve the problem though as it doesn't _guarantee_ that the Host would try to resolve _all_ the missing required properties. So it might not be worth complicating the `PublishingAcces` enum given its not a robust solution. 

An alternative could be to add an optional trait set to `preflight` that indicates which traits will be resolved during publishing. However this list only improves validation, as the host still woudln't know which traits a manager might be able to resolve without the above proposed mechanism, and it woudln't provide means to refine that list beforehand. It also has complexities when publishing multiple entity trait sets in one batch.

It would perhaps be more robust (and less intrusive) to add method(s) to pre-validate the TraitsData (that would be) passed to `preflight` instead, that return programmatically inspectable failure descriptions (this allows value ranges to be checked as well as just missing data) this, when also supplied with a list of "traits Im going to resolve during publish" it would perhaps be the most effective way to pro-actively defend against "try it an see if it explodes" workflows in relation to specific entity references and planed publishing approaches. Being entity-specific though, so is not suitable for high-level application behaviour switching so is probably not an alternative to the above proposed `managementPolicy` approach. This would also need consideration in relations to the "permissions" mechanism.

### Alternatives

 - Splitting `managementPolicy` into different methods eg: `managementPolicy` and `publishingPolicy` that simplifies the Access enums and makes it more obvious.  A much bigger scope of change though.

## ACs
```[tasklist]
- [ ] Add `kManagerProvided` and `kRequired` to `PolicyAccess`
- [x] Remove `ResolvesFutureEntitiesTrait` from MediaCreation (might already be done)
- [ ] https://github.com/OpenAssetIO/OpenAssetIO-Manager-BAL/issues/98
- [ ] https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/issues/75
- [ ] Scan docs and update any publishing examples
- [ ] Update existing publishing examples to account for kRequired/kManagerProvided
```


Remove `access` from `Context`
## What

👆

## Why

https://github.com/OpenAssetIO/OpenAssetIO/blob/main/doc/decisions/DR019-Remove-access-from-the-context.md

## Acceptance Criteria

- `access` removed from `Context`
- New specific enums added to the `openassetio::access` namespace for specific workflows:
  - **`PolicyAccess` (`managementPolicy`)**: `kRead`, `kWrite`, `kCreateRelated`
  - **`RelationsAccess` (`getWithRelationship*`)**: `kRead`, `kWrite`, `kCreateRelated`
  - **`ResolveAccess` (`resolve`)**: `kRead`, `kWrite`
  - **`PublishingAccess` (`preflight`/`register`)**: `kWrite`, `kCreateRelated`
  - Matching enum members should have the same value across the definitions.
  - `kAccessNames` array persisted to allow bi-directional string conversions.
 - Methods update with the above as a[nother] positional argument `access` at the end of the batch arguments, before `context` or `hostSession`, callbacks.
 - Validate existing docs that refer to `access` are still meaningful, no additional docs required.
 - `Examples.dox` updated
 - `simpleResolver` updated
 - Significant note in `RELEASE_NOTES` via block quote with `**Warning:**` heading.
- Python bindings hoist contents of `access` in python `access/__init__.py` to allow `from openassetio.access import ResolveAccess`

apiCompliance Rework
```[tasklist]
### Tasks
- [ ] #473
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/1242
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/1243
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/1040
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/604
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/1025
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/659
- [ ] https://github.com/OpenAssetIO/OpenAssetIO/issues/1338
```

Add `kManagerDriven` and `kRequired` to `PolicyAccess`
(Capturing some nascent rumination on issues that came up working on #31) 

## What

- Add `kManagerDriven` and `kRequired` to `PolicyAccess` for when a Host wishes to determine which traits a Manager requires for publishing or can provide for a future entity.
- Remove `ResolvesFutureEntitiesTrait` from MediaCreation

Such that, from the Hosts perspective, `PolicyAccess` cases are defined as follows for _entity_ trait sets:

- `kRead`: What is your policy for existing entities with the supplied trait set, and which traits are you capable of resolving data for.
- `kWrite`: What is your policy when I wish to publish entities with the supplied trait set, that I already hold the data for, and which traits are you capable of storing data for?
- `kManagerDriven`: What is your policy when I wish to publish entities with the supplied trait set, and which of the traits are you capable of providing data for (via resolve) for the yet-to-be-published entity. 
- `kRequired`: What is your policy when I wish to publish entities with the supplied trait set, and which of the traits are required.
- `kCreateRelated`: As per `kWrite` (included for parity with `PublishAccess`.

## Why

Managers may required the data for certain properties to be present in order to publish an entity.  The core API also defines the `prefilght` -> `resolve` -> `register` workflow for when a Host wishes or requires to derive some of an entities properties from the Manager during publish. For example - the location to store a render, or what frame range to export.

There is however, no way for a Host to query ahead of time which traits the Manager requires or may be able to resolve for future entities. This means that the Host has no way to present a coherent user experience without "trying first". The only indication that a Manager is even capable of resolving future data is via the tenuous [`ResolvesFutureEntities`](https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/blob/4f73fb6aa062d5807c4eb8010ff792da391b7d4b/traits.yml#L221C7-L221C29) trait from the optional and distinct MediaCreation library, which provides no information about which specific traits are derivable.

We need a solution within the core API, given these are are core workflows.

Using `managementPolicy` to provide this data has the benefit that as the method does not take an Entity Reference, it is a broad application-level query which allows Hosts to preemptively manage user experience if they are not able to provide data a manager needs or the manager isn't able to provide data the host needs. The resulting low call count hopefully offsets that several calls may be needed to define final behaviour.

It also constrains this business logic to specific entity trait sets, which simplifies implementation as it removes the need (or possibility) for this to vary between entities of the same trait set, which feels beneficial in what is an already complex working environment.

It would be left to Trait definitions to define which particular properties are required or optional. A trait being indicated as supported/required/derivable by `mangementPolicy` would, by contract, required that Hosts provide those properties, and/or Managers would populate them during `resolve`.

## Notes

There are some other potential ideas to help with other areas:

### `ResolveAccess`

`ResolveAccess::kWrite` could be changed to `ResolveAccess::kManagerDriven`, as resolving for write is always the `kManagerDriven` policy case (as if the Host already has the data it wouldn't resolve it). This would be a breaking change though.

### Preflight help

An extension of this could be to add `kWrite_Collaborative` and `kCreateRelated_Collaborative` to `PublishingAccess` for when a Host will attempt to resolve derivable traits from the `preflight` reference. This would help inform the business logic in `preflight` to validate the input. It doesn't completely solve the problem though as it doesn't _guarantee_ that the Host would try to resolve _all_ the missing required properties. So it might not be worth complicating the `PublishingAcces` enum given its not a robust solution. 

An alternative could be to add an optional trait set to `preflight` that indicates which traits will be resolved during publishing. However this list only improves validation, as the host still woudln't know which traits a manager might be able to resolve without the above proposed mechanism, and it woudln't provide means to refine that list beforehand. It also has complexities when publishing multiple entity trait sets in one batch.

It would perhaps be more robust (and less intrusive) to add method(s) to pre-validate the TraitsData (that would be) passed to `preflight` instead, that return programmatically inspectable failure descriptions (this allows value ranges to be checked as well as just missing data) this, when also supplied with a list of "traits Im going to resolve during publish" it would perhaps be the most effective way to pro-actively defend against "try it an see if it explodes" workflows in relation to specific entity references and planed publishing approaches. Being entity-specific though, so is not suitable for high-level application behaviour switching so is probably not an alternative to the above proposed `managementPolicy` approach. This would also need consideration in relations to the "permissions" mechanism.

### Alternatives

 - Splitting `managementPolicy` into different methods eg: `managementPolicy` and `publishingPolicy` that simplifies the Access enums and makes it more obvious.  A much bigger scope of change though.

## ACs
```[tasklist]
- [ ] Add `kManagerProvided` and `kRequired` to `PolicyAccess`
- [x] Remove `ResolvesFutureEntitiesTrait` from MediaCreation (might already be done)
- [ ] https://github.com/OpenAssetIO/OpenAssetIO-Manager-BAL/issues/98
- [ ] https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/issues/75
- [ ] Scan docs and update any publishing examples
- [ ] Update existing publishing examples to account for kRequired/kManagerProvided
```




Discussion / hints:
With these changes, the [Publishing a file example](https://openassetio.github.io/OpenAssetIO/examples.html#example_publishing_a_file) becomes the following (also updating to avoid use of `ManagedTrait` in core docs). My brain is tired, so this probably isnt the best formulation.

```python
from openassetio.access import (
     PolicyAccess, ResolveAccess, PublishingAccess
)
# Commented imports are illustrative and may not exist yet
from openassetio_mediacreation.specifications.files import (
     # TextFileSpecification
)
 
# Ensure that the manager supports publishing.
if not manager.hasCapability(Manager.Capability.kPublishing):
  return
 
# As ever, an appropriately configured context is required
context = manager.createContext()
 
# 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 policy.traitSet():
   # The policy is empty, so the manager doesn't manage this type of asset
   return
 
# Whenever we make new data, we always tell the manager first,
# This allows it to create a placeholder version or similar.
# We must provide the manager with any relevant information that the
# host owns (i.e. won't be queried from the manager during
# publishing) and can be provided up-front).
file_spec = TextFileSpecification.create()
file_spec.markupTrait().setMarkupType("plain")

# See if the the manager can tell us the path and/or encoding, and attempt
# to provide defaults if not. You could choose to error here if there is no way
# to determine this data other means.
derivable_traits_policy = manager.managementPolicy(
     [TextFileSpecification.kTraitSet], PolicyAccess.kDerive, context)[0]

save_path = None
encoding = None

if not LocatablContentTrait.isImbuedTo(derivable_traits_policy):
   # It can't tell us a path, so use some vaguely sensible place
   save_path = os.path.join(os.path.expanduser('~'), 'greeting.txt')
    file_spec.locatableContentTrait().setLocation(pathToURL(save_path))

if not TextEncodingTrait.isImbuedTo(derivable_traits_policy):
    # It can't tell us the encoding, so use our default
    encoding = "utf-8"
    file_spec.textEncodingTrait().setEncoding(encoding)
 
# 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, PublishingAccess.kWrite, context)
 
# If the manager told us it could provide the traits we need
if save_path is None or encoding is None :
 
     # Note we haven't used `hasCapability` to check the resolution
     # capability, as `managementPolicy` has assured us the functionality 
     # is available.
 
     working_data = manager.resolve(
             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
     if custom_encoding := working_spec.textEncodingTrait().getEncoding():
         encoding = custom_encoding
 
# Now we can write the file
with open(save_path, 'w', encoding=encoding) as f:
    f.write("Hello from the documentation example\n")
 
# Prepare the final entity specification to register, with the data about
# where we actually wrote the data to, and with what encoding.
# Even though this came from the manager, we confirm this is
# what we actually used.
file_spec.locatableContentTrait().setLocation(pathToURL(save_path))
file_spec.textEncodingTrait().setEncoding(encoding)
 
# Now the data has been written, we register the file and the publish
# is complete.
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:
     f.write(f"{final_ref}\n")
```
`kDerived` is an awful name for this concept, which is "whether or not the manager provides data in this trait during the publishing phase, for the purposes of driving data generation" i.e, a manager would provide a location to put your bytes in if you were a render node, or provide the encoding an image file should be written in, etc.

I propose `kManagerDriven` instead.
Re. `access/__init__.py` (my suggestion) - the pattern followed for other single-module namespaces is to use a module for the namespace, e.g. `log.py` covers the `log` namespace.  So for consistency we should create an `access.py`.
For posterity, based on offline conversation to eliminate ambiguity:

> Methods update with the above as a[nother] positional argument access at the end of the batch arguments, before context or hostSession, callbacks.

would perhaps be better phrased as, "immediately to the left of `context`".

I.e. in-between "the end of the batch arguments" and "before context" there can be multiple other arguments.  We decided to keep the access argument alongside the context argument.

With these changes, the [Publishing a file example](https://openassetio.github.io/OpenAssetIO/examples.html#example_publishing_a_file) becomes the following (also updating to avoid use of `ManagedTrait` in core docs). My brain is tired, so this probably isnt the best formulation.

```python
from openassetio.access import (
     PolicyAccess, ResolveAccess, PublishingAccess
)
# Commented imports are illustrative and may not exist yet
from openassetio_mediacreation.specifications.files import (
     # TextFileSpecification
)
 
# Ensure that the manager supports publishing.
if not manager.hasCapability(Manager.Capability.kPublishing):
  return
 
# As ever, an appropriately configured context is required
context = manager.createContext()
 
# 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 policy.traitSet():
   # The policy is empty, so the manager doesn't manage this type of asset
   return
 
# Whenever we make new data, we always tell the manager first,
# This allows it to create a placeholder version or similar.
# We must provide the manager with any relevant information that the
# host owns (i.e. won't be queried from the manager during
# publishing) and can be provided up-front).
file_spec = TextFileSpecification.create()
file_spec.markupTrait().setMarkupType("plain")

# See if the the manager can tell us the path and/or encoding, and attempt
# to provide defaults if not. You could choose to error here if there is no way
# to determine this data other means.
derivable_traits_policy = manager.managementPolicy(
     [TextFileSpecification.kTraitSet], PolicyAccess.kDerive, context)[0]

save_path = None
encoding = None

if not LocatablContentTrait.isImbuedTo(derivable_traits_policy):
   # It can't tell us a path, so use some vaguely sensible place
   save_path = os.path.join(os.path.expanduser('~'), 'greeting.txt')
    file_spec.locatableContentTrait().setLocation(pathToURL(save_path))

if not TextEncodingTrait.isImbuedTo(derivable_traits_policy):
    # It can't tell us the encoding, so use our default
    encoding = "utf-8"
    file_spec.textEncodingTrait().setEncoding(encoding)
 
# 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, PublishingAccess.kWrite, context)
 
# If the manager told us it could provide the traits we need
if save_path is None or encoding is None :
 
     # Note we haven't used `hasCapability` to check the resolution
     # capability, as `managementPolicy` has assured us the functionality 
     # is available.
 
     working_data = manager.resolve(
             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
     if custom_encoding := working_spec.textEncodingTrait().getEncoding():
         encoding = custom_encoding
 
# Now we can write the file
with open(save_path, 'w', encoding=encoding) as f:
    f.write("Hello from the documentation example\n")
 
# Prepare the final entity specification to register, with the data about
# where we actually wrote the data to, and with what encoding.
# Even though this came from the manager, we confirm this is
# what we actually used.
file_spec.locatableContentTrait().setLocation(pathToURL(save_path))
file_spec.textEncodingTrait().setEncoding(encoding)
 
# Now the data has been written, we register the file and the publish
# is complete.
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:
     f.write(f"{final_ref}\n")
```
`kDerived` is an awful name for this concept, which is "whether or not the manager provides data in this trait during the publishing phase, for the purposes of driving data generation" i.e, a manager would provide a location to put your bytes in if you were a render node, or provide the encoding an image file should be written in, etc.

I propose `kManagerDriven` instead.

Fix patch:
diff --git a/.github/workflows/integrations.yml b/.github/workflows/integrations.yml
index bfc5baa76..04bb9c09e 100644
--- a/.github/workflows/integrations.yml
+++ b/.github/workflows/integrations.yml
@@ -61,23 +61,24 @@ jobs:
           # `openassetio` package.
           python -m pip install pytest
 
-      - name: Checkout BAL
-        uses: actions/checkout@v4
-        with:
-          repository: OpenAssetIO/OpenAssetIO-Manager-BAL
-          path: external/BAL
-
-      - name: Test BAL
-        run: |
-          python -m pip install external/BAL
-          python -m pip install -r external/BAL/tests/requirements.txt
-          python -m pytest -v external/BAL/tests
-
-      - name: Test Simple Resolver
-        run: |
-          python -m pytest -v examples/host/simpleResolver
-        env:
-          OPENASSETIO_PLUGIN_PATH: ${{ github.workspace }}/external/BAL/plugin
+# Disable pending merge of OpenAssetIO/OpenAssetIO-Manager-BAL#99
+#      - name: Checkout BAL
+#        uses: actions/checkout@v4
+#        with:
+#          repository: OpenAssetIO/OpenAssetIO-Manager-BAL
+#          path: external/BAL
+#
+#      - name: Test BAL
+#        run: |
+#          python -m pip install external/BAL
+#          python -m pip install -r external/BAL/tests/requirements.txt
+#          python -m pytest -v external/BAL/tests
+#
+#      - name: Test Simple Resolver
+#        run: |
+#          python -m pytest -v examples/host/simpleResolver
+#        env:
+#          OPENASSETIO_PLUGIN_PATH: ${{ github.workspace }}/external/BAL/plugin
 
       - name: Checkout TraitGen
         uses: actions/checkout@v4
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 4b9a551c9..9113cc4aa 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -12,6 +12,11 @@ v1.0.0-beta.x.x
   must have, for a given entity reference.
   [#31](https://github.com/OpenAssetIO/OpenAssetIO/issues/31)
 
+- Renamed access policy for resolving future entities,
+  `ResolveAccess.kWrite` to `ResolveAccess.kManagerDriven`. This is then
+  consistent with the newly added `managementPolicy` access mode.
+  [#1209](https://github.com/OpenAssetIO/OpenAssetIO/issues/1209)
+
 - Access policy keyword argument names in the Python bindings for
   `ManagerInterface` has changed  to `"resolveAccess"` for `resolve` and
   `"policyAccess"` for `managementPolicy`, in order to conform to the
@@ -35,6 +40,13 @@ v1.0.0-beta.x.x
   install to allow compile time introspection.
   [#1173](https://github.com/OpenAssetIO/OpenAssetIO/issues/1173)
 
+- Add `managementPolicy` support for querying traits whose required
+  properties must be set in order for a publish to succeed, via a
+  `kRequired` access mode; and support for querying traits whose
+  properties the manager wishes to dictate when publishing, via a
+  `kManagerDriven` access mode.
+  [#1209](https://github.com/OpenAssetIO/OpenAssetIO/issues/1209)
+
 ### Improvements
 
 - Added the string representation of an `EntityReference` when `repr`'d
diff --git a/doc/doxygen/src/Examples.dox b/doc/doxygen/src/Examples.dox
index cc2dfc229..6a2b649a8 100644
--- a/doc/doxygen/src/Examples.dox
+++ b/doc/doxygen/src/Examples.dox
@@ -353,16 +353,20 @@
  * # 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]
+ * [policy] = manager.managementPolicy(
+ *     [TextFileSpecification.kTraitSet], PolicyAccess.kWrite, context)
  *
  * if not ManagedTrait.isImbuedTo(policy):
  *   # The manager doesn't care about this type of asset
  *   return
  *
- * # Not all managers can tell us where to put files (irksome).
- * # The reality of handling this is somewhat more challenging, and
- * # depends on the nature of the task in hand. One for further discussion.
+ * # Managers may want to dictate to the host some of the data to be
+ * # published, e.g. tell us where to put files. So we ask the manager
+ * # which traits, if any, it is interested in "driving" itself.
+ * [manager_driven_policy] = manager.managementPolicy(
+ *     [TextFileSpecification.kTraitSet], PolicyAccess.kManagerDriven, context)
+ *
+ * # Choose some defaults in case the manager cannot drive these values.
  * save_path = os.path.join(os.path.expanduser('~'), 'greeting.txt')
  * encoding = "utf-8"
  *
@@ -380,25 +384,21 @@
  * 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.
- * # Not all managers support providing every trait property up front,
- * # or resolving for write at all. This process is currently quite
- * # cumbersome, but will be improved soon:
- * #   https://github.com/OpenAssetIO/OpenAssetIO/issues/1209
- * if manager.hasCapability(Manager.Capability.kResolution):
- *   try:
- *     working_data = manager.resolve(
- *             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
- *     if custom_encoding := working_spec.textEncodingTrait().getEncoding():
- *       encoding = custom_encoding
- *   except BatchElementException as exc:
- *     # Only ignore the case the manager can't provide data
- *     if exc.error.code != exc.error.ErrorCode.kEntityAccessError
- *        raise exc
+ * # If the manager wants to drive at least one of the traits we're
+ * # publishing, we can `resolve` with the `kManagerDriven` access mode
+ * # to retrieve the values the manager wants us to use. If we attempt
+ * # to `resolve` without checking the policy first, we risk an error if
+ * # the manager does not support this operation.
+ * if (LocatableContentTrait.isImbuedTo(manager_driven_policy) or
+ *       TextEncodingTrait.isImbuedTo(manager_driven_policy)):
+ *   working_data = manager.resolve(
+ *           working_ref,
+ *           {LocatableContentTrait.kId, TextEncodingTrait.kId},
+ *           ResolveAccess.kManagerDriven, context)
+ *   if save_url := LocatableContentTrait(working_data).getLocation():
+ *     # Assume `file://` URL
+ *     save_path = utils.pathFromUrl(save_url)
+ *   encoding = TextEncodingTrait(working_data).getEncoding(defaultValue=encoding):
  *
  * # Now we can write the file
  * with open(save_path, 'w', encoding=encoding) as f:
@@ -451,23 +451,19 @@
  * thumbnail_attr = {"width": 128, "height": 128}
  *
  * # See if the manager can tell us where to put it, and what it should be like
- * #   https://github.com/OpenAssetIO/OpenAssetIO/issues/1209
- * if manager.hasCapability(Manager.Capability.kResolution):
- *   try:
- *     requested = manager.resolve(
- *             thumbnail_ref, ThumbnailFileSpecification.kTraitSet,
- *             ResolveAccess.kWrite, context)
- *     requested_spec = ThumbnailFileSpecification(requested)
- *     if requested_path := requested_spec.locatableContentTrait().getLocation():
- *       thumbnail_path = pathFromURL(requested_path)
- *     raster_trait = requested_spec.rasterTrait()
- *     if raster_trait.isImbued():
- *       # 'get' calls can take a default value to avoid exceptions if missing.
- *       thumbnail_attr["width"] = raster_trait.getWidth(thumbnail_attr["width"])
- *       thumbnail_attr["height"] = raster_trait.getHeight(thumbnail_attr["height"])
- *   except BatchElementException as exc:
- *     if exc.error.code != exc.error.ErrorCode.kEntityAccessError
- *        raise exc
+ * if (LocatableContentTrait.isImbuedTo(manager_driven_policy) or
+ *       RasterTrait.isImbuedTo(manager_driven_policy)):
+ *   requested = manager.resolve(
+ *           thumbnail_ref,
+ *           {LocatableContentTrait.kId, RasterTrait.kId},
+ *           ResolveAccess.kManagerDriven, context)
+ *   if requested_path := LocatableContentTrait(requested).getLocation():
+ *     thumbnail_path = utils.pathFromURL(requested_path)
+ *   raster_trait = RasterTrait(requested)
+ *   if raster_trait.isImbued():
+ *     # 'get' calls can take a default value to avoid `None` if missing.
+ *     thumbnail_attr["width"] = raster_trait.getWidth(thumbnail_attr["width"])
+ *     thumbnail_attr["height"] = raster_trait.getHeight(thumbnail_attr["height"])
  *
  * # Generate a thumbnail using the supplied criteria
  * mk_thumbnail(thumbnail_path, thumbnail_attr["width"], thumbnail_attr["height"])
diff --git a/doc/doxygen/src/ForHosts.dox b/doc/doxygen/src/ForHosts.dox
index cc9ace252..6db0b6190 100644
--- a/doc/doxygen/src/ForHosts.dox
+++ b/doc/doxygen/src/ForHosts.dox
@@ -154,9 +154,9 @@
  * - Ask the @ref manager if they support publishing for the relevant
  *   data by supplying its @ref trait_set to
  *   @fqref{hostApi.Manager.managementPolicy} "Manager.managementPolicy"
- *   with a @ref Context with `kWrite` access, respecting the returned
- *   traits. Note that some managers may not be able to persist data for
- *   all of an entity's traits, these traits will be ommited from the
+ *   with a `kWrite` access, respecting the returned traits. Note that
+ *   some managers may not be able to persist data for all of an
+ *   entity's traits, these traits will be omitted from the
  *   managementPolicy response.
  *
  * - Follow the @fqref{hostApi.Manager.managementPolicy} "policy", @ref
diff --git a/doc/doxygen/src/ForManagers.dox b/doc/doxygen/src/ForManagers.dox
index f2718543b..887857538 100644
--- a/doc/doxygen/src/ForManagers.dox
+++ b/doc/doxygen/src/ForManagers.dox
@@ -116,7 +116,8 @@
  *   to be involved in managing, and specify which property-holding
  *   traits you are capable of resolving. To request not to be involved
  *   in publishing, return an empty @fqref{trait.TraitsData}
- *   "TraitsData" for any queries with a context with `kWrite` access.
+ *   "TraitsData" for any queries with `kWrite` (or other
+ *   publishing-related) access mode.
  *
  * - Implement the methods grouped under the "Entity Reference Inspection"
  *   and "Entity Reference Resolution" sections of the
@@ -139,8 +140,8 @@
  *
  * - Update the implementation of
  *   @fqref{managerApi.ManagerInterface.managementPolicy}
- *   "managementPolicy" to return suitable traits for contexts with a
- *   `kWrite` access.
+ *   "managementPolicy" to return suitable traits for a `kWrite` (and
+ *   other publishing-related) access mode.
  *
  * - Implement the methods listed under the
  *   @fqref{managerApi.ManagerInterface.Capability.kPublishing}
diff --git a/doc/doxygen/src/Glossary.dox b/doc/doxygen/src/Glossary.dox
index c57ab8f94..4bac06c14 100644
--- a/doc/doxygen/src/Glossary.dox
+++ b/doc/doxygen/src/Glossary.dox
@@ -16,19 +16,10 @@
  *
  * A Context is an object that holds all of the persistent state between
  * a set of connected calls to the API. Its properties define details of
- * how a @ref host is calling the Asset API. In addition to information
- * about the desired access (read/write) and persistence of data, the
- * context's @ref locale may contain information to better define which
- * part of the users workflow the call is being generated by.
- *
- * The information in the context is vital to interpreting the intent of
- * a @ref host "hosts" actions. For example, if an Image @ref
- * Specification is passed to a browser, a context access of 'kWrite'
- * indicates the host wishes browse for a destination to publish a
- * single image @ref entity to, so any viable container should be
- * selectable. If it is 'kRead' then the host is wishing to browse for
- * an entity to read, so only existing entities that match the
- * specification should be presented.
+ * how a @ref host is calling the Asset API.
+ *
+ * The Context's @ref locale may contain information to better define
+ * which part of the users workflow the call is being generated by.
  *
  * The Context also holds an opaque (to the host) data pointer, created
  * by the @ref ManagerInterface, that contains any persistent state its
@@ -401,9 +392,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{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.
+ * @fqref{access.ResolveAccess.kManagerDriven} "manager driven" 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.
diff --git a/src/openassetio-core/include/openassetio/access.hpp b/src/openassetio-core/include/openassetio/access.hpp
index 663d55259..ef0e396cc 100644
--- a/src/openassetio-core/include/openassetio/access.hpp
+++ b/src/openassetio-core/include/openassetio/access.hpp
@@ -16,7 +16,8 @@ inline namespace OPENASSETIO_CORE_ABI_VERSION {
 namespace access {
 
 /// Mapping of access enum value to human-readable name.
-[[maybe_unused]] constexpr std::array kAccessNames{"read", "write", "createRelated"};
+[[maybe_unused]] constexpr std::array kAccessNames{"read", "write", "createRelated", "required",
+                                                   "managerDriven"};
 
 /**
  * Access pattern for a manager policy query.
@@ -49,6 +50,44 @@ enum class PolicyAccess : std::underlying_type_t<internal::access::Access> {
    * @ref RelationsAccess.kCreateRelated
    */
   kCreateRelated = internal::access::Access::kCreateRelated,
+  /**
+   * Host wishes to know which subset of traits must have their required
+   * properties filled for successful publishing of an entity.
+   *
+   * Traits are used for both classification of an entity, and
+   * communication of properties of that entity. That is, many traits
+   * have properties associated with them. When publishing an entity,
+   * the entire trait set of that entity must be provided, in order for
+   * the manager classify the entity being published. However, it may
+   * well be that not all of the properties of those traits need to be
+   * set, in order for publishing to succeed.
+   *
+   * On an individual trait level, some properties will be required and
+   * some optional. Determining this currently requires manual
+   * inspection of the documentation for that trait.
+   *
+   * The `kRequired` policy of a manager, with respect to a given entity
+   * trait set, refers to the subset of traits that must have their
+   * required properties set by the host, in order for publishing to
+   * succeed.
+   */
+  kRequired = internal::access::Access::kRequired,
+  /**
+   * Host wishes to know the subset of traits that have properties the
+   * manager can provide for creating new content when publishing an
+   * entity.
+   *
+   * Note that if a manager provides a property for the host to use
+   * during publishing, the host should not assume that the manager
+   * "remembers" that it provided that property. I.e. the manager-driven
+   * property should be published with the rest of the data, especially
+   * if the associated trait is is part of the @ref
+   * PolicyAccess.kRequired "kRequired" policy for the entity's trait
+   * set.
+   *
+   * @see @ref ResolveAccess.kManagerDriven
+   */
+  kManagerDriven = internal::access::Access::kManagerDriven,
 };
 
 /**
@@ -70,7 +109,7 @@ enum class ResolveAccess : std::underlying_type_t<internal::access::Access> {
    * writing of data to a resource, and specifics of its format or
    * content.
    */
-  kWrite = internal::access::Access::kWrite,
+  kManagerDriven = internal::access::Access::kManagerDriven,
 };
 
 /**
diff --git a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
index cc610b8b4..5fd10996d 100644
--- a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
+++ b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
@@ -343,16 +343,25 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    */
   /**
    * Management Policy queries allow a host to ask a Manager how they
-   * would like to interact with different kinds of entity, and which
-   * traits they are capable of resolving (or persisting). This allows
-   * you to adapt application logic or user-facing behaviour
+   * would like to interact with different kinds of entity.
+   *
+   * This includes the policy for a given trait set, as well as the
+   * per-trait policy, with the context for the policy determined by
+   * the @p policyAccess.
+   *
+   * More specifically, depending on the @p policyAccess mode, the
+   * response can tell you
+   * - Whether the manager is capable of resolving or persisting a
+   *   particular kind of entity at all.
+   * - Which specific traits can be @ref resolve "resolved", for
+   *   existing or future entities.
+   * - Which traits can be @ref register_ "persisted" when publishing.
+   * - Which traits must have their required properties filled for
+   *   publishing to succeed.
+   *
+   * This allows you to adapt application logic or user-facing behaviour
    * accordingly.
    *
-   * Queries return a @fqref{trait.TraitsData} "TraitsData" for each
-   * supplied @ref trait_set, imbued with traits that describe the
-   * manager's policy for entities with those traits, and which traits
-   * they are capable of resolving/storing data for.
-   *
    * This is an opt-in mechanism, such that if result is empty, then
    * the manager does not handle entities with the supplied traits.
    * In this situation, OpenAssetIO based functionality should be
@@ -364,7 +373,7 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * question is not managed by the manager, or it can't resolve a
    * required trait. Policy is runtime invariant and so only needs to be
    * checked once for any given set of inputs (which includes the
-   * Context and its @ref locale).
+   * @ref Context and its @ref locale).
    *
    * When querying this API, each Trait Set should be composed of:
    *
@@ -373,22 +382,33 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    *  - 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.
+   *  - For publishing 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{trait.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
-   * persist those properties with @ref register_. This allows you to
-   * know in advance if you can expect the configured manager to be able
-   * to provide data you may require.
+   * signalling the manager's capability or requirements for
+   * resolving/persisting their properties.
+   *
+   * The meaning of the subset of traits in the response varies by
+   * @p policyAccess mode as follows
+   * - @ref access.PolicyAccess.kRead "kRead":  traits that have
+   *   properties the manager can @ref resolve from existing entities.
+   * - @ref access.PolicyAccess.kWrite "kWrite" and @ref
+   *   access.PolicyAccess.kCreateRelated "kCreateRelated": traits that
+   *   have properties the manager can persist when @ref publish
+   *   "publishing".
+   * - @ref access.PolicyAccess.kRequired "kRequired": traits whose
+   *   properties must be provided by the host in order for publishing
+   *   to succeed.
+   * - @ref access.PolicyAccess.kManagerDriven "kManagerDriven": traits
+   *   that have properties that the manager can @ref resolve for a
+   *   future entity (i.e. an entity reference returned from a @ref
+   *   preflight call) that is yet to be @ref register_ "registered".
+   *   That is, traits that the manager wishes to drive, rather than
+   *   have the host decide.
    *
    * This method gives the global policy for how the manager wishes to
    * interact with certain categories of entity. See @ref entityTraits
@@ -406,13 +426,6 @@ class OPENASSETIO_CORE_EXPORT Manager final {
    * trait::TraitsData instead of querying directly using string
    * literals.
    *
-   * @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
    * such information enables high-level behavioural changes or
diff --git a/src/openassetio-core/include/openassetio/internal.hpp b/src/openassetio-core/include/openassetio/internal.hpp
index 312741505..34d1bbb51 100644
--- a/src/openassetio-core/include/openassetio/internal.hpp
+++ b/src/openassetio-core/include/openassetio/internal.hpp
@@ -16,7 +16,7 @@ namespace access {
  * simplifies serialisation, and supports language (especially C)
  * bindings.
  */
-enum Access : std::size_t { kRead = 0, kWrite, kCreateRelated };
+enum Access : std::size_t { kRead = 0, kWrite, kCreateRelated, kRequired, kManagerDriven };
 }  // namespace access
 
 namespace capability::manager {
diff --git a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
index 77e163807..a1536bc2d 100644
--- a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
+++ b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
@@ -549,10 +549,12 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    * @{
    */
   /**
-   * Management Policy queries determine if the manager is interested in
-   * participating in interactions with @ref entity "entities" with a
-   * specified @ref trait_set, and which traits it is capable of
-   * resolving or persisting data for.
+   * Management Policy queries allow a host to ask a Manager how they
+   * would like to interact with different kinds of entity.
+   *
+   * This includes the policy for a given trait set, as well as the
+   * per-trait policy, with the context for the policy determined by
+   * the @p policyAccess.
    *
    * This method is usually called early on by a host to determine
    * whether to enable OpenAssetIO related functionality when handling
@@ -581,7 +583,25 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    *   - Whether and how that kind of entity is managed (traits with the
    *    `managementPolicy` usage metadata)
    *   - Which of the requested set of traits that have properties that
-   *     can be resolved/persisted by your implementation.
+   *     must/can be resolved/persisted by your implementation.
+   *
+   * The meaning of the subset of traits in the response should vary by
+   * @p policyAccess mode as follows
+   * - @ref access.PolicyAccess.kRead "kRead":  traits that have
+   *   properties the manager can @ref resolve from existing entities.
+   * - @ref access.PolicyAccess.kWrite "kWrite" and @ref
+   *   access.PolicyAccess.kCreateRelated "kCreateRelated": traits that
+   *   have properties the manager can persist when @ref publish
+   *   "publishing".
+   * - @ref access.PolicyAccess.kRequired "kRequired": traits whose
+   *   properties must be provided by the host in order for publishing
+   *   to succeed.
+   * - @ref access.PolicyAccess.kManagerDriven "kManagerDriven": traits
+   *   that have properties that the manager can @ref resolve for a
+   *   future entity (i.e. an entity reference returned from a @ref
+   *   preflight call) that is yet to be @ref register_ "registered".
+   *   That is, traits that the manager wishes to drive, rather than
+   *   have the host decide.
    *
    * Entity management is an opt-in mechanism, and returning an empty
    * TraitsData states that you do not manage data with that specific
diff --git a/src/openassetio-python/cmodule/src/accessBinding.cpp b/src/openassetio-python/cmodule/src/accessBinding.cpp
index 975a518c9..032e27292 100644
--- a/src/openassetio-python/cmodule/src/accessBinding.cpp
+++ b/src/openassetio-python/cmodule/src/accessBinding.cpp
@@ -13,11 +13,13 @@ void registerAccess(const py::module& mod) {
   py::enum_<access::PolicyAccess>{mod, "PolicyAccess"}
       .value("kRead", access::PolicyAccess::kRead)
       .value("kWrite", access::PolicyAccess::kWrite)
-      .value("kCreateRelated", access::PolicyAccess::kCreateRelated);
+      .value("kCreateRelated", access::PolicyAccess::kCreateRelated)
+      .value("kRequired", access::PolicyAccess::kRequired)
+      .value("kManagerDriven", access::PolicyAccess::kManagerDriven);
 
   py::enum_<access::ResolveAccess>{mod, "ResolveAccess"}
       .value("kRead", access::ResolveAccess::kRead)
-      .value("kWrite", access::ResolveAccess::kWrite);
+      .value("kManagerDriven", access::ResolveAccess::kManagerDriven);
 
   py::enum_<access::EntityTraitsAccess>{mod, "EntityTraitsAccess"}
       .value("kRead", access::EntityTraitsAccess::kRead)
diff --git a/src/openassetio-python/cmodule/src/trait/TraitsDataBinding.cpp b/src/openassetio-python/cmodule/src/trait/TraitsDataBinding.cpp
index 141b5ff66..63e422096 100644
--- a/src/openassetio-python/cmodule/src/trait/TraitsDataBinding.cpp
+++ b/src/openassetio-python/cmodule/src/trait/TraitsDataBinding.cpp
@@ -5,6 +5,9 @@
 #include <pybind11/operators.h>
 #include <pybind11/stl.h>
 
+#include <fmt/format.h>
+#include <fmt/ranges.h>
+
 #include <openassetio/trait/TraitsData.hpp>
 #include <openassetio/trait/collection.hpp>
 
@@ -41,5 +44,7 @@ void registerTraitsData(const py::module& mod) {
           },
           py::arg("traitId"), py::arg("propertyKey"))
       .def("traitPropertyKeys", &TraitsData::traitPropertyKeys, py::arg("traitId"))
-      .def(py::self == py::self);  // NOLINT(misc-redundant-expression)
+      .def(py::self == py::self)  // NOLINT(misc-redundant-expression)
+      .def("__repr__",
+           [](const TraitsData& self) { return fmt::format("TraitsData({})", self.traitSet()); });
 }


Test patch:
diff --git a/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py b/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py
index 14838e550..92f57365e 100644
--- a/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py
+++ b/src/openassetio-python/package/openassetio/test/manager/apiComplianceSuite.py
@@ -208,6 +208,14 @@ def test_calling_with_createRelated_context(self):
         context = self.createTestContext()
         self.__assertPolicyResults(1, context, policyAccess=PolicyAccess.kCreateRelated)
 
+    def test_calling_with_required_context(self):
+        context = self.createTestContext()
+        self.__assertPolicyResults(1, context, policyAccess=PolicyAccess.kRequired)
+
+    def test_calling_with_managerDriven_context(self):
+        context = self.createTestContext()
+        self.__assertPolicyResults(1, context, policyAccess=PolicyAccess.kManagerDriven)
+
     def test_calling_with_empty_trait_set_does_not_error(self):
         context = self.createTestContext()
         self.__assertPolicyResults(1, context, traitSet=set())
@@ -520,10 +528,10 @@ def test_when_valid_and_unknown_traits_then_returned_specification_only_has_vali
         mixed_traits.add("₲₪₡🤯")
         self.__testResolution([ref], mixed_traits, ResolveAccess.kRead, traits)
 
-    def test_when_resolving_read_only_reference_for_write_then_access_error_is_returned(self):
+    def test_when_resolving_read_only_reference_for_publish_then_access_error_is_returned(self):
         self.__testResolutionError(
             "a_reference_to_a_readonly_entity",
-            resolve_access=ResolveAccess.kWrite,
+            resolve_access=ResolveAccess.kManagerDriven,
             errorCode=BatchElementError.ErrorCode.kEntityAccessError,
         )
 
diff --git a/src/openassetio-python/tests/package/hostApi/test_manager.py b/src/openassetio-python/tests/package/hostApi/test_manager.py
index d86d70d16..dbce88939 100644
--- a/src/openassetio-python/tests/package/hostApi/test_manager.py
+++ b/src/openassetio-python/tests/package/hostApi/test_manager.py
@@ -2722,7 +2722,9 @@ def ensure_singular(item):
 
 class Test_Manager_convenience_exceptions:
     @pytest.mark.parametrize("error_code", batch_element_error_codes)
-    @pytest.mark.parametrize("access", [access.ResolveAccess.kRead, access.ResolveAccess.kWrite])
+    @pytest.mark.parametrize(
+        "access", [access.ResolveAccess.kRead, access.ResolveAccess.kManagerDriven]
+    )
     @pytest.mark.parametrize("singular", [True, False])
     @pytest.mark.parametrize("tag", [[], [Manager.BatchElementErrorPolicyTag.kException]])
     def test_when_batch_resolve_emits_BatchElementError_then_appropriate_exception_raised(
diff --git a/src/openassetio-python/tests/package/test_access.py b/src/openassetio-python/tests/package/test_access.py
index ce6cc91cc..3b0e7ebc0 100644
--- a/src/openassetio-python/tests/package/test_access.py
+++ b/src/openassetio-python/tests/package/test_access.py
@@ -40,22 +40,37 @@ def test_expected_constants_are_unique_and_exhaustive(self, root_enum):
             root_enum.kRead,
             root_enum.kWrite,
             root_enum.kCreateRelated,
+            root_enum.kRequired,
+            root_enum.kManagerDriven,
         )
 
         # 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
+            root_enum.kRead,
+            root_enum.kWrite,
+            root_enum.kCreateRelated,
+            root_enum.kRequired,
+            root_enum.kManagerDriven,
         )
-        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
+
+        expected_num_enum_values = 5
+
+        assert len(all_enum_values) == expected_num_enum_values
+        assert len(set(member.name for member in all_enum_values)) == expected_num_enum_values
+        assert len(set(member.value for member in all_enum_values)) == expected_num_enum_values
 
         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
+        assert len(all_enum_values_from_class) == expected_num_enum_values
+        assert (
+            len(set(member.name for member in all_enum_values_from_class))
+            == expected_num_enum_values
+        )
+        assert (
+            len(set(member.value for member in all_enum_values_from_class))
+            == expected_num_enum_values
+        )
 
 
 class Test_kAccessNames:
@@ -63,6 +78,8 @@ 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"
+        assert kAccessNames[int(root_enum.kRequired)] == "required"
+        assert kAccessNames[int(root_enum.kManagerDriven)] == "managerDriven"
 
 
 class Test_PolicyAccess:
@@ -72,6 +89,8 @@ def test_has_expected_constants(self, root_enum):
             root_enum.kRead,
             root_enum.kWrite,
             root_enum.kCreateRelated,
+            root_enum.kRequired,
+            root_enum.kManagerDriven,
         )
 
 
@@ -87,7 +106,7 @@ def test_has_expected_constants(self, root_enum):
 
 class Test_ResolveAccess:
     def test_has_expected_constants(self, root_enum):
-        assert_expected_enum_values(ResolveAccess, root_enum.kRead, root_enum.kWrite)
+        assert_expected_enum_values(ResolveAccess, root_enum.kRead, root_enum.kManagerDriven)
 
 
 class Test_EntityTraitsAccess:
diff --git a/src/openassetio-python/tests/package/test_traitsdata.py b/src/openassetio-python/tests/package/test_traitsdata.py
index d4deb4c79..0f9f764e7 100644
--- a/src/openassetio-python/tests/package/test_traitsdata.py
+++ b/src/openassetio-python/tests/package/test_traitsdata.py
@@ -183,6 +183,15 @@ def test_when_comparing_with_different_trait_then_are_not_equal(self):
         assert data_a != data_b
 
 
+class Test_TraitsData_repr:
+    def test(self, a_traitsdata):
+        assert repr(a_traitsdata) == str(a_traitsdata)
+        assert (
+            str(a_traitsdata) == 'TraitsData({"first_trait", "second_trait"})'
+            or str(a_traitsdata) == 'TraitsData({"second_trait", "first_trait"})'
+        )
+
+
 @pytest.fixture
 def a_traitsdata():
     return TraitsData({"first_trait", "second_trait"})