You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2023/01/16 14:55:31 UTC

[isis] 01/01: ISIS-3321: prepare removal of DomainObjectLayout#plural

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3321-applib.remove.plural
in repository https://gitbox.apache.org/repos/asf/isis.git

commit e4e0898d903a5ac02fcd37afab81c5b64824aa38
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jan 16 15:55:22 2023 +0100

    ISIS-3321: prepare removal of DomainObjectLayout#plural
---
 .../applib/annotation/DomainObjectLayout.java      |   8 --
 .../_testing/MetaModelContext_forTesting.java      |   2 +-
 .../metamodel/facets/all/i8n/noun/HasNoun.java     |  26 +++---
 .../metamodel/facets/all/i8n/noun/NounForm.java    |  14 +--
 .../metamodel/facets/all/i8n/noun/NounForms.java   |  28 +++---
 .../all/named/ObjectNamedFacetSynthesized.java     |   4 +-
 .../CollectionAccessorFacetViaAccessor.java        |   9 +-
 ...tNamedFacetForDomainObjectLayoutAnnotation.java |   2 -
 .../ObjectNamedFacetForDomainObjectLayoutXml.java  |   4 +-
 .../ActionParameterDefaultsFacetViaMethod.java     |   2 +-
 .../PropertyOrCollectionAccessorFacet.java         |   1 +
 .../autocomplete/PropertyAutoCompleteFacet.java    |   2 +
 .../method/PropertyAutoCompleteFacetMethod.java    |   9 +-
 .../PropertyAutoCompleteFacetMethodFactory.java    |   3 +-
 .../interactions/managed/ActionInteraction.java    |   8 +-
 .../interactions/managed/ManagedAction.java        |  34 ++-----
 .../interactions/managed/ManagedCollection.java    |   2 +-
 .../interactions/managed/ManagedFeature.java       |  10 +--
 .../interactions/managed/ManagedMember.java        |  16 ++--
 .../interactions/managed/ManagedParameter.java     |   4 +-
 .../interactions/managed/ManagedProperty.java      |   2 +-
 .../interactions/managed/ManagedValue.java         |   6 +-
 .../managed/ParameterNegotiationModel.java         |  47 +++++-----
 .../managed/PropertyNegotiationModel.java          |  18 ++--
 .../managed/nonscalar/DataTableModel.java          |   6 +-
 .../core/metamodel/layout/LayoutFacetUtil.java     |  22 ++---
 .../core/metamodel/object/ManagedObject.java       |  13 ++-
 .../core/metamodel/object/MmTitleUtil.java         |   2 +-
 .../core/metamodel/object/PackedManagedObject.java |   3 +-
 .../core/metamodel/object/_InternalTitleUtil.java  |  47 ++++++++--
 .../metamodel/object/_ManagedObjectPacked.java     |   6 ++
 .../metamodel/object/_ManagedObjectSpecified.java  |  29 +++---
 .../metamodel/objectmanager/ObjectManager.java     |  30 ++++---
 .../objectmanager/ObjectManagerDefault.java        |  34 +++----
 .../objectmanager/memento/ObjectMemento.java       |  15 ++--
 ...ementoForEmpty.java => ObjectMementoEmpty.java} |   2 +-
 ...ntoCollection.java => ObjectMementoPlural.java} |  13 ++-
 ...toForScalar.java => ObjectMementoSingular.java} |  18 ++--
 .../objectmanager/memento/_Recreatable.java        |  12 +--
 .../objectmanager/memento/_RecreatableLookup.java  |   6 +-
 .../objectmanager/memento/_RecreatableValue.java   |   8 +-
 .../i18n/SynthesizeObjectNamingPostProcessor.java  |  16 ++--
 .../schema/SchemaValueMarshallerAbstract.java      |   6 +-
 .../services/title/TitleServiceDefault.java        |  32 ++-----
 .../core/metamodel/spec/ObjectSpecification.java   |  16 ++--
 .../metamodel/spec/feature/HasObjectFeature.java   |  24 ++++-
 .../specimpl/ObjectActionParameterAbstract.java    |   2 +-
 .../specimpl/ObjectSpecificationAbstract.java      |  20 ++---
 .../specimpl/OneToManyAssociationDefault.java      |   4 +-
 .../specimpl/OneToOneAssociationDefault.java       |   2 +-
 .../core/metamodel/util/snapshot/XmlSchema.java    |   6 +-
 .../core/metamodel/util/snapshot/XmlSnapshot.java  |   4 +-
 .../DomainObjectLayoutFactoryTest.java             | 100 +++++++++++----------
 .../services/title/TitleServiceDefaultTest.java    |   2 +-
 .../executor/MemberExecutorServiceDefault.java     |   3 +-
 .../wrapper/WrapperFactoryDefault.java             |  19 +++-
 .../handlers/DomainObjectInvocationHandler.java    |   8 +-
 .../plural/DomainObjectLayoutPluralVm.java         |   4 +-
 .../pdfjs/wkt/ui/components/PdfJsViewerPanel.java  |   2 +-
 .../PdfJsViewerPanelComponentFactory.java          |   2 +-
 .../ui/components/ScalarPanelAbstractLegacy.java   |   2 +-
 .../ListeningMarkupPanelFactoriesForWicket.java    |   2 +-
 .../javafx/model/action/ActionUiModelFx.java       |   2 +-
 .../javafx/ui/components/UiComponentFactoryFx.java |   2 +-
 .../vaadin/model/action/ActionUiModelVaa.java      |   2 +-
 .../MetaModelRegressionTest.verify.approved.xml    |  88 +++++-------------
 .../testdomain/interact/ActionInteractionTest.java |   2 +-
 .../testdomain/interact/CommandArgumentTest.java   |   2 +-
 .../testdomain/interact/PropertyBindingTest.java   |  74 +++++++--------
 .../testdomain/interact/SimulatedUiChoices.java    |  12 +--
 .../interaction/DomainObjectTesterFactory.java     |  14 +--
 .../commons/model/scalar/HasUiParameter.java       |  32 +++----
 .../viewer/commons/model/scalar/HasUiProperty.java |   4 +-
 .../viewer/commons/model/scalar/UiParameter.java   |  32 +++----
 .../viewer/commons/model/scalar/UiProperty.java    |  18 ++--
 .../viewer/commons/model/scalar/UiScalar.java      |  34 +++----
 .../rendering/ReprRendererAbstract.java            |  13 +--
 .../AbstractObjectMemberReprRenderer.java          |   2 +-
 .../domainobjects/ObjectActionReprRenderer.java    |   8 +-
 .../domaintypes/DomainTypeReprRenderer.java        |   4 +-
 ...entNegotiationServiceForRestfulObjectsV1_0.java |  37 +++-----
 .../resources/DomainServicesListReprRenderer.java  |  15 ++--
 .../viewer/resources/_DomainResourceHelper.java    |   4 +-
 .../wicket/model/models/ManagedObjectModel.java    |  13 ---
 .../viewer/wicket/model/models/ScalarModel.java    |   4 +-
 .../model/models/ScalarModelWithMultiChoice.java   |   6 +-
 .../wicket/model/models/ScalarPropertyModel.java   |   2 +-
 .../models/interaction/act/UiParameterWkt.java     |   2 +-
 .../models/interaction/prop/UiPropertyWkt.java     |   4 +-
 .../entityactions/LinkAndLabelFactory.java         |   2 +-
 .../components/actions/ActionParametersForm.java   |   4 +-
 .../ui/components/scalars/ScalarPanelAbstract.java |   4 +-
 .../components/scalars/ScalarPanelAbstract2.java   |   2 +-
 .../scalars/ScalarPanelFormFieldAbstract.java      |   2 +-
 .../scalars/ScalarPanelTextFieldAbstract.java      |   2 +-
 .../ScalarPanelTextFieldWithTemporalPicker.java    |   2 +-
 .../viewer/wicket/ui/components/scalars/_Util.java |   6 +-
 .../components/scalars/markup/MarkupComponent.java |   4 +-
 .../scalars/string/StringPanelFactory.java         |   2 +-
 .../widgets/select2/Select2MultiChoiceExt.java     |  16 ++--
 .../widgets/select2/Select2OnSelect.java           |  10 +--
 .../select2/providers/ChoiceProviderAbstract.java  |   4 +-
 .../providers/ChoiceProviderForReferences.java     |   5 +-
 103 files changed, 666 insertions(+), 634 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/causeway/applib/annotation/DomainObjectLayout.java b/api/applib/src/main/java/org/apache/causeway/applib/annotation/DomainObjectLayout.java
