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:30 UTC

[isis] branch 3321-applib.remove.plural created (now e4e0898d90)

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

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


      at e4e0898d90 ISIS-3321: prepare removal of DomainObjectLayout#plural

This branch includes the following new commits:

     new e4e0898d90 ISIS-3321: prepare removal of DomainObjectLayout#plural

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by ah...@apache.org.
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;