index eedbd0e8f8..48d244e6ba 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/annotation/DomainObjectLayout.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/annotation/DomainObjectLayout.java
@@ -132,14 +132,6 @@ public @interface DomainObjectLayout {
     int paged()
             default -1;
 
-    /**
-     * The plural name of the class.
-     *
-     * @see DomainObjectLayout#named()
-     */
-    String plural()
-            default "";
-
     /**
      * Whether a standalone collection when represented in a table form should additionally be
      * &quot;decorated&quot; with client-side (javascript) enhancements, for example to enable paging and filtering.
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
index 3d292eb612..26dfc6ff56 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
@@ -162,7 +162,7 @@ implements MetaModelContext {
     private AuthenticationManager authenticationManager;
 
     @Builder.Default
-    private TitleService titleService = new TitleServiceDefault(null, null);
+    private TitleService titleService = new TitleServiceDefault(null);
 
     private ObjectIconService objectIconService;
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/HasNoun.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/HasNoun.java
index c375a1cf37..d17c0377d6 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/HasNoun.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/HasNoun.java
@@ -65,19 +65,19 @@ extends HasMemoizableTranslation {
         return translated(NounForm.SINGULAR).orElse(null);
     }
 
-    /**
-     * Originating text of plural noun-form to be translated before use in the UI.
-     */
-    default @Nullable String plural() {
-        return text(NounForm.PLURAL).orElse(null);
-    }
-
-    /**
-     * Translated text of plural noun-form to be used in the UI.
-     */
-    default @Nullable String pluralTranslated() {
-        return translated(NounForm.PLURAL).orElse(null);
-    }
+//    /**
+//     * Originating text of plural noun-form to be translated before use in the UI.
+//     */
+//    default @Nullable String plural() {
+//        return text(NounForm.PLURAL).orElse(null);
+//    }
+//
+//    /**
+//     * Translated text of plural noun-form to be used in the UI.
+//     */
+//    default @Nullable String pluralTranslated() {
+//        return translated(NounForm.PLURAL).orElse(null);
+//    }
 
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForm.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForm.java
index 0d4e444d14..fdd35401e3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForm.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForm.java
@@ -29,18 +29,18 @@ public enum NounForm {
      */
     SINGULAR,
 
-    /**
-     * Represents the plural linguistic form.
-     */
-    PLURAL
+//    /**
+//     * Represents the plural linguistic form.
+//     */
+//    PLURAL
     ;
 
     public boolean isSingular() {
         return this == SINGULAR;
     }
 
-    public boolean isPlural() {
-        return this == PLURAL;
-    }
+//    public boolean isPlural() {
+//        return this == PLURAL;
+//    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForms.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForms.java
index 2016e85077..b50d7e61a3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForms.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/i8n/noun/NounForms.java
@@ -46,13 +46,13 @@ public class NounForms {
                 .singular(singular);
     }
 
-    public static NounFormsBuilder builderPlural(final @Nullable String plural) {
-        return NounForms.builder()
-                .plural(plural);
-    }
+//    public static NounFormsBuilder builderPlural(final @Nullable String plural) {
+//        return NounForms.builder()
+//                .plural(plural);
+//    }
 
     private final @Nullable String singular;
-    private final @Nullable String plural;
+    //private final @Nullable String plural;
 
     @Getter(lazy = true)
     final ImmutableEnumSet<NounForm> supportedNounForms = supportedNounForms();
@@ -65,9 +65,9 @@ public class NounForms {
             supportedNounForms.add(NounForm.SINGULAR);
         }
 
-        if(plural!=null) {
-            supportedNounForms.add(NounForm.PLURAL);
-        }
+//        if(plural!=null) {
+//            supportedNounForms.add(NounForm.PLURAL);
+//        }
 
         return ImmutableEnumSet.from(supportedNounForms);
     }
@@ -80,9 +80,9 @@ public class NounForms {
         case SINGULAR:
             // non-null, as nulls are guarded by getSupportedNounForms()
             return Optional.of(getSingular());
-        case PLURAL:
-            // non-null, as nulls are guarded by getSupportedNounForms()
-            return Optional.of(getPlural());
+//        case PLURAL:
+//            // non-null, as nulls are guarded by getSupportedNounForms()
+//            return Optional.of(getPlural());
         default:
             break;
         }
@@ -103,9 +103,9 @@ public class NounForms {
             case SINGULAR:
                 builder.singular(translationService.translate(context, singular));
                 break;
-            case PLURAL:
-                builder.plural(translationService.translate(context, plural));
-                break;
+//            case PLURAL:
+//                builder.plural(translationService.translate(context, plural));
+//                break;
             default:
                 throw _Exceptions.unmatchedCase(nounForm);
             }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java
index 96fcd7ab06..1e7f237446 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java
@@ -35,8 +35,8 @@ extends ObjectNamedFacetAbstract {
             final NounForms nounForms,
             final FacetHolder holder) {
         super(nounForms, holder, Precedence.SYNTHESIZED);
-        // assert that both noun-forms are populated
-        _Assert.assertTrue(nounForms.getSupportedNounForms().stream().count() == 2L);
+        // assert that 1 noun-form is populated
+        _Assert.assertTrue(nounForms.getSupportedNounForms().stream().count() == 1L);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/accessor/CollectionAccessorFacetViaAccessor.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/accessor/CollectionAccessorFacetViaAccessor.java
index 095b6cfe44..5b28b9a390 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/accessor/CollectionAccessorFacetViaAccessor.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/accessor/CollectionAccessorFacetViaAccessor.java
@@ -19,9 +19,11 @@
 package org.apache.causeway.core.metamodel.facets.collections.accessor;
 
 import java.lang.reflect.Method;
+import java.util.Optional;
 import java.util.function.BiConsumer;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facets.ImperativeFacet;
@@ -41,11 +43,14 @@ implements ImperativeFacet {
 
     @Getter(onMethod_ = {@Override}) private final @NonNull Can<Method> methods;
 
+    private final String collId;
+
     public CollectionAccessorFacetViaAccessor(
             final ObjectSpecification declaringType,
             final Method method,
             final FacetHolder holder) {
         super(declaringType, holder);
+        this.collId = _Strings.decapitalize(method.getName().substring(3)); // infer object feature id from method name
         this.methods = ImperativeFacet.singleMethod(method);
     }
 
@@ -65,7 +70,9 @@ implements ImperativeFacet {
             return null;
         }
 
-        val collectionAdapter = getObjectManager().adapt(collectionOrArray);
+        //TODO optimize: the caller most likely already has an instance of coll ready to use
+        val coll = owningAdapter.getSpecification().getCollectionElseFail(collId); // doing this lazily after MM was populated
+        val collectionAdapter = getObjectManager().adapt(collectionOrArray, Optional.of(coll));
 
         final boolean filterForVisibility = getConfiguration().getCore().getMetaModel().isFilterVisibility();
         if(filterForVisibility) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutAnnotation.java
index b538533ebd..1cfe9ecdbb 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutAnnotation.java
@@ -43,12 +43,10 @@ extends ObjectNamedFacetAbstract {
         val domainObjectLayout = domainObjectLayoutIfAny.get();
 
         val singular = _Strings.emptyToNull(domainObjectLayout.named());
-        val plural = _Strings.emptyToNull(domainObjectLayout.plural());
 
         val nounForms = NounForms
                 .builder()
                 .singular(singular)
-                .plural(plural)
                 .build();
 
         if(nounForms.getSupportedNounForms().isEmpty()) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutXml.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutXml.java
index 20858843a3..bc1add45ee 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutXml.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/ObjectNamedFacetForDomainObjectLayoutXml.java
@@ -43,12 +43,12 @@ extends ObjectNamedFacetAbstract {
         }
 
         val singular = _Strings.emptyToNull(domainObjectLayout.getNamed());
-        val plural = _Strings.emptyToNull(domainObjectLayout.getPlural());
+//        val plural = _Strings.emptyToNull(domainObjectLayout.getPlural());
 
         val nounForms = NounForms
                 .builder()
                 .singular(singular)
-                .plural(plural)
+  //              .plural(plural)
                 .build();
 
         if(nounForms.getSupportedNounForms().isEmpty()) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/param/defaults/methodnum/ActionParameterDefaultsFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/param/defaults/methodnum/ActionParameterDefaultsFacetViaMethod.java
index 9159d7192f..fb5925af59 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/param/defaults/methodnum/ActionParameterDefaultsFacetViaMethod.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/param/defaults/methodnum/ActionParameterDefaultsFacetViaMethod.java
@@ -89,7 +89,7 @@ implements ImperativeFacet {
         return _NullSafe.streamAutodetect(defaultValue)
                 .map(pojo->pojo!=null
                     ? getObjectManager().adapt(pojo)
-                    : managedParam.getMetaModel().isPlural()
+                    : managedParam.getObjectFeature().isPlural()
                         ? null // assuming for non-scalar parameters, including null makes no sense
                         : ManagedObject.empty(managedParam.getElementType()))
                 .collect(Can.toCan());
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacet.java
index 9a026957be..498675b6ef 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacet.java
@@ -49,4 +49,5 @@ public interface PropertyOrCollectionAccessorFacet extends Facet {
             final InteractionInitiatedBy interactionInitiatedBy);
 
     ObjectSpecification getDeclaringType();
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/PropertyAutoCompleteFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/PropertyAutoCompleteFacet.java
index f4bf0ca89c..d2ead5fbdb 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/PropertyAutoCompleteFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/PropertyAutoCompleteFacet.java
@@ -21,6 +21,7 @@ package org.apache.causeway.core.metamodel.facets.properties.autocomplete;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 /**
  * Provides a set of auto-complete choices for a property.
@@ -40,6 +41,7 @@ public interface PropertyAutoCompleteFacet extends Facet {
      * Gets the available auto-complete choices for this property.
      */
     public Object[] autoComplete(
+            final ObjectFeature objectFeature,
             final ManagedObject inObject,
             final String searchArg,
             final InteractionInitiatedBy interactionInitiatedBy);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethod.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethod.java
index 4eacf749d4..1674135f9f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethod.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethod.java
@@ -19,6 +19,7 @@
 package org.apache.causeway.core.metamodel.facets.properties.autocomplete.method;
 
 import java.lang.reflect.Method;
+import java.util.Optional;
 import java.util.function.BiConsumer;
 
 import org.apache.causeway.commons.collections.Can;
@@ -30,6 +31,7 @@ import org.apache.causeway.core.metamodel.facets.properties.autocomplete.Propert
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.MmInvokeUtil;
 import org.apache.causeway.core.metamodel.object.MmVisibilityUtil;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.Getter;
 import lombok.NonNull;
@@ -44,8 +46,8 @@ implements ImperativeFacet {
     private final int minLength;
 
     public PropertyAutoCompleteFacetMethod(
-            final Method method,
-            final Class<?> choicesClass,
+            final Method method, // member support method
+            final Class<?> choicesClass, // return type of the getter, which is subject to be supported by this facet
             final FacetHolder holder) {
         super(holder);
         this.methods = ImperativeFacet.singleMethod(method);
@@ -65,6 +67,7 @@ implements ImperativeFacet {
 
     @Override
     public Object[] autoComplete(
+            final ObjectFeature objectFeature,
             final ManagedObject owningAdapter,
             final String searchArg,
             final InteractionInitiatedBy interactionInitiatedBy) {
@@ -75,7 +78,7 @@ implements ImperativeFacet {
             return null;
         }
 
-        val collectionAdapter = getObjectManager().adapt(collectionOrArray);
+        val collectionAdapter = getObjectManager().adapt(collectionOrArray, Optional.of(objectFeature));
 
         val visiblePojos = MmVisibilityUtil
                 .visiblePojosAsArray(collectionAdapter, interactionInitiatedBy);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethodFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethodFactory.java
index 853206a1d1..0e3ac06383 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethodFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/autocomplete/method/PropertyAutoCompleteFacetMethodFactory.java
@@ -42,7 +42,6 @@ extends MemberSupportFacetFactoryAbstract {
             final MethodFinder methodFinder) {
 
         val getterOrMixinMain = processMethodContext.getMethod();
-        val getterType = getterOrMixinMain.getReturnType();
 
         methodFinder
         .streamMethodsMatchingSignature(STRING_ARG)
@@ -50,7 +49,7 @@ extends MemberSupportFacetFactoryAbstract {
         .forEach(autoCompleteMethod->{
             addFacet(
                     new PropertyAutoCompleteFacetMethod(
-                            autoCompleteMethod, getterType, processMethodContext.getFacetHolder()));
+                            autoCompleteMethod, getterOrMixinMain.getReturnType(), processMethodContext.getFacetHolder()));
         });
 
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
index 3a72472f43..b68a4972f9 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
@@ -199,7 +199,7 @@ extends MemberInteraction<ManagedAction, ActionInteraction> {
             final String memberId,
             final Where where) {
         val propertyOwner = associatedWithProperty.getOwner();
-        val prop = associatedWithProperty.getMetaModel();
+        val prop = associatedWithProperty.getObjectFeature();
         val elementType = prop.getElementType();
 
         val valueFacet = elementType.isValue()
@@ -239,7 +239,7 @@ extends MemberInteraction<ManagedAction, ActionInteraction> {
 
         val actionOwner = parameterNegotiationModel.getActionTarget();
         val param = parameterNegotiationModel.getParamModels().getElseFail(paramIndex);
-        val elementType = param.getMetaModel().getElementType();
+        val elementType = param.getObjectFeature().getElementType();
 
         val valueFacet = elementType.isValue()
                 ? (ValueFacet<?>) elementType.valueFacet().orElse(null)
@@ -254,8 +254,8 @@ extends MemberInteraction<ManagedAction, ActionInteraction> {
             val compositeValueNullable = parameterNegotiationModel.getParamValue(paramIndex);
             val compositeValue =
                     ManagedObjects.nullOrEmptyToDefault(elementType, compositeValueNullable, ()->
-                        valueFacet.selectDefaultsProviderForParameter(param.getMetaModel())
-                            .orElseThrow(()->onMissingDefaultsProvider(param.getMetaModel()))
+                        valueFacet.selectDefaultsProviderForParameter(param.getObjectFeature())
+                            .orElseThrow(()->onMissingDefaultsProvider(param.getObjectFeature()))
                             .getDefaultValue());
 
             val mixinAction = valueFacet.selectCompositeValueMixinForParameter(parameterNegotiationModel, paramIndex);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedAction.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedAction.java
index e18a23f346..ce328dac00 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedAction.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedAction.java
@@ -25,8 +25,6 @@ import org.springframework.lang.Nullable;
 
 import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.applib.annotation.Where;
-import org.apache.causeway.applib.services.inject.ServiceInjector;
-import org.apache.causeway.applib.services.registry.ServiceRegistry;
 import org.apache.causeway.applib.services.routing.RoutingService;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.functional.Railway;
@@ -34,7 +32,6 @@ import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.commons.internal.base._Lazy;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
@@ -119,7 +116,7 @@ public final class ManagedAction extends ManagedMember {
     }
 
     @Override
-    public ObjectAction getMetaModel() {
+    public ObjectAction getObjectFeature() {
         return getAction();
     }
 
@@ -169,13 +166,13 @@ public final class ManagedAction extends ManagedMember {
         }
 
         val resultPojo = actionResult.getPojo();
-        val objManager = mmc().getObjectManager();
+        val objManager = getObjectManager();
 
         val resultAdapter = getRoutingServices().stream()
                 .filter(routingService->routingService.canRoute(resultPojo))
                 .map(routingService->routingService.route(resultPojo))
                 .filter(_NullSafe::isPresent)
-                .map(objManager::adapt)
+                .map(pojoAfterAppliedRouting->objManager.adapt(pojoAfterAppliedRouting, Optional.of(getAction())))
                 .filter(_NullSafe::isPresent)
                 .findFirst()
                 .orElse(actionResult);
@@ -195,25 +192,11 @@ public final class ManagedAction extends ManagedMember {
         return getServiceRegistry().select(RoutingService.class);
     }
 
-    // -- SERVICES
-
-    private MetaModelContext mmc() {
-        return getAction().getMetaModelContext();
-    }
-
-    private ServiceInjector getServiceInjector() {
-        return mmc().getServiceInjector();
-    }
-
-    private ServiceRegistry getServiceRegistry() {
-        return mmc().getServiceRegistry();
-    }
-
     // -- MEMENTO FOR ARGUMENT LIST
 
     public MementoForArgs getMementoForArgs(final Can<ManagedObject> args) {
         return MementoForArgs.create(
-                getMetaModel().getMetaModelContext().getObjectManager(),
+                getObjectManager(),
                 args);
     }
 
@@ -229,11 +212,10 @@ public final class ManagedAction extends ManagedMember {
 
         private final Can<ObjectMemento> argsMementos;
 
-        public Can<ManagedObject> getArgumentList(final ObjectAction actionMeta) {
-            val argTypes = actionMeta.getParameterTypes();
-            val objectManager = actionMeta.getMetaModelContext().getObjectManager();
-            return argsMementos.zipMap(argTypes, (argSpec, argMemento)->
-                objectManager.demementify(argMemento, argSpec));
+        public Can<ManagedObject> getArgumentList(final ObjectAction act) {
+            val objectManager = act.getObjectManager();
+            return argsMementos.zipMap(act.getParameters(), (argMemento, feature)->
+                objectManager.demementify(feature, argMemento));
         }
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedCollection.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedCollection.java
index 633a7d2732..492b3e1205 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedCollection.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedCollection.java
@@ -69,7 +69,7 @@ public final class ManagedCollection extends ManagedMember {
     }
 
     @Override
-    public OneToManyAssociation getMetaModel() {
+    public OneToManyAssociation getObjectFeature() {
         return getCollection();
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java
index e075aef46a..01c31e926e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java
@@ -26,9 +26,9 @@ import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
+import org.apache.causeway.core.metamodel.spec.feature.HasObjectFeature;
 
-public interface ManagedFeature {
+public interface ManagedFeature extends HasObjectFeature {
 
     Identifier getIdentifier();
 
@@ -57,8 +57,6 @@ public interface ManagedFeature {
         return getElementType().getCorrespondingClass();
     }
 
-    ObjectFeature getMetaModel();
-
     /**
      * @param facetType
      * @return Optionally the feature's facet of the specified {@code facetType}
@@ -66,7 +64,7 @@ public interface ManagedFeature {
      */
     default <T extends Facet> Optional<T> getFacet(final @Nullable Class<T> facetType) {
         return facetType!=null
-                ? Optional.ofNullable(getMetaModel().getFacet(facetType))
+                ? Optional.ofNullable(getObjectFeature().getFacet(facetType))
                 : Optional.empty();
     }
 
@@ -77,7 +75,7 @@ public interface ManagedFeature {
      */
     default <T extends Facet> boolean hasFacet(final @Nullable Class<T> facetType) {
         return facetType!=null
-                ? getMetaModel().getFacet(facetType)!=null
+                ? getObjectFeature().getFacet(facetType)!=null
                 : false;
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java
index ddb8fb23ac..17462b8fcb 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java
@@ -75,32 +75,32 @@ implements ManagedFeature {
 
 
     @Override
-    public abstract ObjectMember getMetaModel();
+    public abstract ObjectMember getObjectFeature();
 
     public abstract Identifier.Type getMemberType();
 
     @Override
     public ObjectSpecification getElementType() {
-        return getMetaModel().getElementType();
+        return getObjectFeature().getElementType();
     }
 
     public String getId() {
-        return getMetaModel().getId();
+        return getObjectFeature().getId();
     }
 
     @Override
     public Identifier getIdentifier() {
-        return getMetaModel().getFeatureIdentifier();
+        return getObjectFeature().getFeatureIdentifier();
     }
 
     @Override
     public String getFriendlyName() {
-        return getMetaModel().getFriendlyName(this::getOwner);
+        return getObjectFeature().getFriendlyName(this::getOwner);
     }
 
     @Override
     public Optional<String> getDescription() {
-        return getMetaModel().getDescription(this::getOwner);
+        return getObjectFeature().getDescription(this::getOwner);
     }
 
     @Getter @Setter @NonNull
@@ -114,7 +114,7 @@ implements ManagedFeature {
 
         try {
             val visibilityConsent =
-                    getMetaModel()
+                    getObjectFeature()
                     .isVisible(getOwner(), InteractionInitiatedBy.USER, where);
 
             return visibilityConsent.isVetoed()
@@ -138,7 +138,7 @@ implements ManagedFeature {
         try {
 
             val usabilityConsent =
-                    getMetaModel()
+                    getObjectFeature()
                     .isUsable(getOwner(), InteractionInitiatedBy.USER, where);
 
             return usabilityConsent.isVetoed()
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java
index b66faf726c..1c339bd85e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java
@@ -37,7 +37,7 @@ implements
     ManagedFeature {
 
     public abstract int getParamNr();
-    @Override public abstract ObjectActionParameter getMetaModel();
+    @Override public abstract ObjectActionParameter getObjectFeature();
     public abstract ParameterNegotiationModel getNegotiationModel();
 
     /**
@@ -50,7 +50,7 @@ implements
             val head = getNegotiationModel().getHead();
 
             val usabilityConsent =
-                    getMetaModel()
+                    getObjectFeature()
                     .isUsable(head, params, InteractionInitiatedBy.USER);
 
             return usabilityConsent.isVetoed()
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedProperty.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedProperty.java
index 625603daf3..4ee6e70ffd 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedProperty.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedProperty.java
@@ -80,7 +80,7 @@ extends ManagedMember {
     }
 
     @Override
-    public OneToOneAssociation getMetaModel() {
+    public OneToOneAssociation getObjectFeature() {
         return getProperty();
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedValue.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedValue.java
index 2085635a7a..cb04543462 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedValue.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedValue.java
@@ -27,13 +27,11 @@ import org.apache.causeway.commons.binding.Observable;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
-import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.HasObjectFeature;
 
 import lombok.val;
 
-public interface ManagedValue {
-
-    ObjectSpecification getElementType();
+public interface ManagedValue extends HasObjectFeature {
 
     Bindable<ManagedObject> getValue();
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java
index e4e229c15c..66faa2ae38 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java
@@ -146,7 +146,7 @@ public class ParameterNegotiationModel {
     }
 
     @NonNull public ObjectActionParameter getParamMetamodel(final int paramNr) {
-        return paramModels.getElseFail(paramNr).getMetaModel();
+        return paramModels.getElseFail(paramNr).getObjectFeature();
     }
 
     @NonNull public Bindable<ManagedObject> getBindableParamValue(final int paramNr) {
@@ -235,12 +235,19 @@ public class ParameterNegotiationModel {
         paramModels.getElseFail(paramIndex).getBindableParamValue().setValue(emptyValue);
     }
 
-    @NonNull public ManagedObject adaptParamValuePojo(final int paramIndex,
+    @NonNull public ManagedObject adaptParamValuePojo(
+            final int paramIndex,
             final @Nullable Object newParamValuePojo) {
         val paramMeta = getParamMetamodel(paramIndex);
         return ManagedObject.adaptParameter(paramMeta, newParamValuePojo);
     }
 
+    public void setParamValuePojo(
+            final int paramIndex,
+            final @Nullable Object newParamValuePojo) {
+        setParamValue(paramIndex, adaptParamValuePojo(paramIndex, newParamValuePojo));
+    }
+
     /**
      * exposed for testing
      */
@@ -267,7 +274,7 @@ public class ParameterNegotiationModel {
     private static class ParameterModel extends ManagedParameter {
 
         @Getter(onMethod_ = {@Override}) private final int paramNr;
-        @Getter(onMethod_ = {@Override}) @NonNull private final ObjectActionParameter metaModel;
+        @Getter(onMethod_ = {@Override}) @NonNull private final ObjectActionParameter objectFeature;
         @Getter(onMethod_ = {@Override}) @NonNull private final ParameterNegotiationModel negotiationModel;
         @Getter @NonNull private final _BindableAbstract<ManagedObject> bindableParamValue;
         @Getter @NonNull private final BooleanBindable bindableParamValueDirtyFlag;
@@ -286,18 +293,18 @@ public class ParameterNegotiationModel {
             val action = negotiationModel.getHead().getMetaModel();
 
             this.paramNr = paramNr;
-            this.metaModel = action.getParameters().getElseFail(paramNr);
+            this.objectFeature = action.getParameters().getElseFail(paramNr);
             this.negotiationModel = negotiationModel;
 
             bindableParamValue = _Bindables.forValue(initialValue);
             bindableParamValueDirtyFlag = _Bindables.forBoolean(false);
 
             //bindableParamValue.setValueRefiner(MmEntityUtil::refetch); no longer used
-            bindableParamValue.setValueGuard(MmAssertionUtil.assertInstanceOf(metaModel.getElementType()));
+            bindableParamValue.setValueGuard(MmAssertionUtil.assertInstanceOf(objectFeature.getElementType()));
             bindableParamValue.addListener((event, oldValue, newValue)->{
                 if(newValue==null) {
                     // lift null to empty ...
-                    bindableParamValue.setValue(metaModel.getEmpty()); // triggers this event again
+                    bindableParamValue.setValue(objectFeature.getEmpty()); // triggers this event again
                     return;
                 }
                 getNegotiationModel().onNewParamValue();
@@ -305,20 +312,20 @@ public class ParameterNegotiationModel {
             });
 
             // has either autoComplete, choices, or none
-            observableParamChoices = metaModel.hasAutoComplete()
+            observableParamChoices = objectFeature.hasAutoComplete()
             ? _Observables.lazy(()->
-                getMetaModel().getAutoComplete(
+                getObjectFeature().getAutoComplete(
                         getNegotiationModel(),
                         getBindableParamSearchArgument().getValue(),
                         InteractionInitiatedBy.USER))
-            : metaModel.hasChoices()
+            : objectFeature.hasChoices()
                 ? _Observables.lazy(()->
-                    getMetaModel().getChoices(getNegotiationModel(), InteractionInitiatedBy.USER))
+                    getObjectFeature().getChoices(getNegotiationModel(), InteractionInitiatedBy.USER))
                 : _Observables.lazy(Can::empty);
 
             // if has autoComplete, then activate the search argument
             bindableParamSearchArgument = _Bindables.forValue(null);
-            if(metaModel.hasAutoComplete()) {
+            if(objectFeature.hasAutoComplete()) {
                 bindableParamSearchArgument.addListener((e,o,n)->{
                     observableParamChoices.invalidate();
                 });
@@ -327,7 +334,7 @@ public class ParameterNegotiationModel {
             // validate this parameter, but only when validationFeedback has been activated
             observableParamValidation = _Observables.lazy(()->
                 isValidationFeedbackActive()
-                ? getMetaModel()
+                ? getObjectFeature()
                         .isValid(getNegotiationModel().getHead(), getNegotiationModel().getParamValues(), InteractionInitiatedBy.USER)
                         .getReason()
                 : (String)null);
@@ -347,23 +354,23 @@ public class ParameterNegotiationModel {
 
         @Override
         public Identifier getIdentifier() {
-            return getMetaModel().getFeatureIdentifier();
+            return getObjectFeature().getFeatureIdentifier();
         }
 
         @Override
         public String getFriendlyName() {
-            return getMetaModel().getStaticFriendlyName()
+            return getObjectFeature().getStaticFriendlyName()
                     .orElseThrow(_Exceptions::unexpectedCodeReach);
         }
 
         @Override
         public Optional<String> getDescription() {
-            return getMetaModel().getStaticDescription();
+            return getObjectFeature().getStaticDescription();
         }
 
         @Override
         public ObjectSpecification getElementType() {
-            return getMetaModel().getElementType();
+            return getObjectFeature().getElementType();
         }
 
         @Override
@@ -376,7 +383,7 @@ public class ParameterNegotiationModel {
             if(bindableParamAsTitle==null) {
                 // value types should have associated rederers via value semantics
                 bindableParamAsTitle = _BindingUtil
-                        .bindAsFormated(TargetFormat.TITLE, metaModel, bindableParamValue);
+                        .bindAsFormated(TargetFormat.TITLE, objectFeature, bindableParamValue);
             }
             return bindableParamAsTitle;
         }
@@ -386,14 +393,14 @@ public class ParameterNegotiationModel {
             if(bindableParamAsHtml==null) {
                 // value types should have associated rederers via value semantics
                 bindableParamAsHtml = _BindingUtil
-                        .bindAsFormated(TargetFormat.HTML, metaModel, bindableParamValue);
+                        .bindAsFormated(TargetFormat.HTML, objectFeature, bindableParamValue);
             }
             return bindableParamAsHtml;
         }
 
         @Override
         public boolean isValueAsParsableTextSupported() {
-            return _BindingUtil.hasParser(metaModel);
+            return _BindingUtil.hasParser(objectFeature);
         }
 
         @Override
@@ -402,7 +409,7 @@ public class ParameterNegotiationModel {
                 // value types should have associated parsers/formatters via value semantics
                 // except for composite value types, which might have not
                 bindableParamAsParsableText = (Bindable<String>) _BindingUtil
-                        .bindAsFormated(TargetFormat.PARSABLE_TEXT, metaModel, bindableParamValue);
+                        .bindAsFormated(TargetFormat.PARSABLE_TEXT, objectFeature, bindableParamValue);
             }
             return bindableParamAsParsableText;
         }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/PropertyNegotiationModel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/PropertyNegotiationModel.java
index 20c2a99f31..7ce43ff60d 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/PropertyNegotiationModel.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/PropertyNegotiationModel.java
@@ -34,6 +34,7 @@ import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.object.MmAssertionUtil;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.Getter;
 import lombok.NonNull;
@@ -54,7 +55,7 @@ public class PropertyNegotiationModel implements ManagedValue {
     PropertyNegotiationModel(
             final ManagedProperty managedProperty) {
         this.managedProperty = managedProperty;
-        val propMeta = managedProperty.getMetaModel();
+        val propMeta = managedProperty.getObjectFeature();
 
         validationFeedbackActive = _Bindables.forValue(false);
 
@@ -108,6 +109,11 @@ public class PropertyNegotiationModel implements ManagedValue {
         return isCurrentValueAbsent;
     }
 
+    @Override
+    public ObjectFeature getObjectFeature() {
+        return managedProperty.getObjectFeature();
+    }
+
     @Override
     public ObjectSpecification getElementType() {
         return managedProperty.getElementType();
@@ -123,7 +129,7 @@ public class PropertyNegotiationModel implements ManagedValue {
         if(proposedValueAsTitle==null) {
             // value types should have associated renderer via value semantics
             proposedValueAsTitle = _BindingUtil
-                    .bindAsFormated(TargetFormat.TITLE, managedProperty.getMetaModel(), proposedValue);
+                    .bindAsFormated(TargetFormat.TITLE, managedProperty.getObjectFeature(), proposedValue);
         }
         return proposedValueAsTitle;
     }
@@ -133,14 +139,14 @@ public class PropertyNegotiationModel implements ManagedValue {
         if(proposedValueAsHtml==null) {
             // value types should have associated renderer via value semantics
             proposedValueAsHtml = _BindingUtil
-                    .bindAsFormated(TargetFormat.HTML, managedProperty.getMetaModel(), proposedValue);
+                    .bindAsFormated(TargetFormat.HTML, managedProperty.getObjectFeature(), proposedValue);
         }
         return proposedValueAsHtml;
     }
 
     @Override
     public boolean isValueAsParsableTextSupported() {
-        return _BindingUtil.hasParser(managedProperty.getMetaModel());
+        return _BindingUtil.hasParser(managedProperty.getObjectFeature());
     }
 
     @Override
@@ -149,7 +155,7 @@ public class PropertyNegotiationModel implements ManagedValue {
             // value types should have associated parsers/formatters via value semantics
             // except for composite value types, which might have not
             proposedValueAsParsableText = (Bindable<String>) _BindingUtil
-                    .bindAsFormated(TargetFormat.PARSABLE_TEXT, managedProperty.getMetaModel(), proposedValue);
+                    .bindAsFormated(TargetFormat.PARSABLE_TEXT, managedProperty.getObjectFeature(), proposedValue);
         }
         return proposedValueAsParsableText;
     }
@@ -226,4 +232,6 @@ public class PropertyNegotiationModel implements ManagedValue {
     }
 
 
+
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/nonscalar/DataTableModel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
index 6bf11177e0..0eb8de99ab 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
@@ -126,7 +126,7 @@ implements MultiselectChoices {
             dataElements.getValue().stream()
                 //XXX future extension: filter by searchArgument
                 .filter(this::ignoreHidden)
-                .sorted(managedMember.getMetaModel().getElementComparator()
+                .sorted(managedMember.getObjectFeature().getElementComparator()
                         .orElseGet(()->(a, b)->0)) // else don't sort (no-op comparator for streams)
                 .map(domainObject->new DataRow(this, domainObject))
                 .collect(Can.toCan()));
@@ -181,7 +181,7 @@ implements MultiselectChoices {
     }
 
     public ObjectMember getMetaModel() {
-        return managedMember.getMetaModel();
+        return managedMember.getObjectFeature();
     }
 
     public ObjectSpecification getElementType() {
@@ -301,7 +301,7 @@ implements MultiselectChoices {
             }
             val actionInteraction = ActionInteraction.start(owner, memberId, where);
             val managedAction = actionInteraction.getManagedActionElseFail();
-            val args = argsMemento.getArgumentList(managedAction.getMetaModel());
+            val args = argsMemento.getArgumentList(managedAction.getObjectFeature());
             // invocation bypassing domain events (pass-through)
             val actionResult = managedAction.invoke(args, InteractionInitiatedBy.PASS_THROUGH)
                     .getSuccessElseFail();
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java
index 92a5756797..907879ade8 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java
@@ -242,16 +242,16 @@ public class LayoutFacetUtil {
         }
     }
 
-    public void setPluralIfAny(
-            final DomainObjectLayoutData domainObjectLayoutData,
-            final FacetHolder facetHolder) {
-
-        facetHolder
-        .lookupNonFallbackFacet(ObjectNamedFacet.class)
-        .filter(namedFacet->namedFacet.getSupportedNounForms().contains(NounForm.PLURAL))
-        .map(ObjectNamedFacet::pluralTranslated)
-        .ifPresent(domainObjectLayoutData::setPlural);
-    }
+//    public void setPluralIfAny(
+//            final DomainObjectLayoutData domainObjectLayoutData,
+//            final FacetHolder facetHolder) {
+//
+//        facetHolder
+//        .lookupNonFallbackFacet(ObjectNamedFacet.class)
+//        .filter(namedFacet->namedFacet.getSupportedNounForms().contains(NounForm.PLURAL))
+//        .map(ObjectNamedFacet::pluralTranslated)
+//        .ifPresent(domainObjectLayoutData::setPlural);
+//    }
 
     public void setActionPositionIfAny(
             final ActionLayoutData actionLayoutData,
@@ -395,7 +395,7 @@ public class LayoutFacetUtil {
             setCssClassFaIfAny(domainObjectLayoutData, objectSpec);
             setObjectDescribedIfAny(domainObjectLayoutData, objectSpec);
             setObjectNamedIfAny(domainObjectLayoutData, objectSpec);
-            setPluralIfAny(domainObjectLayoutData, objectSpec);
+            //setPluralIfAny(domainObjectLayoutData, objectSpec);
         }
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
index a47f960251..9cd2d42486 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
@@ -33,7 +33,10 @@ import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.causeway.core.metamodel.object.ManagedObject.Specialization.BookmarkPolicy;
 import org.apache.causeway.core.metamodel.spec.HasObjectSpecification;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
+import org.apache.causeway.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 
 import lombok.Getter;
@@ -507,16 +510,18 @@ extends
     }
     /**
      * PACKED
-     * @param elementSpec - required
+     * @param objectFeature - required;
+     *      the {@link ObjectFeature} which this packed {@link ManagedObject} originates from,
+     *      either an {@link ObjectAction}, an {@link ObjectActionParameter} or an {@link OneToManyAssociation}
      * @param nonScalar - if <code>null</code> uses {@link Can#empty()} instead
      * @see ManagedObject.Specialization.TypePolicy#ABSTRACT_TYPE_ALLOWED
      * @see ManagedObject.Specialization.BookmarkPolicy#NO_BOOKMARK
      * @see ManagedObject.Specialization.PojoPolicy#PACKED
      */
     static PackedManagedObject packed(
-            final @NonNull ObjectSpecification elementSpec,
+            final @NonNull ObjectFeature objectFeature,
             final @Nullable Can<ManagedObject> nonScalar) {
-        return new _ManagedObjectPacked(elementSpec, nonScalar);
+        return new _ManagedObjectPacked(objectFeature, objectFeature.getElementType(), nonScalar);
     }
 
     /**
@@ -600,7 +605,7 @@ extends
         return param.isSingular()
                 ? adaptSingular(param.getElementType(), paramValue)
                 // else adopt each element pojo then pack
-                : packed(param.getElementType(),
+                : packed(param,
                         ManagedObjects.adaptMultipleOfType(param.getElementType(), paramValue));
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmTitleUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmTitleUtil.java
index 43d7457c71..e5b52d7d4d 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmTitleUtil.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmTitleUtil.java
@@ -43,7 +43,7 @@ public class MmTitleUtil {
         return ManagedObjects.isPacked(managedObject)
                 ? "(multiple objects)"
                 : managedObject != null
-                    ? _InternalTitleUtil.titleString(
+                    ? _InternalTitleUtil.titleStringSingular(
                             TitleRenderRequest.builder()
                             .object(managedObject)
                             .skipTitlePartEvaluator(skipTitlePart)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/PackedManagedObject.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/PackedManagedObject.java
index c13730169f..4cfc8124f5 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/PackedManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/PackedManagedObject.java
@@ -19,13 +19,14 @@
 package org.apache.causeway.core.metamodel.object;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.core.metamodel.spec.feature.HasObjectFeature;
 
 /**
  * 'Collection' of {@link ManagedObject}s.
  * @see ManagedObject.Specialization#PACKED
  */
 public interface PackedManagedObject
-extends ManagedObject {
+extends ManagedObject, HasObjectFeature {
 
     Can<ManagedObject> unpack();
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_InternalTitleUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_InternalTitleUtil.java
index 4589ad8e34..b6f9919e84 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_InternalTitleUtil.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_InternalTitleUtil.java
@@ -20,6 +20,7 @@ package org.apache.causeway.core.metamodel.object;
 
 import java.util.Optional;
 
+import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.core.metamodel.facets.collections.CollectionFacet;
 import org.apache.causeway.core.metamodel.facets.object.title.TitleRenderRequest;
 
@@ -32,7 +33,9 @@ final class _InternalTitleUtil {
 
     // -- TITLE SUPPORT
 
-    String titleString(@NonNull final TitleRenderRequest titleRenderRequest) {
+    String titleString(
+            final String pluralName,
+            final @NonNull TitleRenderRequest titleRenderRequest) {
 
         val managedObject = titleRenderRequest.getObject();
 
@@ -44,10 +47,44 @@ final class _InternalTitleUtil {
             ? objectTitleString(titleRenderRequest)
                     .trim()
             : collectionTitleString(
+                    pluralName,
                     managedObject,
                     managedObject.getSpecification().getFacet(CollectionFacet.class));
     }
 
+    String titleStringSingular(
+            final @NonNull TitleRenderRequest titleRenderRequest) {
+
+        val managedObject = titleRenderRequest.getObject();
+
+        if(managedObject.getSpecialization().isUnspecified()) {
+            return managedObject.getTitle();
+        }
+
+        _Assert.assertTrue(managedObject.getSpecification().isSingular());
+
+        return objectTitleString(titleRenderRequest).trim();
+    }
+
+    String titleStringPlural(
+            final String pluralName,
+            final @NonNull TitleRenderRequest titleRenderRequest) {
+
+        val managedObject = titleRenderRequest.getObject();
+
+        if(managedObject.getSpecialization().isUnspecified()) {
+            return managedObject.getTitle();
+        }
+
+        _Assert.assertTrue(managedObject.getSpecification().isPlural());
+
+        return collectionTitleString(
+                    pluralName,
+                    managedObject,
+                    managedObject.getSpecification().getFacet(CollectionFacet.class));
+    }
+
+
     // -- HELPER
 
     String abbreviated(final String str, final int maxLength, final String suffix) {
@@ -68,7 +105,7 @@ final class _InternalTitleUtil {
                 .orElseGet(()->getDefaultTitle(managedObject));
     }
 
-    private String collectionTitleString(final ManagedObject managedObject, final CollectionFacet facet) {
+    private String collectionTitleString(final String pluralName, final ManagedObject managedObject, final CollectionFacet facet) {
         final int size = facet.size(managedObject);
         val elementSpec = managedObject.getElementSpecification().orElse(null);
         if (elementSpec == null
@@ -86,13 +123,13 @@ final class _InternalTitleUtil {
         } else {
             switch (size) {
             case -1:
-                return elementSpec.getPluralName();
+                return pluralName;
             case 0:
-                return "No " + elementSpec.getPluralName();
+                return "No " + pluralName;
             case 1:
                 return "1 " + elementSpec.getSingularName();
             default:
-                return size + " " + elementSpec.getPluralName();
+                return size + " " + pluralName;
             }
         }
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectPacked.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectPacked.java
index 16d134c23b..b2b8b431b9 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectPacked.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectPacked.java
@@ -25,7 +25,9 @@ import org.springframework.lang.Nullable;
 
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
+import lombok.Getter;
 import lombok.NonNull;
 
 /**
@@ -38,12 +40,16 @@ implements
     Bookmarkable.NoBookmark,
     PackedManagedObject {
 
+    @Getter(onMethod_={@Override})
+    private final @NonNull ObjectFeature objectFeature;
     private final @NonNull Can<ManagedObject> nonScalar;
 
     _ManagedObjectPacked(
+            final ObjectFeature objectFeature,
             final ObjectSpecification elementSpec,
             final @Nullable Can<ManagedObject> nonScalar) {
         super(Specialization.PACKED, elementSpec);
+        this.objectFeature = objectFeature;
         this.nonScalar = nonScalar!=null
                 ? nonScalar
                 : Can.empty();
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectSpecified.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectSpecified.java
index 1958790b7e..f1f00a19f3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectSpecified.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectSpecified.java
@@ -32,9 +32,9 @@ import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facets.object.title.TitleRenderRequest;
 import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMemento;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoCollection;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoForEmpty;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoForScalar;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoEmpty;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoPlural;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoSingular;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 
 import lombok.AccessLevel;
@@ -92,34 +92,39 @@ implements ManagedObject {
 
     @Override
     public String getTitle() {
-        return _InternalTitleUtil.titleString(
-                TitleRenderRequest.forObject(this));
+        return this instanceof PackedManagedObject
+                ? _InternalTitleUtil.titleStringPlural(
+                        ((PackedManagedObject)this).getObjectFeature().getCanonicalFriendlyName(),
+                        TitleRenderRequest.forObject(this))
+                : _InternalTitleUtil.titleStringSingular(
+                        TitleRenderRequest.forObject(this));
     }
 
     @Override
     public Optional<ObjectMemento> getMemento() {
         return this instanceof PackedManagedObject
                 ? Optional.ofNullable(mementoForPacked((PackedManagedObject)this))
-                : Optional.ofNullable(mementoForScalar(this));
+                : Optional.ofNullable(mementoForSingular(this));
     }
 
-    private ObjectMemento mementoForScalar(@Nullable final ManagedObject adapter) {
+    private ObjectMemento mementoForSingular(@Nullable final ManagedObject adapter) {
         MmAssertionUtil.assertPojoIsScalar(adapter);
-        return ObjectMementoForScalar.create(adapter)
+        return ObjectMementoSingular.create(adapter)
                 .map(ObjectMemento.class::cast)
                 .orElseGet(()->
                 ManagedObjects.isSpecified(adapter)
-                ? new ObjectMementoForEmpty(adapter.getLogicalType())
+                ? new ObjectMementoEmpty(adapter.getLogicalType())
                         : null);
     }
 
     private ObjectMemento mementoForPacked(@NonNull final PackedManagedObject packedAdapter) {
         val listOfMementos = packedAdapter.unpack().stream()
-                .map(this::mementoForScalar)
+                .map(this::mementoForSingular)
                 .collect(Collectors.toCollection(ArrayList::new)); // ArrayList is serializable
-        return ObjectMementoCollection.of(
+        return ObjectMementoPlural.of(
                 listOfMementos,
-                packedAdapter.getLogicalType());
+                packedAdapter.getLogicalType(),
+                packedAdapter.getObjectFeature().getFeatureIdentifier());
     }
 
     //XXX compares pojos by their 'equals' semantics -
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java
index d0686c5450..1bfda106b5 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java
@@ -19,7 +19,6 @@
 package org.apache.causeway.core.metamodel.objectmanager;
 
 import java.util.Optional;
-import java.util.function.Supplier;
 
 import org.springframework.lang.Nullable;
 
@@ -33,6 +32,7 @@ import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ProtoObject;
 import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMemento;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.NonNull;
 import lombok.val;
@@ -66,11 +66,17 @@ public interface ObjectManager extends HasMetaModelContext {
                     _Exceptions.unrecoverable("failed to create memento for  %s", object.getSpecification()));
     }
 
-    //TODO why not use loadObject(bookmark) instead
-    ManagedObject demementify(final ObjectMemento memento);
-    //TODO why not use loadObject(bookmark) instead
-    default ManagedObject demementify(final ObjectSpecification spec, final ObjectMemento memento) {
-        return demementify(memento);
+    /**
+     * TODO why not use loadObject(bookmark) instead?
+     *
+     * @param objectFeatureIfAny - nullable;
+     *      when present, can shortcut the process of reconstructing the associated {@link ObjectFeature} from the memento itself
+     * @param memento - nullable
+     */
+    ManagedObject demementify(@Nullable ObjectFeature objectFeatureIfAny, @Nullable ObjectMemento memento);
+
+    default ManagedObject demementify(@Nullable final ObjectMemento memento) {
+        return demementify(null, memento);
     }
 
     // -- SHORTCUTS
@@ -159,24 +165,24 @@ public interface ObjectManager extends HasMetaModelContext {
     // -- ADAPTING POJOS
 
     /**
-     * Not suitable for adapting a non-scalar.
+     * Suitable for adapting a singular (not plural).
      * If {@code pojo} is an entity, automatically memoizes its bookmark.
      * <p>
      * Resolves injection-points for the result. (Handles service injection.)
      */
     public default ManagedObject adapt(final @Nullable Object pojo) {
-        return adapt(pojo, ()->specForType(Object.class).orElseThrow());
+        return adapt(pojo, Optional.empty());
     }
 
     /**
-     * Suitable for adapting a non-scalar.
+     * Suitable for adapting a singular or plural.
      * If {@code pojo} is an entity, automatically memoizes its bookmark.
      * <p>
      * Resolves injection-points for the result. (Handles service injection.)
      */
     public default ManagedObject adapt(
             final @Nullable Object pojo,
-            final @NonNull Supplier<ObjectSpecification> fallbackElementType) {
+            final @NonNull Optional<ObjectFeature> objectFeatureIfPlural) {
         if(pojo==null) {
             return ManagedObject.unspecified();
         }
@@ -192,11 +198,13 @@ public interface ObjectManager extends HasMetaModelContext {
         return spec.isSingular()
                 ? ManagedObject.adaptSingular(spec, pojo)
                 : ManagedObject.packed(
-                        spec.getElementSpecification().orElseGet(fallbackElementType),
+                        objectFeatureIfPlural.orElseThrow(()->_Exceptions
+                                .unrecoverable("framework bug: for the plural case an ObjectFeature is required")),
                         _NullSafe.streamAutodetect(pojo)
                         .map(element->adapt(element))
                         .collect(Can.toCan()));
     }
 
 
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManagerDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManagerDefault.java
index 345de4ebcc..6cfbfcd902 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManagerDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManagerDefault.java
@@ -34,9 +34,10 @@ import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMemento;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoCollection;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoForEmpty;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoForScalar;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoEmpty;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoPlural;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoSingular;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -69,14 +70,14 @@ public class ObjectManagerDefault implements ObjectManager {
     }
 
     @Override
-    public ManagedObject demementify(final @Nullable ObjectMemento memento) {
+    public ManagedObject demementify(final @Nullable ObjectFeature objectFeatureIfAny, final @Nullable ObjectMemento memento) {
 
         if(memento==null) {
             return null;
         }
 
-        if(memento instanceof ObjectMementoForEmpty) {
-            val objectMementoForEmpty = (ObjectMementoForEmpty) memento;
+        if(memento instanceof ObjectMementoEmpty) {
+            val objectMementoForEmpty = (ObjectMementoEmpty) memento;
             val logicalType = objectMementoForEmpty.getLogicalType();
             val spec = getSpecificationLoader().specForLogicalType(logicalType);
             return spec.isPresent()
@@ -84,20 +85,19 @@ public class ObjectManagerDefault implements ObjectManager {
                     : ManagedObject.unspecified();
         }
 
-        if(memento instanceof ObjectMementoCollection) {
-            val objectMementoCollection = (ObjectMementoCollection) memento;
-
-            val elementSpec = getSpecificationLoader().specForLogicalTypeNameElseFail(memento.getLogicalTypeName());
-
-            val objects = objectMementoCollection.unwrapList().stream()
-                    .map(this::demementify)
+        if(memento instanceof ObjectMementoPlural) {
+            val objectMementoPlural = (ObjectMementoPlural) memento;
+            val objectFeature = objectFeatureIfAny!=null
+                    ? objectFeatureIfAny
+                    : getSpecificationLoader().loadFeatureElseFail(objectMementoPlural.getFeatureId());
+            val objects = objectMementoPlural.unwrapList().stream()
+                    .map(_memento->demementify(objectFeature, _memento))
                     .collect(Can.toCan());
-
-            return ManagedObject.packed(elementSpec, objects);
+            return ManagedObject.packed(objectFeature, objects);
         }
 
-        if(memento instanceof ObjectMementoForScalar) {
-            val objectMementoAdapter = (ObjectMementoForScalar) memento;
+        if(memento instanceof ObjectMementoSingular) {
+            val objectMementoAdapter = (ObjectMementoSingular) memento;
             return objectMementoAdapter.reconstructObject(getMetaModelContext());
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMemento.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMemento.java
index ff0fba5f3b..528b1bec53 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMemento.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMemento.java
@@ -27,13 +27,13 @@ import java.util.Optional;
 import org.springframework.lang.Nullable;
 
 import org.apache.causeway.applib.id.HasLogicalType;
-import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.services.bookmark.BookmarkHolder;
 import org.apache.causeway.commons.internal.base._Bytes;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.resources._Serializables;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.val;
 
@@ -57,14 +57,14 @@ public interface ObjectMemento extends BookmarkHolder, HasLogicalType, Serializa
     // -- FACTORIES
 
     static ObjectMemento pack(
-            final Collection<ObjectMemento> container,
-            final LogicalType logicalType) {
+            final ObjectFeature objectFeature,
+            final Collection<ObjectMemento> container) {
 
         // ArrayList is serializable
         if(container instanceof ArrayList) {
-            return ObjectMementoCollection.of((ArrayList<ObjectMemento>)container, logicalType);
+            return ObjectMementoPlural.of(objectFeature, (ArrayList<ObjectMemento>)container);
         }
-        return ObjectMementoCollection.of(_Lists.newArrayList(container), logicalType);
+        return ObjectMementoPlural.of(objectFeature, _Lists.newArrayList(container));
     }
 
     // ArrayList is serializable
@@ -72,10 +72,10 @@ public interface ObjectMemento extends BookmarkHolder, HasLogicalType, Serializa
         if(memento==null) {
             return Optional.empty();
         }
-        if(!(memento instanceof ObjectMementoCollection)) {
+        if(!(memento instanceof ObjectMementoPlural)) {
             return Optional.empty();
         }
-        return Optional.ofNullable(((ObjectMementoCollection)memento).unwrapList());
+        return Optional.ofNullable(((ObjectMementoPlural)memento).unwrapList());
     }
 
     @Nullable
@@ -105,4 +105,5 @@ public interface ObjectMemento extends BookmarkHolder, HasLogicalType, Serializa
 
     }
 
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoEmpty.java
similarity index 96%
rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java
rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoEmpty.java
index 8ed48882e1..5eea0611a8 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoEmpty.java
@@ -33,7 +33,7 @@ import lombok.ToString;
  */
 @ToString
 @RequiredArgsConstructor
-public class ObjectMementoForEmpty implements ObjectMemento {
+public class ObjectMementoEmpty implements ObjectMemento {
 
     private static final long serialVersionUID = 1L;
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoCollection.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoPlural.java
similarity index 77%
rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoCollection.java
rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoPlural.java
index 122817e528..6590f7137f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoCollection.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoPlural.java
@@ -20,9 +20,11 @@ package org.apache.causeway.core.metamodel.objectmanager.memento;
 
 import java.util.ArrayList;
 
+import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.Getter;
 import lombok.NonNull;
@@ -32,7 +34,7 @@ import lombok.Value;
  * @since 2.0
  */
 @Value(staticConstructor = "of")
-public final class ObjectMementoCollection implements ObjectMemento {
+public final class ObjectMementoPlural implements ObjectMemento {
 
     private static final long serialVersionUID = 1L;
 
@@ -41,6 +43,15 @@ public final class ObjectMementoCollection implements ObjectMemento {
     @Getter(onMethod_ = {@Override})
     @NonNull private final LogicalType logicalType;
 
+    @Getter
+    @NonNull private final Identifier featureId;
+
+    public static ObjectMementoPlural of(
+            final ObjectFeature objectFeature,
+            final ArrayList<ObjectMemento> container) {
+        return of(container, objectFeature.getElementType().getLogicalType(), objectFeature.getFeatureIdentifier());
+    }
+
     @Override
     public String getTitle() {
         throw _Exceptions.notImplemented(); // please unwrap at call-site
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoForScalar.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoSingular.java
similarity index 89%
rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoForScalar.java
rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoSingular.java
index 164d55b2f5..3e8eff86e7 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoForScalar.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectMementoSingular.java
@@ -40,23 +40,23 @@ import lombok.ToString;
 import lombok.val;
 
 @ToString
-public final class ObjectMementoForScalar
+public final class ObjectMementoSingular
 implements HasLogicalType, Serializable, ObjectMemento {
 
     private static final long serialVersionUID = 1L;
 
     // -- FACTORIES
 
-    public static Optional<ObjectMementoForScalar> create(final @Nullable ManagedObject adapter) {
+    public static Optional<ObjectMementoSingular> create(final @Nullable ManagedObject adapter) {
         return ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)
                 ? Optional.empty()
-                : Optional.of(new ObjectMementoForScalar(adapter));
+                : Optional.of(new ObjectMementoSingular(adapter));
     }
 
-    static ObjectMementoForScalar createPersistent(
+    static ObjectMementoSingular createPersistent(
             final Bookmark bookmark,
             final SpecificationLoader specificationLoader) {
-        return new ObjectMementoForScalar(bookmark, specificationLoader);
+        return new ObjectMementoSingular(bookmark, specificationLoader);
     }
 
     // --
@@ -71,7 +71,7 @@ implements HasLogicalType, Serializable, ObjectMemento {
     @ToString.Exclude
     byte[] serializedPayload;
 
-    private ObjectMementoForScalar(
+    private ObjectMementoSingular(
             final @NonNull Bookmark bookmark,
             final @NonNull SpecificationLoader specLoader) {
 
@@ -91,7 +91,7 @@ implements HasLogicalType, Serializable, ObjectMemento {
                 : _Recreatable.RecreateStrategy.LOOKUP;
     }
 
-    private ObjectMementoForScalar(final @NonNull ManagedObject adapter) {
+    private ObjectMementoSingular(final @NonNull ManagedObject adapter) {
 
         this.logicalType = adapter.getLogicalType();
         this.title = MmTitleUtil.titleOf(adapter);
@@ -152,10 +152,10 @@ implements HasLogicalType, Serializable, ObjectMemento {
 
     @Override
     public boolean equals(final Object other) {
-        if (!(other instanceof ObjectMementoForScalar)) {
+        if (!(other instanceof ObjectMementoSingular)) {
             return false;
         }
-        return recreateStrategy.equals(this, (ObjectMementoForScalar) other);
+        return recreateStrategy.equals(this, (ObjectMementoSingular) other);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_Recreatable.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_Recreatable.java
index e561fc58ec..c9752ffbc4 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_Recreatable.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_Recreatable.java
@@ -44,26 +44,26 @@ interface _Recreatable {
         private final _Recreatable delegate;
 
         @Override
-        public ManagedObject recreateObject(final ObjectMementoForScalar memento, final MetaModelContext mmc) {
+        public ManagedObject recreateObject(final ObjectMementoSingular memento, final MetaModelContext mmc) {
             return delegate.recreateObject(memento, mmc);
         }
 
         @Override
-        public boolean equals(final ObjectMementoForScalar memento, final ObjectMementoForScalar otherMemento) {
+        public boolean equals(final ObjectMementoSingular memento, final ObjectMementoSingular otherMemento) {
             return delegate.equals(memento, otherMemento);
         }
 
         @Override
-        public int hashCode(final ObjectMementoForScalar memento) {
+        public int hashCode(final ObjectMementoSingular memento) {
             return delegate.hashCode();
         }
 
     }
 
-    ManagedObject recreateObject(ObjectMementoForScalar memento, MetaModelContext mmc);
+    ManagedObject recreateObject(ObjectMementoSingular memento, MetaModelContext mmc);
 
-    boolean equals(ObjectMementoForScalar memento, ObjectMementoForScalar otherMemento);
+    boolean equals(ObjectMementoSingular memento, ObjectMementoSingular otherMemento);
 
-    int hashCode(ObjectMementoForScalar memento);
+    int hashCode(ObjectMementoSingular memento);
 
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableLookup.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableLookup.java
index 4451eabe1c..a9fdb47c4b 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableLookup.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableLookup.java
@@ -31,7 +31,7 @@ class _RecreatableLookup implements _Recreatable{
 
     @Override
     public @Nullable ManagedObject recreateObject(
-            final ObjectMementoForScalar memento,
+            final ObjectMementoSingular memento,
             final MetaModelContext mmc) {
 
         if(memento.bookmark==null) {
@@ -46,13 +46,13 @@ class _RecreatableLookup implements _Recreatable{
     }
 
     @Override
-    public boolean equals(final ObjectMementoForScalar oam, final ObjectMementoForScalar other) {
+    public boolean equals(final ObjectMementoSingular oam, final ObjectMementoSingular other) {
         return other.recreateStrategy == RecreateStrategy.LOOKUP
                 && oam.bookmark.equals(other.bookmark);
     }
 
     @Override
-    public int hashCode(final ObjectMementoForScalar oam) {
+    public int hashCode(final ObjectMementoSingular oam) {
         return oam.bookmark.hashCode();
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableValue.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableValue.java
index 3a67d5b191..689908e460 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableValue.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/_RecreatableValue.java
@@ -25,7 +25,7 @@ class _RecreatableValue implements _Recreatable{
 
     @Override
     public ManagedObject recreateObject(
-            final ObjectMementoForScalar memento,
+            final ObjectMementoSingular memento,
             final MetaModelContext mmc) {
 
         return mmc.getObjectManager().loadObjectElseFail(memento.bookmark);
@@ -33,15 +33,15 @@ class _RecreatableValue implements _Recreatable{
 
     @Override
     public boolean equals(
-            final ObjectMementoForScalar memento,
-            final ObjectMementoForScalar otherMemento) {
+            final ObjectMementoSingular memento,
+            final ObjectMementoSingular otherMemento) {
 
         return otherMemento.recreateStrategy == RecreateStrategy.VALUE
                 && memento.bookmark.equals(otherMemento.bookmark);
     }
 
     @Override
-    public int hashCode(final ObjectMementoForScalar memento) {
+    public int hashCode(final ObjectMementoSingular memento) {
         return memento.bookmark.hashCode();
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNamingPostProcessor.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNamingPostProcessor.java
index 7fdd61291c..d6a25b7729 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNamingPostProcessor.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNamingPostProcessor.java
@@ -65,19 +65,19 @@ extends ObjectSpecificationPostProcessorAbstract {
                 .filter(_Strings::isNotEmpty)
                 .orElseGet(()->getSingularFallbackNoun(objectSpecification));
 
-        val plural = topRank
-                .stream()
-                .filter(objectNamedFacet->objectNamedFacet.getSupportedNounForms().contains(NounForm.PLURAL))
-                .findFirst()
-                .map(ObjectNamedFacet::plural)
-                .filter(_Strings::isNotEmpty)
-                .orElseGet(()->getPluralFallbackNoun(singular));
+//        val plural = topRank
+//                .stream()
+//                .filter(objectNamedFacet->objectNamedFacet.getSupportedNounForms().contains(NounForm.PLURAL))
+//                .findFirst()
+//                .map(ObjectNamedFacet::plural)
+//                .filter(_Strings::isNotEmpty)
+//                .orElseGet(()->getPluralFallbackNoun(singular));
 
         FacetUtil.addFacet(
                 new ObjectNamedFacetSynthesized(
                         NounForms.builder()
                             .singular(singular)
-                            .plural(plural)
+//                            .plural(plural)
                             .build(),
                         objectSpecification)
                 );
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java
index 2e4d58dee0..eb61067059 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java
@@ -274,7 +274,7 @@ implements SchemaValueMarshaller, HasMetaModelContext {
 
         return cardinalityConstraint.isMultiple()
                 ? ManagedObject.packed(
-                        valueTypeHelper.getElementType(),
+                        valueTypeHelper.getFeature(),
                         recoverCollectionOfValues(valueTypeHelper, valueDto.getCollection()))
                 : recoverScalarValue(valueTypeHelper, valueDto);
     }
@@ -286,7 +286,7 @@ implements SchemaValueMarshaller, HasMetaModelContext {
 
         return cardinalityConstraint.isMultiple()
                 ? ManagedObject.packed(
-                        feature.getElementType(),
+                        feature,
                         recoverCollectionOfReferences(valueDto.getCollection()))
                 : recoverReferenceFrom(valueDto.getReference());
     }
@@ -343,7 +343,7 @@ implements SchemaValueMarshaller, HasMetaModelContext {
                 || (valueWithTypeDto.isNull()!=null
                     && valueWithTypeDto.isNull())) {
             return cardinalityConstraint.isMultiple()
-                    ? ManagedObject.packed(feature.getElementType(), Can.empty())
+                    ? ManagedObject.packed(feature, Can.empty())
                     : ManagedObject.empty(feature.getElementType());
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
index 8ebf243a7f..a344bd529a 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
@@ -27,9 +27,7 @@ import org.springframework.stereotype.Service;
 
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.services.title.TitleService;
-import org.apache.causeway.applib.services.wrapper.WrapperFactory;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
-import org.apache.causeway.core.metamodel.facets.object.title.TitleRenderRequest;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.object.MmEntityUtil;
 import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
@@ -44,7 +42,6 @@ import lombok.val;
 @RequiredArgsConstructor(onConstructor_ = @Inject)
 public class TitleServiceDefault implements TitleService {
 
-    private final WrapperFactory wrapperFactory;
     private final ObjectManager objectManager;
 
     @Override
@@ -54,8 +51,7 @@ public class TitleServiceDefault implements TitleService {
             return "" + domainObject;
         }
 
-        val pojo = unwrapped(domainObject);
-        val objectAdapter = objectManager.adapt(pojo);
+        val objectAdapter = objectManager.adapt(domainObject);
 
         if(ManagedObjects.isNullOrUnspecifiedOrEmpty(objectAdapter)) {
             return "[UNSPECIFIED]";
@@ -64,10 +60,7 @@ public class TitleServiceDefault implements TitleService {
         if(MmEntityUtil.isDetachedCannotReattach(objectAdapter)) {
             return "[DETACHED]";
         } else {
-            return objectAdapter.getSpecification().getTitle(
-                    TitleRenderRequest.builder()
-                    .object(objectAdapter)
-                    .build());
+            return objectAdapter.getTitle();
         }
     }
 
@@ -75,23 +68,16 @@ public class TitleServiceDefault implements TitleService {
     public String iconNameOf(final Object domainObject) {
 
         if(objectManager == null) { // simplified JUnit test support
-            return domainObject!=null ? domainObject.getClass().getSimpleName() : "null";
+            return domainObject!=null
+                    ? domainObject.getClass().getSimpleName()
+                    : "null";
         }
 
-        val pojo = unwrapped(domainObject);
-        val objectAdapter = objectManager.adapt(pojo);
+        val objectAdapter = objectManager.adapt(domainObject);
 
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(objectAdapter)) {
-            return "unspecified";
-        }
-
-        return objectAdapter.getSpecification().getIconName(objectAdapter);
-    }
-
-    //-- HELPER
-
-    private Object unwrapped(final Object domainObject) {
-        return wrapperFactory != null ? wrapperFactory.unwrap(domainObject) : domainObject;
+        return ManagedObjects.isNullOrUnspecifiedOrEmpty(objectAdapter)
+            ? "unspecified"
+            : objectAdapter.getIconName();
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
index 112499eaea..5c067853d4 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
@@ -207,14 +207,14 @@ extends
      */
     String getSingularName();
 
-    /**
-     * Returns the plural name for objects of this specification.
-     * Corresponds to the {@link HasNoun#translated(NounForm)}
-     * with {@link NounForm#PLURAL}
-     * of {@link ObjectNamedFacet}; is
-     * not necessarily immutable.
-     */
-    String getPluralName();
+//    /**
+//     * Returns the plural name for objects of this specification.
+//     * Corresponds to the {@link HasNoun#translated(NounForm)}
+//     * with {@link NounForm#PLURAL}
+//     * of {@link ObjectNamedFacet}; is
+//     * not necessarily immutable.
+//     */
+//    String getPluralName();
 
     /**
      * Returns the description, if any, of the specification.
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectFeature.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectFeature.java
index 1e0217e005..b65c3033f2 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectFeature.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectFeature.java
@@ -18,9 +18,31 @@
  */
 package org.apache.causeway.core.metamodel.spec.feature;
 
+import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+
 @FunctionalInterface
-public interface HasObjectFeature {
+public interface HasObjectFeature
+extends HasMetaModelContext {
 
     ObjectFeature getObjectFeature();
 
+    default ObjectSpecification getElementType() {
+        return getObjectFeature().getElementType();
+    }
+
+    @Override
+    default MetaModelContext getMetaModelContext() {
+        return getObjectFeature().getMetaModelContext();
+    }
+
+    /**
+     * @deprecated use {@link #getObjectFeature()} instead
+     */
+    @Deprecated(forRemoval = true)
+    default ObjectFeature getMetaModel() {
+        return getObjectFeature();
+    }
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
index a9f4a910ac..06fb5910d8 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
@@ -251,7 +251,7 @@ implements
             // post processing each entry
             .map(obj->ManagedObjects.emptyToDefault(paramSpec, !isOptional(), obj));
             // pack up
-            val packed = ManagedObject.packed(paramSpec, pluralDefaults);
+            val packed = ManagedObject.packed(this, pluralDefaults);
             return packed;
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index 67671d9d11..77aa670eea 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -512,16 +512,16 @@ implements ObjectSpecification {
                     getFullIdentifier()));
     }
 
-    @Override
-    public String getPluralName() {
-        return lookupFacet(ObjectNamedFacet.class)
-            .flatMap(textFacet->textFacet.translated(NounForm.PLURAL))
-            // unexpected code reach, however keep for JUnit testing
-            .orElseGet(()->String.format(
-                    "(%s has neither title- nor object-named-facet)",
-                    getFullIdentifier()));
-
-    }
+//    @Override
+//    public String getPluralName() {
+//        return lookupFacet(ObjectNamedFacet.class)
+//            .flatMap(textFacet->textFacet.translated(NounForm.PLURAL))
+//            // unexpected code reach, however keep for JUnit testing
+//            .orElseGet(()->String.format(
+//                    "(%s has neither title- nor object-named-facet)",
+//                    getFullIdentifier()));
+//
+//    }
 
     /**
      * The translated description according to any available {@link ObjectDescribedFacet},
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToManyAssociationDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToManyAssociationDefault.java
index afa7e21d7d..194c3b30f2 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToManyAssociationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToManyAssociationDefault.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.core.metamodel.specloader.specimpl;
 
+import java.util.Optional;
+
 import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.applib.annotation.Collection;
 import org.apache.causeway.applib.annotation.CollectionLayout;
@@ -110,7 +112,7 @@ implements OneToManyAssociation {
 
         super.getServiceInjector().injectServicesInto(collection);
 
-        return objectManager.adapt(collection);
+        return objectManager.adapt(collection, Optional.of(this));
     }
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
index ca7a5eac88..eeff67e771 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
@@ -292,7 +292,7 @@ implements OneToOneAssociation {
 
         final PropertyAutoCompleteFacet propertyAutoCompleteFacet = getFacet(PropertyAutoCompleteFacet.class);
         final Object[] pojoOptions = propertyAutoCompleteFacet
-                .autoComplete(ownerAdapter, searchArg, interactionInitiatedBy);
+                .autoComplete(this, ownerAdapter, searchArg, interactionInitiatedBy);
 
         val adapters = _NullSafe.stream(pojoOptions)
                 .map(getObjectManager()::adapt)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSchema.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSchema.java
index 50ae80e758..590cb79308 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSchema.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSchema.java
@@ -148,11 +148,13 @@ public final class XmlSchema {
      * doc used to create the element. The element is not parented but to avoid
      * an error can only be added as a child of another element in the same doc.
      */
-    Element createElement(final Document doc, final String localName, final String fullyQualifiedClassName, final String singularName, final String pluralName) {
+    Element createElement(final Document doc, final String localName, final String fullyQualifiedClassName, final String singularName
+            //, final String pluralName
+            ) {
         final Element element = doc.createElementNS(getUri(), getPrefix() + ":" + localName);
         element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":fqn", fullyQualifiedClassName);
         element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":singular", singularName);
-        element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":plural", pluralName);
+        //element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":plural", pluralName);
         causewayMeta.addNamespace(element); // good a place as any
 
         addNamespace(element, getPrefix(), getUri());
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSnapshot.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSnapshot.java
index 59dee4c6ed..63bf8fee81 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSnapshot.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/snapshot/XmlSnapshot.java
@@ -634,7 +634,9 @@ public class XmlSnapshot implements Snapshot {
             log.debug("objectToElement(NO): create element and causeway:title");
         }
         final Element element = schema.createElement(getXmlDocument(), spec.getShortIdentifier(),
-                spec.getFullIdentifier(), spec.getSingularName(), spec.getPluralName());
+                spec.getFullIdentifier(), spec.getSingularName()
+                //, spec.getPluralName()
+                );
         causewayMetaModel.appendCausewayTitle(element, adapter.getTitle());
 
         if (log.isDebugEnabled()) {
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
index ecd9508260..c0c4420b6a 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
@@ -78,8 +78,8 @@ extends AbstractFacetFactoryJupiterTestCase {
             cssClassFaPosition = CssClassFaPosition.RIGHT,
             describedAs = "This is a description",
             named = "Name override",
-            paged = 20,
-            plural = "Customers Plural Form"
+            paged = 20
+            //tableDecoration = TableDecoration.DATATABLES_NET
             )
     class Customer { }
 
@@ -340,12 +340,14 @@ extends AbstractFacetFactoryJupiterTestCase {
                 facetFactory.process(ProcessClassContext
                         .forTesting(cls, mockMethodRemover, facetHolder));
 
-                final Facet facet = facetHolder.getFacet(PagedFacet.class);
+                final PagedFacet facet = facetHolder.getFacet(PagedFacet.class);
                 assertNotNull(facet);
-                assertTrue(facet instanceof PagedFacetForDomainObjectLayoutAnnotation);
+                assertEquals(PagedFacetForDomainObjectLayoutAnnotation.class, facet.getClass());
+
+                //PagedFacetOverriddenByDataTablesDecoration
 
                 final PagedFacetForDomainObjectLayoutAnnotation facetImpl = (PagedFacetForDomainObjectLayoutAnnotation) facet;
-                assertThat(facetImpl.value(), is(20));
+                assertThat(facet.value(), is(20));
 
                 expectNoMethodsRemoved();
             }
@@ -367,49 +369,49 @@ extends AbstractFacetFactoryJupiterTestCase {
 
     }
 
-    public static class Plural extends DomainObjectLayoutFactoryTest {
-
-        @Mock ManagedObject mockAdapter;
-
-        public static class ForDomainObjectLayout extends Plural {
-
-            @BeforeEach
-            public void setUp2() throws Exception {
-            }
-
-            @Test
-            public void whenSpecified() {
-
-                final Class<?> cls = DomainObjectLayoutFactoryTest.Customer.class;
-
-                facetFactory.process(ProcessClassContext
-                        .forTesting(cls, mockMethodRemover, facetHolder));
-
-                val namedFacet = facetHolder.getFacet(ObjectNamedFacet.class);
-                assertNotNull(namedFacet);
-
-                assertEquals("Customers Plural Form", namedFacet.pluralTranslated());
-
-                expectNoMethodsRemoved();
-            }
-
-            @Test
-            public void whenDefaults() {
-
-                final Class<?> cls = CustomerWithDefaults.class;
-
-                facetFactory.process(ProcessClassContext
-                        .forTesting(cls, mockMethodRemover, facetHolder));
-
-                val namedFacet = facetHolder.getFacet(ObjectNamedFacet.class);
-                assertNull(namedFacet);
-
-                //assertEquals("", namedFacet.translated(NounForm.PLURAL));
-
-                expectNoMethodsRemoved();
-            }
-        }
-
-    }
+//    public static class Plural extends DomainObjectLayoutFactoryTest {
+//
+//        @Mock ManagedObject mockAdapter;
+//
+//        public static class ForDomainObjectLayout extends Plural {
+//
+//            @BeforeEach
+//            public void setUp2() throws Exception {
+//            }
+//
+//            @Test
+//            public void whenSpecified() {
+//
+//                final Class<?> cls = DomainObjectLayoutFactoryTest.Customer.class;
+//
+//                facetFactory.process(ProcessClassContext
+//                        .forTesting(cls, mockMethodRemover, facetHolder));
+//
+//                val namedFacet = facetHolder.getFacet(ObjectNamedFacet.class);
+//                assertNotNull(namedFacet);
+//
+//                assertEquals("Customers Plural Form", namedFacet.pluralTranslated());
+//
+//                expectNoMethodsRemoved();
+//            }
+//
+//            @Test
+//            public void whenDefaults() {
+//
+//                final Class<?> cls = CustomerWithDefaults.class;
+//
+//                facetFactory.process(ProcessClassContext
+//                        .forTesting(cls, mockMethodRemover, facetHolder));
+//
+//                val namedFacet = facetHolder.getFacet(ObjectNamedFacet.class);
+//                assertNull(namedFacet);
+//
+//                //assertEquals("", namedFacet.translated(NounForm.PLURAL));
+//
+//                expectNoMethodsRemoved();
+//            }
+//        }
+//
+//    }
 
 }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
index a897da549d..79a6bf7668 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
@@ -36,7 +36,7 @@ class TitleServiceDefaultTest {
 
         val mmc = MetaModelContext_forTesting.buildDefault();
 
-        titleService = new TitleServiceDefault(null, mmc.getObjectManager());
+        titleService = new TitleServiceDefault(mmc.getObjectManager());
     }
 
     // -- FEATURED
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
index b200a05bbb..76636a9a53 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
@@ -161,8 +161,7 @@ implements MemberExecutorService {
         val priorExecution = interaction.getPriorExecutionOrThrowIfAnyException(actionInvocation);
 
         val returnedPojo = priorExecution.getReturned();
-        val returnedAdapter = objectManager.adapt(
-                returnedPojo, owningAction::getElementType);
+        val returnedAdapter = objectManager.adapt(returnedPojo, Optional.of(owningAction));
 
         // assert has bookmark, unless non-scalar
         ManagedObjects.asScalarNonEmpty(returnedAdapter)
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
index 0133daca74..1f6b96db1d 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
@@ -94,6 +94,7 @@ import org.apache.causeway.core.metamodel.services.command.CommandDtoFactory;
 import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
 import org.apache.causeway.core.metamodel.spec.feature.MixedInMember;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices;
 import org.apache.causeway.core.runtimeservices.wrapper.dispatchers.InteractionEventDispatcher;
@@ -371,7 +372,7 @@ implements WrapperFactory, HasMetaModelContext {
         val targetAdapter = memberAndTarget.getTarget();
         val method = memberAndTarget.getMethod();
 
-        val argAdapters = Can.ofArray(WrapperFactoryDefault.this.adaptersFor(args));
+        val argAdapters = Can.ofArray(WrapperFactoryDefault.this.adaptersFor(memberAndTarget.getObjectFeature(), args));
         val head = InteractionHead.regular(targetAdapter);
 
         CommandDto commandDto;
@@ -484,6 +485,17 @@ implements WrapperFactory, HasMetaModelContext {
             return type != Type.NONE;
         }
 
+        public Optional<ObjectFeature> getObjectFeature() {
+            switch (getType()) {
+            case ACTION:
+                return Optional.of(getAction());
+            case PROPERTY:
+                return Optional.of(getProperty());
+            default:
+                return Optional.empty();
+            }
+        }
+
         enum Type {
             ACTION,
             PROPERTY,
@@ -500,11 +512,12 @@ implements WrapperFactory, HasMetaModelContext {
         private final OneToOneAssociation property;
         private final ManagedObject target;
         private final Method method;
+
     }
 
-    private ManagedObject[] adaptersFor(final Object[] args) {
+    private ManagedObject[] adaptersFor(final Optional<ObjectFeature> objectFeature, final Object[] args) {
         return _NullSafe.stream(args)
-                .map(getObjectManager()::adapt)
+                .map(argPojo->getObjectManager().adapt(argPojo, objectFeature))
                 .collect(_Arrays.toArray(ManagedObject.class, _NullSafe.size(args)));
     }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
index cee0b67f28..748f4f9056 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
@@ -21,6 +21,7 @@ package org.apache.causeway.core.runtimeservices.wrapper.handlers;
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
@@ -469,12 +470,13 @@ extends DelegatingInvocationHandlerDefault<T> {
         val objectManager = getObjectManager();
 
         // adapt argument pojos to managed objects
-        val argAdapters = objectAction.getParameterTypes().map(IndexedFunction.zeroBased((paramIndex, paramSpec)->{
+
+        val argAdapters = objectAction.getParameters().map(IndexedFunction.zeroBased((paramIndex, param)->{
             // guard against index out of bounds
             val argPojo = _Arrays.get(args, paramIndex).orElse(null);
             return argPojo!=null
-                    ? objectManager.adapt(argPojo)
-                    : ManagedObject.empty(paramSpec);
+                    ? objectManager.adapt(argPojo, Optional.of(param))
+                    : ManagedObject.empty(param.getElementType());
         }));
 
         runValidationTask(()->{
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObjectLayout/plural/DomainObjectLayoutPluralVm.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObjectLayout/plural/DomainObjectLayoutPluralVm.java
index 8a7861fb68..86d6e4113d 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObjectLayout/plural/DomainObjectLayoutPluralVm.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObjectLayout/plural/DomainObjectLayoutPluralVm.java
@@ -49,8 +49,8 @@ import lombok.Setter;
 @DomainObject(
         nature=Nature.VIEW_MODEL)
 @DomainObjectLayout(
-        tableDecoration = TableDecoration.DATATABLES_NET,
-        plural="Example Plural Name")
+        tableDecoration = TableDecoration.DATATABLES_NET)
+//TODO rename class
 public class DomainObjectLayoutPluralVm implements HasAsciiDocDescription {
 
     @ObjectSupport public String title() {
diff --git a/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanel.java b/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanel.java
index 7d268178f9..77c5cc2cad 100644
--- a/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanel.java
+++ b/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanel.java
@@ -186,7 +186,7 @@ implements IRequestListener {
         val regularFrame = new WebMarkupContainer(ID_SCALAR_IF_REGULAR);
 
         val pdfJsConfig =
-                scalarModel.getMetaModel().lookupFacet(PdfJsViewerFacet.class)
+                scalarModel.getObjectFeature().lookupFacet(PdfJsViewerFacet.class)
                 .map(pdfJsViewerFacet->pdfJsViewerFacet.configFor(buildKey()))
                 .orElseGet(PdfJsConfig::new)
                 .withDocumentUrl(urlFor(
diff --git a/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanelComponentFactory.java b/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanelComponentFactory.java
index 6f397ba0f4..ee41974cd8 100644
--- a/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanelComponentFactory.java
+++ b/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/PdfJsViewerPanelComponentFactory.java
@@ -50,7 +50,7 @@ public class PdfJsViewerPanelComponentFactory extends ComponentFactoryAbstract {
         }
 
         val scalarModel = (ScalarModel) model;
-        if(!scalarModel.getMetaModel().containsFacet(PdfJsViewerFacet.class)) {
+        if(!scalarModel.getObjectFeature().containsFacet(PdfJsViewerFacet.class)) {
             return ApplicationAdvice.DOES_NOT_APPLY;
         }
 
diff --git a/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/ScalarPanelAbstractLegacy.java b/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/ScalarPanelAbstractLegacy.java
index 30f01fc8af..a07d7273ec 100644
--- a/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/ScalarPanelAbstractLegacy.java
+++ b/extensions/vw/pdfjs/wicket/ui/src/main/java/org/apache/causeway/extensions/pdfjs/wkt/ui/components/ScalarPanelAbstractLegacy.java
@@ -80,7 +80,7 @@ extends PanelAbstract<ManagedObject, ScalarModel> {
 
         Wkt.cssAppend(this, scalarModel.getCssClass());
 
-        Facets.cssClass(scalarModel.getMetaModel(), scalarModel.getParentUiModel().getManagedObject())
+        Facets.cssClass(scalarModel.getObjectFeature(), scalarModel.getParentUiModel().getManagedObject())
         .ifPresent(cssClass->
             Wkt.cssAppend(this, cssClass));
     }
diff --git a/extensions/vw/sse/wicket/src/main/java/org/apache/causeway/extensions/sse/wicket/markup/ListeningMarkupPanelFactoriesForWicket.java b/extensions/vw/sse/wicket/src/main/java/org/apache/causeway/extensions/sse/wicket/markup/ListeningMarkupPanelFactoriesForWicket.java
index 49d981db53..5d42edf9ec 100644
--- a/extensions/vw/sse/wicket/src/main/java/org/apache/causeway/extensions/sse/wicket/markup/ListeningMarkupPanelFactoriesForWicket.java
+++ b/extensions/vw/sse/wicket/src/main/java/org/apache/causeway/extensions/sse/wicket/markup/ListeningMarkupPanelFactoriesForWicket.java
@@ -60,7 +60,7 @@ public class ListeningMarkupPanelFactoriesForWicket {
         // -- HELPER
 
         private LocalResourcePath getEventStreamResource(final ScalarModel scalarModel) {
-            val observeFacet  = scalarModel.getMetaModel().getFacet(SseObserveFacet.class);
+            val observeFacet  = scalarModel.getObjectFeature().getFacet(SseObserveFacet.class);
             return observeFacet!=null
                     ? observeFacet.getEventStreamResource()
                     : null;
diff --git a/incubator/viewers/javafx/model/src/main/java/org/apache/causeway/incubator/viewer/javafx/model/action/ActionUiModelFx.java b/incubator/viewers/javafx/model/src/main/java/org/apache/causeway/incubator/viewer/javafx/model/action/ActionUiModelFx.java
index 493c542b94..27b10bf626 100644
--- a/incubator/viewers/javafx/model/src/main/java/org/apache/causeway/incubator/viewer/javafx/model/action/ActionUiModelFx.java
+++ b/incubator/viewers/javafx/model/src/main/java/org/apache/causeway/incubator/viewer/javafx/model/action/ActionUiModelFx.java
@@ -41,7 +41,7 @@ implements UiAction<MenuItem, Node> {
 
     @Override
     public ObjectAction getAction() {
-        return managedAction.getMetaModel();
+        return managedAction.getObjectFeature();
     }
 
     @Override
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/causeway/incubator/viewer/javafx/ui/components/UiComponentFactoryFx.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/causeway/incubator/viewer/javafx/ui/components/UiComponentFactoryFx.java
index f150dc4af6..433d6602b1 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/causeway/incubator/viewer/javafx/ui/components/UiComponentFactoryFx.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/causeway/incubator/viewer/javafx/ui/components/UiComponentFactoryFx.java
@@ -111,7 +111,7 @@ public class UiComponentFactoryFx implements UiComponentFactory<Node, Node> {
 
     @Override
     public LabelAndPosition<Node> labelFor(final ComponentRequest request) {
-        val labelPosition = Facets.labelAt(request.getManagedFeature().getMetaModel())
+        val labelPosition = Facets.labelAt(request.getManagedFeature().getObjectFeature())
                 .orElse(LabelPosition.LEFT);
         val uiLabel = new Label(request.getFriendlyName());
         return LabelAndPosition.of(labelPosition, uiLabel);
diff --git a/incubator/viewers/vaadin/model/src/main/java/org/apache/causeway/incubator/viewer/vaadin/model/action/ActionUiModelVaa.java b/incubator/viewers/vaadin/model/src/main/java/org/apache/causeway/incubator/viewer/vaadin/model/action/ActionUiModelVaa.java
index 848fc73a00..39a2d09770 100644
--- a/incubator/viewers/vaadin/model/src/main/java/org/apache/causeway/incubator/viewer/vaadin/model/action/ActionUiModelVaa.java
+++ b/incubator/viewers/vaadin/model/src/main/java/org/apache/causeway/incubator/viewer/vaadin/model/action/ActionUiModelVaa.java
@@ -38,7 +38,7 @@ public class ActionUiModelVaa implements UiAction<Component, Component> {
 
     @Override
     public ObjectAction getAction() {
-        return managedAction.getMetaModel();
+        return managedAction.getObjectFeature();
     }
 
     @Override
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
index e73876a0d8..72282eedd2 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
@@ -815,11 +815,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperActionWithNameStartingWithSetOrGet#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Action With Name Starting With Set Or Gets]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Action With Name Starting With Set Or Get]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Action With Name Starting With Set Or Gets]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Action With Name Starting With Set Or Get]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -1746,11 +1744,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperChoicesWhenActionHasParamSupportingMethodTypeOfReference#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of References]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of Reference]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of References]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of Reference]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -2820,11 +2816,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperChoicesWhenActionHasParamSupportingMethodTypeOfString#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of Strings]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of String]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of Strings]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Choices When Action Has Param Supporting Method Type Of String]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -3906,11 +3900,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperChoicesWhenChoicesFrom#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Choices When Choices Froms]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Choices When Choices From]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Choices When Choices Froms]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Choices When Choices From]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -4796,11 +4788,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperElementTypeVm#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Element Type Vms]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Element Type Vm]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Element Type Vms]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Element Type Vm]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -6117,11 +6107,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperFullyImpl#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Fully Impls]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Fully Impl]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Fully Impls]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Fully Impl]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.cssclass.method.CssClassFacetViaCssClassMethod">
@@ -7100,11 +7088,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperGenericImpl#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Generic Impls]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Generic Impl]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Generic Impls]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Generic Impl]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -7888,11 +7874,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperLayoutOnPropertyWithLombok#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Layout On Property With Lomboks]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Layout On Property With Lombok]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Layout On Property With Lomboks]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Layout On Property With Lombok]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -8700,11 +8684,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberInheritance_usingAbstract#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Member Inheritance_using Abstracts]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Member Inheritance_using Abstract]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Member Inheritance_using Abstracts]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Member Inheritance_using Abstract]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.cssclass.method.CssClassFacetViaCssClassMethod">
@@ -9855,11 +9837,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberInheritance_usingInterface#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Member Inheritance_using Interfaces]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Member Inheritance_using Interface]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Member Inheritance_using Interfaces]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Member Inheritance_using Interface]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -11103,11 +11083,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberOverloadingWhenInherited.WhenAnnotationOptional#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[When Annotation Optionals]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[When Annotation Optional]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[When Annotation Optionals]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[When Annotation Optional]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -11887,11 +11865,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberOverloadingWhenInherited.WhenAnnotationRequired#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[When Annotation Requireds]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[When Annotation Required]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[When Annotation Requireds]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[When Annotation Required]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -12675,11 +12651,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberOverloadingWhenInherited.WhenEncapsulationEnabled#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[When Encapsulation Enableds]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[When Encapsulation Enabled]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[When Encapsulation Enableds]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[When Encapsulation Enabled]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -13463,11 +13437,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberSupport#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Member Supports]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Member Support]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Member Supports]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Member Support]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -16665,11 +16637,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberSupportDiscovery.WhenAnnotationOptional#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[When Annotation Optionals]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[When Annotation Optional]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[When Annotation Optionals]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[When Annotation Optional]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -17803,11 +17773,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberSupportDiscovery.WhenAnnotationRequired#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[When Annotation Requireds]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[When Annotation Required]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[When Annotation Requireds]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[When Annotation Required]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -18945,11 +18913,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperMemberSupportDiscovery.WhenEncapsulationEnabled#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[When Encapsulation Enableds]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[When Encapsulation Enabled]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[When Encapsulation Enableds]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[When Encapsulation Enabled]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -23062,11 +23028,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperParameterSupport#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Parameter Supports]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Parameter Support]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Parameter Supports]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Parameter Support]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -24047,11 +24011,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperPrefixedAction#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Prefixed Actions]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Prefixed Action]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Prefixed Actions]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Prefixed Action]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -24830,11 +24792,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ProperPrefixedMember#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[Proper Prefixed Members]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[Proper Prefixed Member]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[Proper Prefixed Members]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[Proper Prefixed Member]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -25859,11 +25819,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ViewModelWithAnnotationOptionalUsingPrivateSupport#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[View Model With Annotation Optional Using Private Supports]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[View Model With Annotation Optional Using Private Support]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[View Model With Annotation Optional Using Private Supports]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[View Model With Annotation Optional Using Private Support]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
@@ -26851,11 +26809,9 @@
             <mml:facet id="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet" fqcn="org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized">
                 <mml:attr name="context" value="TranslationContext(name=org.apache.causeway.testdomain.model.good.ViewModelWithEncapsulatedMembers#)"/>
                 <mml:attr name="facet" value="ObjectNamedFacetSynthesized"/>
-                <mml:attr name="nounForms" value="SINGULAR, PLURAL"/>
-                <mml:attr name="originalText.PLURAL" value="Optional[View Model With Encapsulated Memberses]"/>
+                <mml:attr name="nounForms" value="SINGULAR"/>
                 <mml:attr name="originalText.SINGULAR" value="Optional[View Model With Encapsulated Members]"/>
                 <mml:attr name="precedence" value="SYNTHESIZED"/>
-                <mml:attr name="translated.PLURAL" value="Optional[View Model With Encapsulated Memberses]"/>
                 <mml:attr name="translated.SINGULAR" value="Optional[View Model With Encapsulated Members]"/>
             </mml:facet>
             <mml:facet id="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet" fqcn="org.apache.causeway.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacetForDomainObjectAnnotation">
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/ActionInteractionTest.java b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/ActionInteractionTest.java
index ad78f23a87..2999d3f37b 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/ActionInteractionTest.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/ActionInteractionTest.java
@@ -459,7 +459,7 @@ class ActionInteractionTest extends InteractionTestAbstract {
     }
 
     @Test
-    void whenNonScalarResult_shouldHaveDataTable() {
+    void whenPluralResult_shouldHaveDataTable() {
 
         val tester =
                 testerFactory.actionTester(InteractionDemo.class, "limitedItems", Where.OBJECT_FORMS);
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
index b81b0f34da..9fb5a1d424 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
@@ -106,7 +106,7 @@ class CommandArgumentTest extends InteractionTestAbstract {
 
         val pendingArgs = actionInteraction.startParameterNegotiation().get();
 
-        pendingArgs.setParamValue(0, objectManager.adapt(Arrays.asList(1L, 2L, 3L)));
+        pendingArgs.setParamValuePojo(0, Arrays.asList(1L, 2L, 3L));
 
         val resultOrVeto = actionInteraction.invokeWith(pendingArgs);
         assertTrue(resultOrVeto.isSuccess());
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/PropertyBindingTest.java b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/PropertyBindingTest.java
index 6f1bf0c0f2..6a684b13c6 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/PropertyBindingTest.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/PropertyBindingTest.java
@@ -39,10 +39,10 @@ import org.apache.causeway.testdomain.util.interaction.InteractionTestAbstract;
 import lombok.val;
 
 @SpringBootTest(
-        classes = { 
+        classes = {
                 Configuration_headless.class,
                 Configuration_usingInteractionDomain.class
-        }, 
+        },
         properties = {
                 "causeway.core.meta-model.introspector.mode=FULL",
                 "causeway.applib.annotation.domain-object.editing=TRUE",
@@ -56,67 +56,67 @@ class PropertyBindingTest extends InteractionTestAbstract {
 
     PropertyNegotiationModel proposalA;
     PropertyNegotiationModel proposalB;
-    
+
     SimulatedUiComponent uiPropStringMultiline;
-    
-    // UI components for the new value negotiation dialog 
+
+    // UI components for the new value negotiation dialog
     SimulatedUiComponent uiPropA;
     SimulatedUiChoices uiPropAChoices;
     SimulatedUiAutoComplete uiPropBAutoComplete;
-    
+
     @BeforeEach
     void setUpSimulatedUi() {
 
-        val propertyInteractionA = startPropertyInteractionOn(InteractionDemo.class, "stringMultiline", Where.OBJECT_FORMS);  
+        val propertyInteractionA = startPropertyInteractionOn(InteractionDemo.class, "stringMultiline", Where.OBJECT_FORMS);
         assertTrue(propertyInteractionA.getManagedProperty().isPresent(), "prop is expected to be editable");
-        
-        val propertyInteractionB = startPropertyInteractionOn(InteractionDemo.class, "string2", Where.OBJECT_FORMS);  
+
+        val propertyInteractionB = startPropertyInteractionOn(InteractionDemo.class, "string2", Where.OBJECT_FORMS);
         assertTrue(propertyInteractionB.getManagedProperty().isPresent(), "prop is expected to be editable");
-        
+
         val managedPropA = propertyInteractionA.getManagedProperty().get();
         this.proposalA = managedPropA.startNegotiation();
-        
+
         val managedPropB = propertyInteractionB.getManagedProperty().get();
         this.proposalB = managedPropB.startNegotiation();
-        
+
         // setting up and binding all the simulated UI components
-        
+
         uiPropStringMultiline = new SimulatedUiComponent();
         uiPropA = new SimulatedUiComponent();
         uiPropAChoices = new SimulatedUiChoices();
         uiPropBAutoComplete = new SimulatedUiAutoComplete();
-        
+
         uiPropStringMultiline.bind(managedPropA);
-        
+
         uiPropA.bind(proposalA);
         uiPropAChoices.bind(proposalA);
         uiPropBAutoComplete.bind(proposalB);
-        
+
         // verify that initial defaults are as expected
-        
+
         assertEquals("initial", uiPropStringMultiline.getValue().getPojo());
         assertEquals("initial", uiPropA.getValue().getPojo());
-        
+
         // verify that initial choices are as expected
-        
-        assertTrue(managedPropA.getMetaModel().hasChoices());
+
+        assertTrue(managedPropA.getObjectFeature().hasChoices());
         assertComponentWiseUnwrappedEquals(new String[] {"Hello", "World"}, uiPropAChoices.getChoices());
-        
-        assertTrue(managedPropB.getMetaModel().hasAutoComplete());
+
+        assertTrue(managedPropB.getObjectFeature().hasAutoComplete());
         assertComponentWiseUnwrappedEquals(new String[] {}, uiPropBAutoComplete.getChoices());
-        
-        // verify that initial validation messages are all empty, 
-        // because we don't validate anything until a user initiated submit attempt occurs 
-        
+
+        // verify that initial validation messages are all empty,
+        // because we don't validate anything until a user initiated submit attempt occurs
+
         assertEmpty(uiPropA.getValidationMessage());
-        
+
         // verify that validation feedback is not active
-        
+
         assertFalse(proposalA.getObservableValidationFeedbackActive().getValue());
         assertFalse(proposalB.getObservableValidationFeedbackActive().getValue());
-        
+
     }
-    
+
     @Test
     void propA_whenChanging_shouldPropagteNewValue() {
 
@@ -127,23 +127,23 @@ class PropertyBindingTest extends InteractionTestAbstract {
         proposalA.submit();
         assertEquals("Hi", uiPropStringMultiline.getValue().getPojo());
     }
-    
+
     @Test
     void propB_whenSettingSearchArgument_shouldProvideChoices() {
-        
+
         // verify that changing the search argument fires change event
         assertDoesIncrement(
                 uiPropBAutoComplete::getChoiceBoxUpdateEventCount,
                 ()->uiPropBAutoComplete.setSimulatedSearchArgument("H")); // select "Hello"
-                        
-        
+
+
         // verify that no additional changes are triggered
-        assertDoesNotIncrement(        
+        assertDoesNotIncrement(
                 uiPropBAutoComplete::getChoiceBoxUpdateEventCount,
                 ()->assertComponentWiseUnwrappedEquals(new String[] {"Hello"}, uiPropBAutoComplete.getChoices()));
-        
+
         // TODO such a change might set or clear propA validation message once validation feedback is active
     }
 
-    
+
 }
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/SimulatedUiChoices.java b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/SimulatedUiChoices.java
index 4c6b8afa30..11909be97a 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/SimulatedUiChoices.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/causeway/testdomain/interact/SimulatedUiChoices.java
@@ -26,7 +26,7 @@ import org.apache.causeway.commons.internal.binding._Bindables;
 import org.apache.causeway.core.metamodel.interactions.managed.ManagedValue;
 import org.apache.causeway.core.metamodel.interactions.managed.ParameterNegotiationModel;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
-import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.Getter;
 import lombok.val;
@@ -39,7 +39,7 @@ public class SimulatedUiChoices extends HasValueValidation {
     @Getter private final LongAdder choiceBoxUpdateEventCount = new LongAdder();
     @Getter private final LongAdder selectedItemUpdateEventCount = new LongAdder();
 
-    private ObjectSpecification valueSpecification;
+    private ObjectFeature objectFeature;
 
     @Override
     public void bind(final ManagedValue managedValue) {
@@ -54,7 +54,7 @@ public class SimulatedUiChoices extends HasValueValidation {
             selectedItemUpdateEventCount.increment();
         });
 
-        valueSpecification = managedValue.getElementType();
+        objectFeature = managedValue.getObjectFeature();
     }
 
     public void bind(final ParameterNegotiationModel pendingArgs, final int paramNr) {
@@ -62,7 +62,7 @@ public class SimulatedUiChoices extends HasValueValidation {
     }
 
     /**
-     * assuming the parameter is a scalar type
+     * assuming the parameter is a singular type
      * @param choiceIndex
      */
     public void simulateChoiceSelect(final int choiceIndex) {
@@ -70,13 +70,13 @@ public class SimulatedUiChoices extends HasValueValidation {
     }
 
     /**
-     * assuming the parameter is a non-scalar type
+     * assuming the parameter is a plural type
      * @param choiceIndices
      */
     public void simulateMultiChoiceSelect(final int ... choiceIndices) {
         val newValues = choiceBox.getValue()
                 .pickByIndex(choiceIndices);
-        selectedItem.setValue(ManagedObject.packed(valueSpecification, newValues));
+        selectedItem.setValue(ManagedObject.packed(objectFeature, newValues));
     }
 
     public ManagedObject getValue() {
diff --git a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
index 9c6da0f4f8..7166535639 100644
--- a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
+++ b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
@@ -31,11 +31,13 @@ import org.junit.jupiter.api.function.ThrowingSupplier;
 import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Service;
 
+import static org.hamcrest.CoreMatchers.either;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.when;
 
 import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.applib.annotation.Where;
@@ -281,7 +283,7 @@ public class DomainObjectTesterFactory {
         @Override
         public Optional<ObjectAction> getMetaModel() {
             return getManagedAction()
-            .map(ManagedAction::getMetaModel)
+            .map(ManagedAction::getObjectFeature)
             .map(ObjectAction.class::cast);
         }
 
@@ -539,7 +541,7 @@ public class DomainObjectTesterFactory {
                     pendingArgs.getParamModels()
                     .forEach(param->{
 
-                        val objManager = param.getMetaModel().getObjectManager();
+                        val objManager = param.getObjectFeature().getObjectManager();
 
                         pojoArgMappers
                             .get(param.getParamNr())
@@ -683,7 +685,7 @@ public class DomainObjectTesterFactory {
         @Override
         public Optional<OneToOneAssociation> getMetaModel() {
             return managedPropertyIfAny
-            .map(ManagedProperty::getMetaModel)
+            .map(ManagedProperty::getObjectFeature)
             .map(OneToOneAssociation.class::cast);
         }
 
@@ -730,7 +732,7 @@ public class DomainObjectTesterFactory {
             .ifPresent(managedProperty->{
                 interactionService.runAnonymous(()->{
 
-                    val newPropertyValue = managedProperty.getMetaModel().getMetaModelContext()
+                    val newPropertyValue = managedProperty.getObjectFeature().getMetaModelContext()
                           .getObjectManager().adapt(proposedNewPropertyValue);
 
                     managedProperty.modifyProperty(newPropertyValue);
@@ -753,7 +755,7 @@ public class DomainObjectTesterFactory {
 
                     assertEquals(initialValue, propNeg.getValue().getValue());
 
-                    val newPropertyValue = managedProperty.getMetaModel().getMetaModelContext()
+                    val newPropertyValue = managedProperty.getObjectFeature().getMetaModelContext()
                             .getObjectManager().adapt(proposedNewPropertyValue);
                     propNeg.getValue().setValue(newPropertyValue);
 
@@ -860,7 +862,7 @@ public class DomainObjectTesterFactory {
         @Override
         public Optional<OneToManyAssociation> getMetaModel() {
             return managedCollectionIfAny
-            .map(ManagedCollection::getMetaModel)
+            .map(ManagedCollection::getObjectFeature)
             .map(OneToManyAssociation.class::cast);
         }
 
diff --git a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiParameter.java b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiParameter.java
index 0d373bf795..26ef1d7fd9 100644
--- a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiParameter.java
+++ b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiParameter.java
@@ -38,8 +38,8 @@ public interface HasUiParameter extends UiParameter {
     UiParameter getUiParameter();
 
     @Override
-    default ObjectActionParameter getMetaModel() {
-        return getUiParameter().getMetaModel();
+    default ObjectActionParameter getObjectFeature() {
+        return getUiParameter().getObjectFeature();
     }
 
     @Override
@@ -64,57 +64,57 @@ public interface HasUiParameter extends UiParameter {
 
     @Override
     default int getAutoCompleteMinLength() {
-        return hasAutoComplete() ? getMetaModel().getAutoCompleteMinLength() : 0;
+        return hasAutoComplete() ? getObjectFeature().getAutoCompleteMinLength() : 0;
     }
 
     @Override
     default boolean hasChoices() {
-        return getMetaModel().hasChoices();
+        return getObjectFeature().hasChoices();
     }
 
     @Override
     default boolean hasAutoComplete() {
-        return getMetaModel().hasAutoComplete();
+        return getObjectFeature().hasAutoComplete();
     }
 
     @Override
     default ManagedObject getDefault() {
-        return getMetaModel().getDefault(getParameterNegotiationModel());
+        return getObjectFeature().getDefault(getParameterNegotiationModel());
     }
 
     @Override
     default Can<ManagedObject> getChoices() {
-        return getMetaModel().getChoices(getParameterNegotiationModel(), InteractionInitiatedBy.USER);
+        return getObjectFeature().getChoices(getParameterNegotiationModel(), InteractionInitiatedBy.USER);
     }
 
     @Override
     default Can<ManagedObject> getAutoComplete(final String searchArg) {
-        return getMetaModel().getAutoComplete(getParameterNegotiationModel(), searchArg, InteractionInitiatedBy.USER);
+        return getObjectFeature().getAutoComplete(getParameterNegotiationModel(), searchArg, InteractionInitiatedBy.USER);
     }
 
     @Override
     default String getFriendlyName() {
-        return getMetaModel().getFriendlyName(this::getOwner);
+        return getObjectFeature().getFriendlyName(this::getOwner);
     }
 
     @Override
     default Optional<String> getDescribedAs() {
-        return getMetaModel().getDescription(this::getOwner);
+        return getObjectFeature().getDescription(this::getOwner);
     }
 
     @Override
     default String getFileAccept() {
-        return Facets.fileAccept(getMetaModel()).orElse(null);
+        return Facets.fileAccept(getObjectFeature()).orElse(null);
     }
 
     @Override
     default boolean isRequired() {
-        return !getMetaModel().isOptional();
+        return !getObjectFeature().isOptional();
     }
 
     @Override
     default ObjectSpecification getScalarTypeSpec() {
-        return getMetaModel().getElementType();
+        return getObjectFeature().getElementType();
     }
 
     @Override
@@ -124,7 +124,7 @@ public interface HasUiParameter extends UiParameter {
 
     @Override
     default int getParameterIndex() {
-        return getMetaModel().getParameterIndex();
+        return getObjectFeature().getParameterIndex();
     }
 
     @Override
@@ -149,12 +149,12 @@ public interface HasUiParameter extends UiParameter {
 
     @Override
     default ActionInteractionHead getPendingParamHead() {
-        return getMetaModel().getAction().interactionHead(getUiParameter().getOwner());
+        return getObjectFeature().getAction().interactionHead(getUiParameter().getOwner());
     }
 
     @Override
     default MetaModelContext getMetaModelContext() {
-        return getUiParameter().getMetaModel().getMetaModelContext();
+        return getUiParameter().getObjectFeature().getMetaModelContext();
     }
 
 }
diff --git a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiProperty.java b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiProperty.java
index f42c9cd6ad..f684a3295b 100644
--- a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiProperty.java
+++ b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/HasUiProperty.java
@@ -34,8 +34,8 @@ public interface HasUiProperty extends UiProperty {
     }
 
     @Override
-    default OneToOneAssociation getMetaModel() {
-        return getUiProperty().getMetaModel();
+    default OneToOneAssociation getObjectFeature() {
+        return getUiProperty().getObjectFeature();
     }
 
     @Override
diff --git a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiParameter.java b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiParameter.java
index d3d925f065..fc77d78f87 100644
--- a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiParameter.java
+++ b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiParameter.java
@@ -36,7 +36,7 @@ public interface UiParameter extends UiScalar {
 
     /** param meta model */
     @Override
-    ObjectActionParameter getMetaModel();
+    ObjectActionParameter getObjectFeature();
 
     /** param value */
     @NonNull
@@ -59,57 +59,57 @@ public interface UiParameter extends UiScalar {
 
     @Override
     default int getAutoCompleteMinLength() {
-        return hasAutoComplete() ? getMetaModel().getAutoCompleteMinLength() : 0;
+        return hasAutoComplete() ? getObjectFeature().getAutoCompleteMinLength() : 0;
     }
 
     @Override
     default boolean hasChoices() {
-        return getMetaModel().hasChoices();
+        return getObjectFeature().hasChoices();
     }
 
     @Override
     default boolean hasAutoComplete() {
-        return getMetaModel().hasAutoComplete();
+        return getObjectFeature().hasAutoComplete();
     }
 
     @Override
     default ManagedObject getDefault() {
-        return getMetaModel().getDefault(getParameterNegotiationModel());
+        return getObjectFeature().getDefault(getParameterNegotiationModel());
     }
 
     @Override
     default Can<ManagedObject> getChoices() {
-        return getMetaModel().getChoices(getParameterNegotiationModel(), InteractionInitiatedBy.USER);
+        return getObjectFeature().getChoices(getParameterNegotiationModel(), InteractionInitiatedBy.USER);
     }
 
     @Override
     default Can<ManagedObject> getAutoComplete(final String searchArg) {
-        return getMetaModel().getAutoComplete(getParameterNegotiationModel(), searchArg, InteractionInitiatedBy.USER);
+        return getObjectFeature().getAutoComplete(getParameterNegotiationModel(), searchArg, InteractionInitiatedBy.USER);
     }
 
     @Override
     default String getFriendlyName() {
-        return getMetaModel().getFriendlyName(this::getOwner);
+        return getObjectFeature().getFriendlyName(this::getOwner);
     }
 
     @Override
     default Optional<String> getDescribedAs() {
-        return getMetaModel().getDescription(this::getOwner);
+        return getObjectFeature().getDescription(this::getOwner);
     }
 
     @Override
     default String getFileAccept() {
-        return Facets.fileAccept(getMetaModel()).orElse(null);
+        return Facets.fileAccept(getObjectFeature()).orElse(null);
     }
 
     @Override
     default boolean isRequired() {
-        return !getMetaModel().isOptional();
+        return !getObjectFeature().isOptional();
     }
 
     @Override
     default ObjectSpecification getScalarTypeSpec() {
-        return getMetaModel().getElementType();
+        return getObjectFeature().getElementType();
     }
 
     @Override
@@ -118,12 +118,12 @@ public interface UiParameter extends UiScalar {
     }
 
     default int getParameterIndex() {
-        return getMetaModel().getParameterIndex();
+        return getObjectFeature().getParameterIndex();
     }
 
     @Override
     default String getCssClass() {
-        return getMetaModel().getCssClass("causeway-");
+        return getObjectFeature().getCssClass("causeway-");
     }
 
     @Override
@@ -132,12 +132,12 @@ public interface UiParameter extends UiScalar {
     }
 
     default ActionInteractionHead getPendingParamHead() {
-        return getMetaModel().getAction().interactionHead(getOwner());
+        return getObjectFeature().getAction().interactionHead(getOwner());
     }
 
     @Override
     default MetaModelContext getMetaModelContext() {
-        return getMetaModel().getMetaModelContext();
+        return getObjectFeature().getMetaModelContext();
     }
 
 
diff --git a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiProperty.java b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiProperty.java
index 4617424bef..41f2eabcfd 100644
--- a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiProperty.java
+++ b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiProperty.java
@@ -29,7 +29,7 @@ public interface UiProperty extends UiScalar {
 
     /** prop meta model */
     @Override
-    OneToOneAssociation getMetaModel();
+    OneToOneAssociation getObjectFeature();
 
     // -- PENDING PROPERTY VALUE MODEL
 
@@ -43,42 +43,42 @@ public interface UiProperty extends UiScalar {
 
     @Override
     default String getIdentifier() {
-        return getMetaModel().getFeatureIdentifier().getMemberLogicalName();
+        return getObjectFeature().getFeatureIdentifier().getMemberLogicalName();
     }
 
     @Override
     default String getCssClass() {
-        return getMetaModel().getCssClass("causeway-");
+        return getObjectFeature().getCssClass("causeway-");
     }
 
     @Override
     default int getAutoCompleteMinLength() {
-        return hasAutoComplete() ? getMetaModel().getAutoCompleteMinLength() : 0;
+        return hasAutoComplete() ? getObjectFeature().getAutoCompleteMinLength() : 0;
     }
 
     @Override
     default boolean hasChoices() {
-        return getMetaModel().hasChoices();
+        return getObjectFeature().hasChoices();
     }
 
     @Override
     default boolean hasAutoComplete() {
-        return getMetaModel().hasAutoComplete();
+        return getObjectFeature().hasAutoComplete();
     }
 
     @Override
     default ManagedObject getDefault() {
-        return getMetaModel().getDefault(getOwner());
+        return getObjectFeature().getDefault(getOwner());
     }
 
     @Override
     default Can<ManagedObject> getChoices() {
-        return getMetaModel().getChoices(getOwner(), InteractionInitiatedBy.USER);
+        return getObjectFeature().getChoices(getOwner(), InteractionInitiatedBy.USER);
     }
 
     @Override
     default Can<ManagedObject> getAutoComplete(final String searchArg) {
-        return getMetaModel().getAutoComplete(getOwner(), searchArg, InteractionInitiatedBy.USER);
+        return getObjectFeature().getAutoComplete(getOwner(), searchArg, InteractionInitiatedBy.USER);
     }
 
     @Override
diff --git a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiScalar.java b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiScalar.java
index 3a48ab8676..a34741314d 100644
--- a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiScalar.java
+++ b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/scalar/UiScalar.java
@@ -21,23 +21,17 @@ package org.apache.causeway.viewer.commons.model.scalar;
 import java.util.Optional;
 
 import org.apache.causeway.commons.collections.Can;
-import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.FeatureType;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
+import org.apache.causeway.core.metamodel.spec.feature.HasObjectFeature;
 import org.apache.causeway.core.metamodel.util.Facets;
 import org.apache.causeway.viewer.commons.model.UiModel;
 
-public interface UiScalar extends UiModel, HasMetaModelContext {
+public interface UiScalar
+extends UiModel, HasObjectFeature {
 
-    ObjectFeature getMetaModel();
 
-    @Override
-    default MetaModelContext getMetaModelContext() {
-        return getMetaModel().getMetaModelContext();
-    }
 
     /** action's or property's owner */
     ManagedObject getOwner();
@@ -49,44 +43,44 @@ public interface UiScalar extends UiModel, HasMetaModelContext {
 
     /** feature name */
     default String getFriendlyName() {
-        return getMetaModel().getFriendlyName(this::getOwner);
+        return getObjectFeature().getFriendlyName(this::getOwner);
     }
 
     default boolean isSingular() {
-        return getMetaModel().getFeatureType() == FeatureType.ACTION_PARAMETER_SINGULAR
-                || getMetaModel().getFeatureType() == FeatureType.PROPERTY;
+        return getObjectFeature().getFeatureType() == FeatureType.ACTION_PARAMETER_SINGULAR
+                || getObjectFeature().getFeatureType() == FeatureType.PROPERTY;
     }
 
     default boolean isPlural() {
-        return getMetaModel().getFeatureType() == FeatureType.ACTION_PARAMETER_PLURAL
-                || getMetaModel().getFeatureType() == FeatureType.COLLECTION;
+        return getObjectFeature().getFeatureType() == FeatureType.ACTION_PARAMETER_PLURAL
+                || getObjectFeature().getFeatureType() == FeatureType.COLLECTION;
     }
 
     default boolean isProperty() {
-        return getMetaModel().getFeatureType().isProperty();
+        return getObjectFeature().getFeatureType().isProperty();
     }
 
     default boolean isParameter() {
-        return getMetaModel().getFeatureType().isActionParameter();
+        return getObjectFeature().getFeatureType().isActionParameter();
     }
 
 
     default Optional<String> getDescribedAs() {
-        return getMetaModel().getDescription(this::getOwner);
+        return getObjectFeature().getDescription(this::getOwner);
     }
 
     default String getFileAccept() {
-        return Facets.fileAccept(getMetaModel()).orElse(null);
+        return Facets.fileAccept(getObjectFeature()).orElse(null);
     }
 
     int getAutoCompleteMinLength();
 
     default boolean isRequired() {
-        return !getMetaModel().isOptional();
+        return !getObjectFeature().isOptional();
     }
 
     default ObjectSpecification getScalarTypeSpec() {
-        return getMetaModel().getElementType();
+        return getObjectFeature().getElementType();
     }
 
     ManagedObject getDefault();
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/ReprRendererAbstract.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/ReprRendererAbstract.java
index 04fb1defca..20f7ceff6f 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/ReprRendererAbstract.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/ReprRendererAbstract.java
@@ -20,7 +20,6 @@ package org.apache.causeway.viewer.restfulobjects.rendering;
 
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Stream;
 
 import javax.ws.rs.core.MediaType;
 
@@ -28,6 +27,8 @@ import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.collections._Maps;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.viewer.restfulobjects.applib.JsonRepresentation;
@@ -41,7 +42,7 @@ import lombok.Getter;
 import lombok.val;
 
 public abstract class ReprRendererAbstract<T>
-implements ReprRenderer<T> {
+implements ReprRenderer<T>, HasMetaModelContext {
 
     @Getter protected final IResourceContext resourceContext;
     @Getter protected final JsonValueEncoderService jsonValueEncoder;
@@ -203,9 +204,11 @@ implements ReprRenderer<T> {
         }
     }
 
-    protected Stream<ManagedObject> streamServiceAdapters() {
-        val metaModelContext = resourceContext.getMetaModelContext();
-        return metaModelContext.streamServiceAdapters();
+    // -- HAS MMC
+
+    @Override
+    public MetaModelContext getMetaModelContext() {
+        return resourceContext.getMetaModelContext();
     }
 
 }
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
index 5a46a136b5..c329dba8d3 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
@@ -108,7 +108,7 @@ extends ReprRendererAbstract<ManagedMember> {
     @Override
     public AbstractObjectMemberReprRenderer<T> with(final ManagedMember objectAndMember) {
         this.objectAdapter = objectAndMember.getOwner();
-        this.objectMember = _Casts.uncheckedCast(objectAndMember.getMetaModel());
+        this.objectMember = _Casts.uncheckedCast(objectAndMember.getObjectFeature());
         this.objectMemberType = MemberType.determineFrom(objectMember);
         this.memberId = objectMember.getId();
         usingLinkTo(new DomainObjectLinkTo());
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
index a41f818a0e..86cd22bef5 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
@@ -133,7 +133,7 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
             val act = ManagedAction.of(objectAdapter, objectMember, Where.ANYWHERE);
             val paramNeg = act.startParameterNegotiation();
             for(val paramMod : paramNeg.getParamModels()) {
-                val paramMeta = paramMod.getMetaModel();
+                val paramMeta = paramMod.getObjectFeature();
                 final Object paramDetails = paramDetails(paramMod, paramNeg);
                 parameters.put(paramMeta.getId(), paramDetails);
             }
@@ -143,7 +143,7 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
     }
 
     private Object paramDetails(final ManagedParameter paramMod, final ParameterNegotiationModel paramNeg) {
-        val paramMeta = paramMod.getMetaModel();
+        val paramMeta = paramMod.getObjectFeature();
         final JsonRepresentation paramRep = JsonRepresentation.newMap();
         paramRep.mapPutInt("num", paramMeta.getParameterIndex());
         paramRep.mapPutString("id", paramMeta.getId());
@@ -163,7 +163,7 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
     private Object choicesFor(
             final ManagedParameter paramMod,
             final ParameterNegotiationModel paramNeg) {
-        val paramMeta = paramMod.getMetaModel();
+        val paramMeta = paramMod.getObjectFeature();
         val choiceAdapters = paramMeta.getChoices(paramNeg, getInteractionInitiatedBy());
         if (choiceAdapters == null || choiceAdapters.isEmpty()) {
             return null;
@@ -184,7 +184,7 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
         }
         // REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself
         // final ObjectSpecification defaultSpec = param.getSpecification();
-        val paramMeta = paramMod.getMetaModel();
+        val paramMeta = paramMod.getObjectFeature();
         return DomainObjectReprRenderer.valueOrRef(resourceContext, paramMeta, getJsonValueEncoder(), defaultAdapter);
     }
 
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domaintypes/DomainTypeReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domaintypes/DomainTypeReprRenderer.java
index dc8fb0e895..18d7f2bd6e 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domaintypes/DomainTypeReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/domaintypes/DomainTypeReprRenderer.java
@@ -177,8 +177,8 @@ extends ReprRendererAbstract<ObjectSpecification> {
         final String singularName = objectSpecification.getSingularName();
         getExtensions().mapPutString("friendlyName", singularName);
 
-        final String pluralName = objectSpecification.getPluralName();
-        getExtensions().mapPutString("pluralName", pluralName);
+//        final String pluralName = objectSpecification.getPluralName();
+//        getExtensions().mapPutString("pluralName", pluralName);
     }
 
     protected void putExtensionsDescriptionIfAvailable() {
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
index 42fe932d99..2f3964d4f2 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/causeway/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
@@ -41,6 +41,7 @@ import org.apache.causeway.core.metamodel.interactions.managed.ManagedCollection
 import org.apache.causeway.core.metamodel.interactions.managed.ManagedProperty;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
 import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 import org.apache.causeway.viewer.restfulobjects.applib.CausewayModuleViewerRestfulObjectsApplib;
 import org.apache.causeway.viewer.restfulobjects.applib.JsonRepresentation;
@@ -212,13 +213,11 @@ implements ContentNegotiationService {
                                 resourceContext,
                                 singularActionResult));
             }, pluralActionResult->{
-                final ObjectSpecification elementSpec =
-                        objectAndActionInvocation.getAction().getElementType();
-                final ObjectSpecification actionOwnerSpec = actionOwnerSpecFrom(objectAndActionInvocation);
-                final String actionId = actionIdFrom(objectAndActionInvocation);
+                val action = objectAndActionInvocation.getAction();
+
                 final String actionArguments = actionArgumentsFrom(objectAndActionInvocation);
                 final DomainObjectList listAsViewmodel = domainObjectListFrom(
-                        pluralActionResult, elementSpec, actionOwnerSpec, actionId, actionArguments);
+                        pluralActionResult, action, actionArguments);
 
                 val domainObjectListSpec = resourceContext.getMetaModelContext().getSpecificationLoader()
                     .specForType(DomainObjectList.class)
@@ -258,14 +257,6 @@ implements ContentNegotiationService {
 
     // -- HELPER
 
-    private static ObjectSpecification actionOwnerSpecFrom(final ObjectAndActionInvocation objectAndActionInvocation) {
-        return objectAndActionInvocation.getAction().getDeclaringType();
-    }
-
-    private static String actionIdFrom(final ObjectAndActionInvocation objectAndActionInvocation) {
-        return objectAndActionInvocation.getAction().getId();
-    }
-
     private static String actionArgumentsFrom(final ObjectAndActionInvocation objectAndActionInvocation) {
         final StringBuilder buf = new StringBuilder();
         val parameters = objectAndActionInvocation.getAction().getParameters();
@@ -301,15 +292,16 @@ implements ContentNegotiationService {
 
     private static DomainObjectList domainObjectListFrom(
             final Collection<ManagedObject> collectionAdapters,
-            final ObjectSpecification elementSpec,
-            final ObjectSpecification actionOwnerSpec,
-            final String actionId,
+            final ObjectAction action,
             final String actionArguments) {
 
-        final String title = titleFrom(collectionAdapters, elementSpec);
+        final ObjectSpecification elementSpec = action.getElementType();
+        final ObjectSpecification actionOwnerSpec = action.getDeclaringType();
+
+        final String title = titleFrom(action.getCanonicalFriendlyName(), collectionAdapters.size(), elementSpec);
 
         final DomainObjectList list = new DomainObjectList(
-                title, elementSpec.fqcn(), actionOwnerSpec.fqcn(), actionId, actionArguments);
+                title, elementSpec.fqcn(), actionOwnerSpec.fqcn(), action.getId(), actionArguments);
         for (val adapter : collectionAdapters) {
             list.getObjects().add(adapter.getPojo());
         }
@@ -317,13 +309,12 @@ implements ContentNegotiationService {
     }
 
     private static String titleFrom(
-            final Collection<ManagedObject> collectionAdapters,
+            final String pluralName,
+            final int elementCount,
             final ObjectSpecification elementSpec) {
         final String singularName = elementSpec.getSingularName();
-        final String pluralName = elementSpec.getPluralName();
-        int size = collectionAdapters.size();
         final String title;
-        switch (size) {
+        switch (elementCount) {
         case 0:
             title = "0 " + pluralName;
             break;
@@ -331,7 +322,7 @@ implements ContentNegotiationService {
             title = "1 " + singularName;
             break;
         default:
-            title = size + " " + pluralName;
+            title = elementCount + " " + pluralName;
             break;
         }
         return title;
diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainServicesListReprRenderer.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainServicesListReprRenderer.java
index 00cf8031f4..5b54559d5d 100644
--- a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainServicesListReprRenderer.java
+++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainServicesListReprRenderer.java
@@ -28,7 +28,8 @@ import org.apache.causeway.viewer.restfulobjects.rendering.domainobjects.ListRep
 
 public class DomainServicesListReprRenderer extends ListReprRenderer {
 
-    public DomainServicesListReprRenderer(final IResourceContext resourceContext, final LinkFollowSpecs linkFollower, final JsonRepresentation representation) {
+    public DomainServicesListReprRenderer(
+            final IResourceContext resourceContext, final LinkFollowSpecs linkFollower, final JsonRepresentation representation) {
         super(resourceContext, linkFollower, representation);
     }
 
@@ -46,11 +47,13 @@ public class DomainServicesListReprRenderer extends ListReprRenderer {
 
 
     private void addLinkToSelf() {
-        final JsonRepresentation link = LinkBuilder.newBuilder(getResourceContext(), Rel.SELF.getName(), RepresentationType.LIST, "services").build();
+        final JsonRepresentation link =
+                LinkBuilder.newBuilder(getResourceContext(), Rel.SELF.getName(), RepresentationType.LIST, "services").build();
 
         final LinkFollowSpecs linkFollower = getLinkFollowSpecs().follow("links");
         if (linkFollower.matches(link)) {
-            final DomainServicesListReprRenderer renderer = new DomainServicesListReprRenderer(getResourceContext(), linkFollower, JsonRepresentation.newMap());
+            final DomainServicesListReprRenderer renderer =
+                    new DomainServicesListReprRenderer(getResourceContext(), linkFollower, JsonRepresentation.newMap());
             renderer.with(streamServiceAdapters());
             link.mapPutJsonRepresentation("value", renderer.render());
         }
@@ -59,11 +62,13 @@ public class DomainServicesListReprRenderer extends ListReprRenderer {
     }
 
     private void addLinkToUp() {
-        final JsonRepresentation link = LinkBuilder.newBuilder(resourceContext, Rel.UP.getName(), RepresentationType.HOME_PAGE, "").build();
+        final JsonRepresentation link =
+                LinkBuilder.newBuilder(resourceContext, Rel.UP.getName(), RepresentationType.HOME_PAGE, "").build();
 
         final LinkFollowSpecs linkFollower = getLinkFollowSpecs().follow("links");
         if (linkFollower.matches(link)) {
-            final HomePageReprRenderer renderer = new HomePageReprRenderer(getResourceContext(), linkFollower, JsonRepresentation.newMap());
+            final HomePageReprRenderer renderer =
+                    new HomePageReprRenderer(getResourceContext(), linkFollower, JsonRepresentation.newMap());
             link.mapPutJsonRepresentation("value", renderer.render());
         }
         getLinks().arrayAdd(link);
diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/_DomainResourceHelper.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/_DomainResourceHelper.java
index 72d65cadd4..c4de927307 100644
--- a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/_DomainResourceHelper.java
+++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/_DomainResourceHelper.java
@@ -243,7 +243,7 @@ class _DomainResourceHelper {
 
                 paramOrVeto.ifFailure(veto->{
                     InteractionFailureHandler
-                        .collectParameterInvalid(managedParam.getMetaModel(), veto, arguments);
+                        .collectParameterInvalid(managedParam.getObjectFeature(), veto, arguments);
                     vetoCount.increment();
                 });
 
@@ -264,7 +264,7 @@ class _DomainResourceHelper {
             pendingArgs.getParamModels().zip(individualParamConsents, (managedParam, consent)->{
                 if(consent.isVetoed()) {
                     val veto = InteractionVeto.actionParamInvalid(consent);
-                    InteractionFailureHandler.collectParameterInvalid(managedParam.getMetaModel(), veto, arguments);
+                    InteractionFailureHandler.collectParameterInvalid(managedParam.getObjectFeature(), veto, arguments);
                     vetoCount.increment();
                 }
             });
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ManagedObjectModel.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ManagedObjectModel.java
index e8501adee1..a1480bdf16 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ManagedObjectModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ManagedObjectModel.java
@@ -156,17 +156,4 @@ extends ModelAbstract<ManagedObject> {
         return false;
     }
 
-    // -- DEPRECATIONS
-
-//    private ObjectMemento memento() {
-//        return memento;
-//    }
-//
-//    private void memento(final ObjectMemento memento) {
-//        val manageObject = super.getMetaModelContext().reconstructObject(memento);
-//        super.setObject(manageObject);
-//        this.memento = memento;
-//        this.elementTypeSpec = null; // invalidate
-//    }
-
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModel.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModel.java
index 0b0795d4f3..f6af8e9c4e 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModel.java
@@ -186,7 +186,7 @@ implements HasRenderingHints, UiScalar, LinksProvider, FormExecutorContext {
 
     @Override
     public final PromptStyle getPromptStyle() {
-        return Facets.promptStyleOrElse(getMetaModel(), PromptStyle.INLINE);
+        return Facets.promptStyleOrElse(getObjectFeature(), PromptStyle.INLINE);
     }
 
     public boolean canEnterEditMode() {
@@ -286,7 +286,7 @@ implements HasRenderingHints, UiScalar, LinksProvider, FormExecutorContext {
     }
 
     public final OptionalInt multilineNumberOfLines() {
-        return Facets.multilineNumberOfLines(getMetaModel());
+        return Facets.multilineNumberOfLines(getObjectFeature());
     }
 
     public final OptionalInt maxLength() {
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModelWithMultiChoice.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModelWithMultiChoice.java
index f5e8f94a3d..1935449beb 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModelWithMultiChoice.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarModelWithMultiChoice.java
@@ -84,9 +84,9 @@ implements
     @Override
     public void setObject(final ArrayList<ObjectMemento> unpackedMemento) {
         log.debug("setObject() as unpackedMemento {}", unpackedMemento);
-        val logicalType = scalarModel().getScalarTypeSpec().getLogicalType();
-        val packedMemento = ObjectMemento.pack(unpackedMemento, logicalType);
-        pendingValue().getValue().setValue(getObjectManager().demementify(packedMemento));
+        val objectFeature = scalarModel().getObjectFeature();
+        val packedMemento = ObjectMemento.pack(objectFeature, unpackedMemento);
+        pendingValue().getValue().setValue(getObjectManager().demementify(objectFeature, packedMemento));
     }
 
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarPropertyModel.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarPropertyModel.java
index f76392dcc0..3a0447b38f 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarPropertyModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ScalarPropertyModel.java
@@ -78,7 +78,7 @@ implements HasUiProperty {
 
     @Override
     public String toStringOf() {
-        val featureId = uiProperty.getMetaModel().getFeatureIdentifier();
+        val featureId = uiProperty.getObjectFeature().getFeatureIdentifier();
         return getFriendlyName() + ": " +
                 featureId.getLogicalTypeName() + "#" + featureId.getMemberLogicalName();
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/UiParameterWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/UiParameterWkt.java
index b67827a8df..cc551618e3 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/UiParameterWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/UiParameterWkt.java
@@ -74,7 +74,7 @@ implements
     }
 
     @Override
-    public ObjectActionParameter getMetaModel() {
+    public ObjectActionParameter getObjectFeature() {
         return actionInteraction().getMetamodel().get().getParameters().getElseFail(paramIndex);
     }
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/prop/UiPropertyWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/prop/UiPropertyWkt.java
index 1fd44636f6..aedf6f5d46 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/prop/UiPropertyWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/prop/UiPropertyWkt.java
@@ -69,8 +69,8 @@ implements
     }
 
     @Override
-    public final OneToOneAssociation getMetaModel() {
-        return propertyInteraction().getManagedProperty().get().getMetaModel();
+    public final OneToOneAssociation getObjectFeature() {
+        return propertyInteraction().getManagedProperty().get().getObjectFeature();
     }
 
     @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelFactory.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelFactory.java
index fa9111179d..608fb51188 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelFactory.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelFactory.java
@@ -99,7 +99,7 @@ extends Function<ObjectAction, LinkAndLabel> {
     public static LinkAndLabelFactory forParameter(
             final ScalarParameterModel parameterModel) {
         //XXX[CAUSEWAY-3080] only supported, when parameter type is a singular composite value-type
-        val param = parameterModel.getMetaModel();
+        val param = parameterModel.getObjectFeature();
         if(param.isSingular()
                 && param.getElementType().isCompositeValue()) {
             return action -> LinkAndLabel.of(
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actions/ActionParametersForm.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actions/ActionParametersForm.java
index 37afdd03e9..4312bf4727 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actions/ActionParametersForm.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actions/ActionParametersForm.java
@@ -140,7 +140,7 @@ extends PromptFormAbstract<ActionModel> {
         // only updates subsequent parameter panels starting from (paramNumberUpdated + 1)
         final int skipCount = paramNumberUpdated + 1; // eg. if paramNumberUpdated=0 then skipCount=1
 
-        val paramCount = updatedParamModel.getMetaModel().getAction().getParameterCount();
+        val paramCount = updatedParamModel.getObjectFeature().getAction().getParameterCount();
         val maxCapacity = paramCount - skipCount; // just an optimization, not strictly required
         val paramOnlyUpdateRequestsHavingParamIndex = _Lists.<Integer>newArrayList(maxCapacity);
 
@@ -149,7 +149,7 @@ extends PromptFormAbstract<ActionModel> {
             .map(paramModel->{
 
                 val paramIndexForReassessment = paramModel.getParameterIndex();
-                val actionParameter = paramModel.getMetaModel();
+                val actionParameter = paramModel.getObjectFeature();
 
                 actionParameter.reassessDefault(paramNegotiationModel);
                 _Xray.reassessedDefault(paramIndexForReassessment, paramNegotiationModel);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
index 477761f36d..9e03e9a81f 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
@@ -416,7 +416,7 @@ implements ScalarModelChangeListener {
 
         Wkt.cssAppend(this, scalarModel.getCssClass());
 
-        Facets.cssClass(scalarModel.getMetaModel(), scalarModel.getParentUiModel().getManagedObject())
+        Facets.cssClass(scalarModel.getObjectFeature(), scalarModel.getParentUiModel().getManagedObject())
         .ifPresent(cssClass->
             Wkt.cssAppend(this, cssClass));
     }
@@ -579,7 +579,7 @@ implements ScalarModelChangeListener {
     }
 
     private static String determinePropParamLayoutCss(final ScalarModel scalarModel) {
-        return Facets.labelAtCss(scalarModel.getMetaModel());
+        return Facets.labelAtCss(scalarModel.getObjectFeature());
     }
 
     private static String determineActionLayoutPositioningCss(final Can<LinkAndLabel> entityActionLinks) {
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
index b52b9a284f..f8b34d6730 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
@@ -147,7 +147,7 @@ extends ScalarPanelAbstract {
     // -- SEMANTICS
 
     private OptionsBasedOnValueSemantics getSemantics(final ScalarModel scalarModel) {
-        return new OptionsBasedOnValueSemantics(scalarModel.getMetaModel(), scalarModel.isEditMode()
+        return new OptionsBasedOnValueSemantics(scalarModel.getObjectFeature(), scalarModel.isEditMode()
                 ? ScalarRepresentation.EDITING
                 : ScalarRepresentation.VIEWING);
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.java
index 4bae3e4593..4aa791458a 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.java
@@ -153,7 +153,7 @@ extends ScalarPanelAbstract2 {
             xrayDetails.put("scalarModel.whetherHidden", ""+scalarModel().whetherHidden());
             xrayDetails.put("scalarModel.identifier", ""+scalarModel().getIdentifier());
             xrayDetails.put("scalarModel.choices (count)", ""+scalarModel().getChoices().size());
-            xrayDetails.put("scalarModel.metaModel.featureIdentifier", ""+scalarModel().getMetaModel().getFeatureIdentifier());
+            xrayDetails.put("scalarModel.metaModel.featureIdentifier", ""+scalarModel().getObjectFeature().getFeatureIdentifier());
             xrayDetails.put("scalarModel.scalarTypeSpec", ""+scalarModel().getScalarTypeSpec().toString());
             xrayDetails.put("scalarModel.proposedValue", ""+scalarModel().proposedValue().getValue().getValue());
 //                    getSpecialization()
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
index b2daeedc17..f8b3a99542 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
@@ -65,7 +65,7 @@ extends ScalarPanelFormFieldAbstract<T> {
     // -- CONVERSION
 
     protected final IConverter<T> getConverter(final ScalarModel scalarModel) {
-        return getConverter(scalarModel.getMetaModel(), scalarModel.isEditMode()
+        return getConverter(scalarModel.getObjectFeature(), scalarModel.isEditMode()
                 ? ScalarRepresentation.EDITING
                 : ScalarRepresentation.VIEWING);
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
index 61ed7e4bf6..3aa4edceb1 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
@@ -45,7 +45,7 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
     }
 
     protected int getDateRenderAdjustDays() {
-        return Facets.dateRenderAdjustDays(scalarModel().getMetaModel());
+        return Facets.dateRenderAdjustDays(scalarModel().getObjectFeature());
     }
 
     @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/_Util.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/_Util.java
index a0f235c147..6882767123 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/_Util.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/_Util.java
@@ -150,7 +150,7 @@ class _Util {
             .map(v->scalarModel
                     .getObjectManager().demementify((ObjectMemento)v))
             .collect(Can.toCan());
-            return Optional.of(ManagedObject.packed(scalarModel.getScalarTypeSpec(), unpackedValues));
+            return Optional.of(ManagedObject.packed(scalarModel.getObjectFeature(), unpackedValues));
         }
 
         if(valueObject instanceof ObjectMemento) {
@@ -177,11 +177,11 @@ class _Util {
         return scalarModel.getSpecialization().<Optional<ObjectAction>>fold(
                 param->
                     Facets.valueCompositeMixinForParameter(
-                            scalarModel.getMetaModel(),
+                            scalarModel.getObjectFeature(),
                             param.getParameterNegotiationModel(), param.getParameterIndex()),
                 prop->
                     Facets.valueCompositeMixinForProperty(
-                            scalarModel.getMetaModel(),
+                            scalarModel.getObjectFeature(),
                             prop.getManagedProperty()));
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
index 0310fd7990..cd074274fb 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
@@ -119,10 +119,10 @@ public class MarkupComponent extends WebComponent {
 
     protected Optional<ObjectFeature> lookupObjectFeatureIn(final IModel<?> model) {
         if(model instanceof ScalarPropertyModel) {
-            return Optional.of(((ScalarPropertyModel)model).getMetaModel());
+            return Optional.of(((ScalarPropertyModel)model).getObjectFeature());
         }
         if(model instanceof UiParameter) {
-            return Optional.of(((UiParameter)model).getMetaModel());
+            return Optional.of(((UiParameter)model).getObjectFeature());
         }
         if(model instanceof ValueModel) {
             return Optional.ofNullable(((ValueModel)model).getActionModelHint())
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/string/StringPanelFactory.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/string/StringPanelFactory.java
index 6bbc84ba0b..49c46e6eaa 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/string/StringPanelFactory.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/string/StringPanelFactory.java
@@ -34,7 +34,7 @@ public class StringPanelFactory extends ComponentFactoryScalarAbstract {
 
     @Override
     public Component createComponent(final String id, final ScalarModel scalarModel) {
-        return Facets.multilineIsPresent(scalarModel.getMetaModel())
+        return Facets.multilineIsPresent(scalarModel.getObjectFeature())
             ? new MultiLineStringPanel(id, scalarModel)
             : new StringPanel(id, scalarModel);
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2MultiChoiceExt.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2MultiChoiceExt.java
index ac9e7b467d..6eab2133e7 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2MultiChoiceExt.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2MultiChoiceExt.java
@@ -24,10 +24,10 @@ import java.util.Collection;
 import org.apache.wicket.model.IModel;
 import org.wicketstuff.select2.Select2MultiChoice;
 
-import org.apache.causeway.applib.id.HasLogicalType;
-import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMemento;
+import org.apache.causeway.core.metamodel.spec.feature.HasObjectFeature;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.causeway.viewer.wicket.model.models.ScalarModel;
 import org.apache.causeway.viewer.wicket.ui.components.widgets.select2.providers.ChoiceProviderAbstract;
 
@@ -36,7 +36,7 @@ import lombok.val;
 
 public class Select2MultiChoiceExt
 extends Select2MultiChoice<ObjectMemento>
-implements HasLogicalType {
+implements HasObjectFeature {
 
     private static final long serialVersionUID = 1L;
 
@@ -49,7 +49,7 @@ implements HasLogicalType {
         return new Select2MultiChoiceExt(id, _Casts.uncheckedCast(modelObject), scalarModel, choiceProvider);
     }
 
-    @Getter(onMethod_ = {@Override}) private final LogicalType logicalType;
+    @Getter(onMethod_ = {@Override}) private final ObjectFeature objectFeature;
 
     Select2MultiChoiceExt(
             final String id,
@@ -58,7 +58,7 @@ implements HasLogicalType {
             final ChoiceProviderAbstract choiceProvider) {
 
         super(id, model, choiceProvider);
-        logicalType = scalarModel.getScalarTypeSpec().getLogicalType();
+        this.objectFeature = scalarModel.getObjectFeature();
 
         getSettings().setCloseOnSelect(true);
         getSettings().setWidth("auto");
@@ -68,11 +68,11 @@ implements HasLogicalType {
     }
 
     public ObjectMemento getPackedModelObject() {
-        return ObjectMemento.pack(this.getModelObject(), this.getLogicalType());
+        return ObjectMemento.pack(getObjectFeature(), getModelObject());
     }
 
     public ObjectMemento getPackedConvertedInput() {
-        return ObjectMemento.pack(this.getConvertedInput(), this.getLogicalType());
+        return ObjectMemento.pack(getObjectFeature(), getConvertedInput());
     }
 
     public IModel<ObjectMemento> getPackingAdapterModel() {
@@ -95,7 +95,7 @@ implements HasLogicalType {
             final IModel<Collection<ObjectMemento>> delegate;
             {
                 this.delegate = multi.getModel();
-                this.memento = ObjectMemento.pack(delegate.getObject(), multi.getLogicalType());
+                this.memento = ObjectMemento.pack(multi.getObjectFeature(), delegate.getObject());
             }
 
             @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java
index 3b2e66f77c..ef03c99656 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java
@@ -41,7 +41,6 @@ import org.apache.causeway.commons.internal.debug.xray.XrayUi;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.PackedManagedObject;
 import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMemento;
-import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.viewer.wicket.model.models.ScalarModel;
 import org.apache.causeway.viewer.wicket.model.util.PageParameterUtils;
 import org.apache.causeway.viewer.wicket.ui.components.scalars.ScalarModelChangeDispatcher;
@@ -199,7 +198,7 @@ class Select2OnSelect extends AbstractAjaxBehavior {
     private PackedManagedObject demementify(
             final @NonNull List<ObjectMemento> mementos) {
         return ManagedObject.packed(
-                elementSpec(),
+                scalarModel.getObjectFeature(),
                 mementos.stream().map(this::demementify).collect(Can.toCan()));
     }
 
@@ -212,13 +211,6 @@ class Select2OnSelect extends AbstractAjaxBehavior {
         return updateReceiver;
     }
 
-    private @NonNull ObjectSpecification elementSpec() {
-        val updateReceiver = scalarModel.getSpecialization().fold(
-                param->param.getScalarTypeSpec(),
-                prop->prop.getScalarTypeSpec());
-        return updateReceiver;
-    }
-
     private void clearUpdateReceiver() {
         scalarModel.getSpecialization().accept(
             param->
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderAbstract.java
index eb89524fa3..1f0d7e0da6 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderAbstract.java
@@ -31,7 +31,7 @@ import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMemento;
-import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoForEmpty;
+import org.apache.causeway.core.metamodel.objectmanager.memento.ObjectMementoEmpty;
 import org.apache.causeway.viewer.wicket.model.models.HasCommonContext;
 
 import lombok.val;
@@ -54,7 +54,7 @@ implements HasCommonContext {
     @Override
     public final String getDisplayValue(final ObjectMemento choiceMemento) {
         if (choiceMemento == null
-                || choiceMemento instanceof ObjectMementoForEmpty) {
+                || choiceMemento instanceof ObjectMementoEmpty) {
             return getPlaceholderRenderService().asText(PlaceholderLiteral.NULL_REPRESENTATION);
         }
         return translate(choiceMemento.getTitle());
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderForReferences.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderForReferences.java
index 685ecdffd0..e7f99ac627 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderForReferences.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/providers/ChoiceProviderForReferences.java
@@ -107,8 +107,11 @@ extends ChoiceProviderAbstractForScalarModel {
             final UiParameter parameterModel,
             final Can<ObjectMemento> pendingArgMementos) {
 
+
+        val feature = parameterModel.getObjectFeature();
+
         val pendingArgsList = _NullSafe.stream(pendingArgMementos)
-            .map(getObjectManager()::demementify)
+            .map(memento->getObjectManager().demementify(feature, memento))
             .collect(Can.toCan());
 
        return pendingArgsList;