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 2019/10/09 14:17:45 UTC

[isis] 04/04: ISIS-2158: introduces a DeficiencyFacet

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

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 175dd48e1433e309eff73e375b972e758bff8a63
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Oct 9 16:17:31 2019 +0200

    ISIS-2158: introduces a DeficiencyFacet
    
    - facets of this particular type do now get added directly to the
    meta-model during validation; these are specifically added to the
    facet-holder, that is considered responsible for the failure
    - this automatically allows for the meta-model export to also include
    deficiency facets, explaining what's going wrong, right at the place
    where they originate from
---
 .../isis/metamodel/facetapi/FacetAbstract.java     |  18 ++--
 ...nChoicesForCollectionParameterFacetFactory.java |  18 ++--
 .../annotation/HomePageFacetAnnotationFactory.java |  41 ++++---
 .../facets/all/deficiencies/DeficiencyFacet.java   | 118 +++++++++++++++++++++
 .../annotation/SortedByFacetAnnotationFactory.java |   7 +-
 .../metamodel/facets/jaxb/JaxbFacetFactory.java    |  53 +++++----
 .../ViewModelSemanticCheckingFacetFactory.java     |  35 ++++--
 .../BookmarkPolicyFacetFallbackFactory.java        |   4 +-
 .../DomainObjectAnnotationFacetFactory.java        |  29 +++--
 .../DomainServiceFacetAnnotationFactory.java       |  13 +--
 .../mixin/MetaModelValidatorForMixinTypes.java     |  10 +-
 .../mixin/MixinFacetForMixinAnnotationFactory.java |   4 +-
 .../NavigableParentAnnotationFacetFactory.java     |   8 +-
 ...jectSpecIdFacetDerivedFromClassNameFactory.java |  13 ++-
 .../recreatable/RecreatableObjectFacetFactory.java |   5 +-
 .../annotation/TitleAnnotationFacetFactory.java    |   5 +-
 ...engthFacetForMaxLengthAnnotationOnProperty.java |  35 ------
 .../progmodel/ProgrammingModelService.java         |   7 +-
 .../specloader/ProgrammingModelServiceDefault.java |  17 ---
 .../metamodel/specloader/SpecificationLoader.java  |  13 ++-
 .../specloader/SpecificationLoaderDefault.java     |  23 +++-
 .../specloader/validator/MetaModelValidator.java   |  28 +++--
 .../validator/MetaModelValidatorAbstract.java      |  26 +++++
 .../validator/MetaModelValidatorComposite.java     |  65 ------------
 ...etaModelValidatorForConflictingOptionality.java |  23 ++--
 .../MetaModelValidatorForDeprecatedAbstract.java   |  17 +--
 ...etaModelValidatorForDeprecatedMethodPrefix.java |   4 +-
 .../MetaModelValidatorForValidationFailures.java   |  13 +--
 .../validator/MetaModelValidatorVisiting.java      |  35 +++---
 .../ViewModelSemanticCheckingFacetFactoryTest.java |   6 +-
 .../jdo/metamodel/JdoProgrammingModelPlugin.java   |  64 ++++++-----
 .../object/query/VisitorForClauseAbstract.java     |  22 ++--
 .../facets/object/query/VisitorForFromClause.java  |   7 +-
 .../object/query/VisitorForVariablesClause.java    |   7 +-
 .../version/JdoVersionAnnotationFacetFactory.java  |  11 +-
 ...DerivedFromJdoColumnAnnotationFacetFactory.java |  18 ++--
 ...ndatoryFromJdoColumnAnnotationFacetFactory.java |  27 +++--
 ...DerivedFromJdoColumnAnnotationFacetFactory.java |  29 ++---
 ...AnnotationFacetFactoryTest_refineMetaModel.java |  35 +++---
 .../system/session/IsisSessionFactoryDefault.java  |   7 +-
 .../validate/ValidateDomainModel.java              |   6 +-
 .../domainmodel/SpecloaderPerformanceTest.java     |   4 +-
 .../SupportingMethodValidatorRefinerFactory.java   |   3 +-
 43 files changed, 525 insertions(+), 408 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facetapi/FacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facetapi/FacetAbstract.java
index 87a04f3..0689275 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facetapi/FacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facetapi/FacetAbstract.java
@@ -20,14 +20,13 @@
 package org.apache.isis.metamodel.facetapi;
 
 import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Stream;
 
-import org.apache.isis.commons.internal.assertions._Ensure;
 import org.apache.isis.metamodel.MetaModelContext;
 
 import static org.apache.isis.commons.internal.base._With.requires;
 
+import lombok.val;
+
 
 public abstract class FacetAbstract implements Facet, MetaModelContext.Delegating {
 
@@ -99,10 +98,10 @@ public abstract class FacetAbstract implements Facet, MetaModelContext.Delegatin
                     throw new IllegalArgumentException("illegal argument, expected underlying facet (a multi-valued facet) to have equivalent to the facet type (or facet types) of this facet");
                 }
             } else {
-                _Ensure.ensureThatArg(
-                        underlyingFacet.facetType(), 
-                        type->Objects.equals(type, facetType), 
-                        ()->String.format("type-missmatch: underlying facet's type '%s' must match this facet's type '%s'"));
+//                _Ensure.ensureThatArg(
+//                        underlyingFacet.facetType(), 
+//                        type->Objects.equals(type, facetType), 
+//                        ()->String.format("type-missmatch: underlying facet's type '%s' must match this facet's type '%s'"));
             }
         }
         this.underlyingFacet = underlyingFacet;
@@ -114,9 +113,8 @@ public abstract class FacetAbstract implements Facet, MetaModelContext.Delegatin
             return multiTypedFacet.containsFacetTypeOf(this.facetType);
         }
 
-        final MultiTypedFacet thisAsMultiTyped = (MultiTypedFacet) this;
-        final Stream<Class<? extends Facet>> facetTypes = thisAsMultiTyped.facetTypes();
-        return facetTypes
+        val thisAsMultiTyped = (MultiTypedFacet) this;
+        return thisAsMultiTyped.facetTypes()
                 .anyMatch(facetType->multiTypedFacet.containsFacetTypeOf(facetType));
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java
index 118dd85..8f18efc 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java
@@ -33,8 +33,8 @@ import org.apache.isis.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.metamodel.specloader.specimpl.ObjectActionContributee;
 import org.apache.isis.metamodel.specloader.specimpl.ObjectActionMixedIn;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -69,14 +69,14 @@ implements MetaModelRefiner {
                     @Override
                     public boolean visit(
                             final ObjectSpecification objectSpec,
-                            final ValidationFailures validationFailures) {
-                        validate(objectSpec, validationFailures);
+                            final MetaModelValidator validator) {
+                        validate(objectSpec, validator);
                         return true;
                     }
 
                     private void validate(
                             final ObjectSpecification objectSpec,
-                            final ValidationFailures validationFailures) {
+                            final MetaModelValidator validator) {
                         objectSpec.streamObjectActions(Contributed.INCLUDED)
                         .forEach(objectAction->{
                             if(objectAction instanceof ObjectActionMixedIn || objectAction instanceof ObjectActionContributee) {
@@ -87,7 +87,7 @@ implements MetaModelRefiner {
                             int paramNum = 0;
                             for (ObjectActionParameter parameter : objectAction.getParameters()) {
                                 if(parameter.getFeatureType() == FeatureType.ACTION_PARAMETER_COLLECTION) {
-                                    validate(objectSpec, objectAction, parameter, paramNum, validationFailures);
+                                    validate(objectSpec, objectAction, parameter, paramNum, validator);
                                 }
                                 paramNum++;
                             }
@@ -99,7 +99,7 @@ implements MetaModelRefiner {
                             final ObjectAction objectAction,
                             final ObjectActionParameter parameter,
                             final int paramNum,
-                            final ValidationFailures validationFailures) {
+                            final MetaModelValidator validator) {
 
 
                         final CollectionSemanticsFacet collectionSemantics =
@@ -109,7 +109,8 @@ implements MetaModelRefiner {
                             // from java.util.Collection but are not of
                             // exact type List, Set, SortedSet or Collection.
                             if(!collectionSemantics.value().isSupportedInterfaceForActionParameters()) {
-                                validationFailures.add(
+                                validator.onFailure(
+                                        objectSpec,
                                         objectSpec.getIdentifier(),
                                         "Collection action parameter found that is not exactly one "
                                                 + "of the following supported types: "
@@ -135,7 +136,8 @@ implements MetaModelRefiner {
                             return;
                         }
 
-                        validationFailures.add(
+                        validator.onFailure(
+                                objectSpec,
                                 objectSpec.getIdentifier(),
                                 "Collection action parameter found without supporting "
                                         + "choices or autoComplete facet.  "
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/homepage/annotation/HomePageFacetAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/homepage/annotation/HomePageFacetAnnotationFactory.java
index 3545863..05b3b92 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/homepage/annotation/HomePageFacetAnnotationFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/actions/homepage/annotation/HomePageFacetAnnotationFactory.java
@@ -23,7 +23,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.annotation.HomePage;
@@ -39,9 +38,9 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import static org.apache.isis.commons.internal.functions._Predicates.not;
 
@@ -72,45 +71,53 @@ implements MetaModelRefiner {
     private Visitor newValidatorVisitor() {
         return new MetaModelValidatorVisiting.SummarizingVisitor() {
 
-            private final List<Identifier> annotated = _Lists.newArrayList();
+            private final List<ObjectAction> actionsHavingHomePageFacet = _Lists.newArrayList();
 
             @Override
-            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
-                final Stream<ObjectAction> objectActions = objectSpec.streamObjectActions(Contributed.EXCLUDED);
+            public boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator) {
+                
+                val objectActions = 
+                        objectSpec.streamObjectActions(Contributed.EXCLUDED);
 
                 objectActions
                 .filter(objectAction->objectAction.containsFacet(HomePageFacet.class))
                 .forEach(objectAction->{
+                    
+                    actionsHavingHomePageFacet.add(objectAction);
 
-                    val identifier = objectAction.getIdentifier();
-
-                    // TODO: it would good to flag if the facet is found on any non-services, however
-                    // ObjectSpecification.isService(...) can only be trusted once a PersistenceSession exists.
+                    // TODO: it would be good to flag if the facet is found on any non-services, however
+                    // ObjectSpecification.isService(...) can only be trusted once a PersistenceSession 
+                    // exists.
                     // this ought to be improved upon at some point...
-                    annotated.add(identifier);
+
+                    // TODO might collide with type level annotations as well 
+                    
                 });
 
                 return true; // keep searching
             }
 
             @Override
-            public void summarize(ValidationFailures validationFailures) {
-                if(annotated.size()>1) {
+            public void summarize(MetaModelValidator validator) {
+                if(actionsHavingHomePageFacet.size()>1) {
                     
-                    final Set<String> annotatedIdSet = annotated.stream()
+                    final Set<String> homepageActionIdSet = actionsHavingHomePageFacet.stream()
+                            .map(ObjectAction::getIdentifier)
                             .map(Identifier::toClassAndNameIdentityString)
                             .collect(Collectors.toCollection(HashSet::new));
                     
-                    for (val actionIdentifier : annotated) {
+                    for (val objectAction : actionsHavingHomePageFacet) {
+                        val actionIdentifier = objectAction.getIdentifier(); 
                         val actionId = actionIdentifier.toClassAndNameIdentityString();
-                        val nonServiceNamesStr = annotatedIdSet.stream()
+                        val colission = homepageActionIdSet.stream()
                                 .filter(not(actionId::equals))
                                 .collect(Collectors.joining(", "));
 
-                        validationFailures.add(
+                        validator.onFailure(
+                                objectAction,
                                 actionIdentifier,
                                 "%s: other actions also specified as home page: %s ",
-                                actionId, nonServiceNamesStr);
+                                actionId, colission);
                     }
                 }
             }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/all/deficiencies/DeficiencyFacet.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/all/deficiencies/DeficiencyFacet.java
new file mode 100644
index 0000000..5cd3d07
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/all/deficiencies/DeficiencyFacet.java
@@ -0,0 +1,118 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.metamodel.facets.all.deficiencies;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.metamodel.facetapi.Facet;
+import org.apache.isis.metamodel.facetapi.FacetHolder;
+
+import static org.apache.isis.commons.internal.base._With.computeIfAbsent;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Value;
+import lombok.val;
+
+/**
+ * Collects meta-model validation failures (deficiencies) directly on the holder that is involved.
+ * @since 2.0
+ */
+@RequiredArgsConstructor(staticName = "of")
+public final class DeficiencyFacet implements Facet {
+
+    @NonNull @Getter private final FacetHolder facetHolder;
+    @NonNull @Getter private final List<Deficiency> deficiencies;
+    
+    @Value(staticConstructor = "of")
+    public static final class Deficiency {
+        @NonNull @Getter private Identifier deficiencyOrigin;
+        @NonNull @Getter private String deficiencyMessage;
+    }
+    
+    /**
+     * Create a new DeficiencyFacet for the facetHolder (if it not already has one), 
+     * then adds given deficiency information to the facet. 
+     * @param facetHolder
+     * @param deficiencyOrigin
+     * @param deficiencyMessage
+     * @return
+     */
+    public static DeficiencyFacet appendTo(
+            @NonNull FacetHolder facetHolder, 
+            @NonNull Identifier deficiencyOrigin, 
+            @NonNull String deficiencyMessage) {
+        
+        val deficiencyFacet = computeIfAbsent(facetHolder.getFacet(DeficiencyFacet.class), 
+                ()->DeficiencyFacet.of(facetHolder, new ArrayList<>()));
+        
+        deficiencyFacet.getDeficiencies().add(Deficiency.of(deficiencyOrigin, deficiencyMessage));
+        return deficiencyFacet;
+    }
+    
+    // -- FACET IMPLEMENTATION
+    
+    @Override
+    public void setFacetHolder(FacetHolder facetHolder) {
+        throw _Exceptions.unsupportedOperation();
+    }
+
+    @Override
+    public Facet getUnderlyingFacet() {
+        return null;
+    }
+
+    @Override
+    public void setUnderlyingFacet(Facet underlyingFacet) {
+        throw _Exceptions.unsupportedOperation();
+    }
+
+    @Override
+    public Class<? extends Facet> facetType() {
+        return DeficiencyFacet.class;
+    }
+
+    @Override
+    public boolean isDerived() {
+        return false;
+    }
+
+    @Override
+    public boolean isNoop() {
+        return true;
+    }
+
+    @Override
+    public boolean alwaysReplace() {
+        return false;
+    }
+
+    @Override
+    public void appendAttributesTo(Map<String, Object> attributeMap) {
+        throw _Exceptions.unsupportedOperation();
+    }
+
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java
index 4d279d3..b2733b9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java
@@ -31,9 +31,9 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 /**
  * There is no check that the value is a {@link Comparator}; instead this is done through
@@ -63,7 +63,7 @@ implements MetaModelRefiner {
         return new MetaModelValidatorVisiting.Visitor() {
 
             @Override
-            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+            public boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator) {
                 final Stream<OneToManyAssociation> objectCollections = objectSpec.streamCollections(Contributed.EXCLUDED);
 
                 objectCollections.forEach(objectCollection->{
@@ -71,7 +71,8 @@ implements MetaModelRefiner {
                     if(facet != null) {
                         final Class<? extends Comparator<?>> cls = facet.value();
                         if(!Comparator.class.isAssignableFrom(cls)) {
-                            validationFailures.add(
+                            validator.onFailure(
+                                    objectSpec,
                                     objectSpec.getIdentifier(),
                                     "%s#%s: is annotated with @SortedBy, but the class specified '%s' is not a Comparator",
                                     objectSpec.getIdentifier().getClassName(), 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/jaxb/JaxbFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/jaxb/JaxbFacetFactory.java
index dd1ff6c..f2100a5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/jaxb/JaxbFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/jaxb/JaxbFacetFactory.java
@@ -46,8 +46,8 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 /**
  * just adds a validator
@@ -157,15 +157,15 @@ implements MetaModelRefiner {
                     @Override
                     public boolean visit(
                             final ObjectSpecification objectSpec,
-                            final ValidationFailures validationFailures) {
+                            final MetaModelValidator validator) {
 
-                        validate(objectSpec, validationFailures);
+                        validate(objectSpec, validator);
                         return true;
                     }
 
                     private void validate(
                             final ObjectSpecification objectSpec,
-                            final ValidationFailures validationFailures) {
+                            final MetaModelValidator validator) {
 
                         final boolean viewModel = objectSpec.isViewModel();
                         if(!viewModel) {
@@ -178,7 +178,7 @@ implements MetaModelRefiner {
                         }
 
                         for (final TypeValidator typeValidator : typeValidators) {
-                            typeValidator.validate(objectSpec, validationFailures);
+                            typeValidator.validate(objectSpec, validator);
                         }
 
                         final Stream<OneToOneAssociation> properties = objectSpec
@@ -189,7 +189,7 @@ implements MetaModelRefiner {
                         .filter(property->property.containsDoOpFacet(PropertySetterFacet.class))
                         .forEach(property->{
                             for (final PropertyValidator adapterValidator : propertyValidators) {
-                                adapterValidator.validate(objectSpec, property, validationFailures);
+                                adapterValidator.validate(objectSpec, property, validator);
                             }
                         });
 
@@ -231,7 +231,7 @@ implements MetaModelRefiner {
     private static abstract class TypeValidator {
         abstract void validate(
                 final ObjectSpecification objectSpec,
-                final ValidationFailures validationFailures);
+                final MetaModelValidator validator);
 
     }
 
@@ -239,7 +239,7 @@ implements MetaModelRefiner {
         abstract void validate(
                 final ObjectSpecification objectSpec,
                 final OneToOneAssociation property,
-                final ValidationFailures validationFailures);
+                final MetaModelValidator validator);
 
     }
 
@@ -249,7 +249,7 @@ implements MetaModelRefiner {
         void validate(
                 final ObjectSpecification objectSpec,
                 final OneToOneAssociation property,
-                final ValidationFailures validationFailures) {
+                final MetaModelValidator validator) {
 
             final ObjectSpecification propertyTypeSpec = property.getSpecification();
             if (!propertyTypeSpec.isEntity()) {
@@ -262,7 +262,8 @@ implements MetaModelRefiner {
                 return;
             }
             final Class<?> propertyType = propertyTypeSpec.getCorrespondingClass();
-            validationFailures.add(
+            validator.onFailure(
+                    property,
                     property.getIdentifier(),
                     "JAXB view model '%s' property '%s' is of type '%s' but that type is not annotated with @XmlJavaTypeAdapter.  The type must be annotated with @XmlJavaTypeAdapter(org.apache.isis.schema.utils.jaxbadapters.PersistentEntityAdapter.class) or equivalent.",
                     objectSpec.getFullIdentifier(),
@@ -283,7 +284,7 @@ implements MetaModelRefiner {
         void validate(
                 final ObjectSpecification objectSpec,
                 final OneToOneAssociation property,
-                final ValidationFailures validationFailures) {
+                final MetaModelValidator validator) {
 
             final ObjectSpecification propertyTypeSpec = property.getSpecification();
             final Class<?> propertyType = propertyTypeSpec.getCorrespondingClass();
@@ -304,7 +305,8 @@ implements MetaModelRefiner {
             }
 
             // else
-            validationFailures.add(
+            validator.onFailure(
+                    property,
                     property.getIdentifier(),
                     "JAXB view model '%s' property '%s' is of type '%s' but is not annotated with @XmlJavaTypeAdapter.  The field/method must be annotated with @XmlJavaTypeAdapter(org.apache.isis.schema.utils.jaxbadapters.XxxAdapter.ForJaxb.class) or equivalent, or be ignored by being annotated with @XmlTransient.",
                     objectSpec.getFullIdentifier(),
@@ -317,10 +319,11 @@ implements MetaModelRefiner {
         @Override
         void validate(
                 final ObjectSpecification objectSpec,
-                final ValidationFailures validationFailures) {
+                final MetaModelValidator validator) {
 
             if(objectSpec.isAbstract()) {
-                validationFailures.add(
+                validator.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "JAXB view model '%s' is abstract", 
                         objectSpec.getFullIdentifier());
@@ -332,21 +335,24 @@ implements MetaModelRefiner {
         @Override
         void validate(
                 final ObjectSpecification objectSpec,
-                final ValidationFailures validationFailures) {
+                final MetaModelValidator validator) {
 
             final Class<?> correspondingClass = objectSpec.getCorrespondingClass();
             if(correspondingClass.isAnonymousClass()) {
-                validationFailures.add(
+                validator.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "JAXB view model '%s' is an anonymous class", 
                         objectSpec.getFullIdentifier());
             } else if(correspondingClass.isLocalClass()) {
-                validationFailures.add(
+                validator.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "JAXB view model '%s' is a local class", 
                         objectSpec.getFullIdentifier());
             } else if(correspondingClass.isMemberClass() && !Modifier.isStatic(correspondingClass.getModifiers())) {
-                validationFailures.add(
+                validator.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "JAXB view model '%s' is an non-static inner class", 
                         objectSpec.getFullIdentifier());
@@ -358,22 +364,25 @@ implements MetaModelRefiner {
         @Override
         void validate(
                 final ObjectSpecification objectSpec,
-                final ValidationFailures validationFailures) {
+                final MetaModelValidator validator) {
 
             final Class<?> correspondingClass = objectSpec.getCorrespondingClass();
             final Constructor<?>[] constructors = correspondingClass.getDeclaredConstructors();
             for (Constructor<?> constructor : constructors) {
                 if(constructor.getParameterCount() == 0) {
                     if (!Modifier.isPublic(constructor.getModifiers())) {
-                        validationFailures
-                        .add(objectSpec.getIdentifier(),
+                        validator
+                        .onFailure(
+                                objectSpec,
+                                objectSpec.getIdentifier(),
                                 "JAXB view model '%s' has a no-arg constructor, however it is not public",
                                 objectSpec.getFullIdentifier());
                     }
                     return;
                 }
             }
-            validationFailures.add(
+            validator.onFailure(
+                    objectSpec,
                     objectSpec.getIdentifier(),
                     "JAXB view model '%s' does not have a public no-arg constructor", 
                     objectSpec.getFullIdentifier());
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java
index ea9b7d9..17a2f18 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java
@@ -33,6 +33,8 @@ import org.apache.isis.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorForValidationFailures;
 
+import lombok.val;
+
 
 public class ViewModelSemanticCheckingFacetFactory extends FacetFactoryAbstract
 implements MetaModelRefiner {
@@ -54,7 +56,8 @@ implements MetaModelRefiner {
             return;
         }
 
-        final Class<?> cls = processClassContext.getCls();
+        val cls = processClassContext.getCls();
+        val facetHolder = processClassContext.getFacetHolder();
 
         final DomainObjectLayout domainObjectLayout = Annotations.getAnnotation(cls, DomainObjectLayout.class);
         final ViewModelLayout viewModelLayout = Annotations.getAnnotation(cls, ViewModelLayout.class);
@@ -70,7 +73,8 @@ implements MetaModelRefiner {
         final boolean annotatedWithViewModel = viewModel != null;
 
         if(implementsViewModel && implementsRecreatableDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %s should not implement both %s and %s interfaces (implement one or the other)",
                     cls.getName(),
@@ -79,7 +83,8 @@ implements MetaModelRefiner {
 
         }
         if(implementsViewModel && annotatedWithDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not implement %2$s and be annotated with @%3$s (annotate with %4$s instead of %2$s, or implement %5s instead of %2$s)",
                     cls.getName(),
@@ -90,7 +95,8 @@ implements MetaModelRefiner {
                     );
         }
         if(implementsViewModel && annotatedWithDomainObjectLayout) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not implement %2$s and be annotated with @%3$s (annotate with @%4$s instead of %3$s, or implement %5$s instead of %2$s)",
                     cls.getName(),
@@ -102,7 +108,8 @@ implements MetaModelRefiner {
 
 
         if(annotatedWithViewModel && implementsRecreatableDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with @%2$s but implement @%3$s (implement %4$s instead of %3$s, or annotate with @%5$s with nature of %6s, %7s or %8s instead of annotating with @%2$s)",
                     cls.getName(),
@@ -116,7 +123,8 @@ implements MetaModelRefiner {
                     );
         }
         if(annotatedWithViewModel && annotatedWithDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with both @%2$s and @%3$s (annotate with one or the other)",
                     cls.getName(),
@@ -125,7 +133,8 @@ implements MetaModelRefiner {
 
         }
         if(annotatedWithViewModel && annotatedWithDomainObjectLayout) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with both @%2$s and @%3$s (annotate with @%4$s instead of @%3$s, or annotate with @%5$s instead of @%2$s)",
                     cls.getName(),
@@ -136,7 +145,8 @@ implements MetaModelRefiner {
         }
 
         if(annotatedWithViewModelLayout && implementsRecreatableDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with @%2$s but implement @%3$s (implement %4$s instead of %3$s, or annotate with %5$s instead of %2$s)",
                     cls.getName(),
@@ -146,7 +156,8 @@ implements MetaModelRefiner {
                     DomainObjectLayout.class.getSimpleName());
         }
         if(annotatedWithViewModelLayout && annotatedWithDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with @%2$s and also be annotated with @%3$s (annotate with @%4$s instead of @%3$s, or instead annotate with @%5$s instead of @%2$s)",
                     cls.getName(),
@@ -156,7 +167,8 @@ implements MetaModelRefiner {
                     DomainObjectLayout.class.getSimpleName());
         }
         if(annotatedWithViewModelLayout && annotatedWithDomainObjectLayout) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with both @%2$s and @%3$s (annotate with one or the other)",
                     cls.getName(),
@@ -168,7 +180,8 @@ implements MetaModelRefiner {
         if(     annotatedWithDomainObject &&
                 (domainObject.nature() == Nature.NOT_SPECIFIED || domainObject.nature() == Nature.JDO_ENTITY) &&
                 implementsRecreatableDomainObject) {
-            validator.addFailure(
+            validator.onFailure(
+                    facetHolder,
                     Identifier.classIdentifier(cls),
                     "Inconsistent view model / domain object semantics; %1$s should not be annotated with @%2$s with nature of %3$s and also implement %4$s (specify a nature of %5$s, %6$s or %7$s)",
                     cls.getName(),
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/bookmarkpolicy/bookmarkable/BookmarkPolicyFacetFallbackFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/bookmarkpolicy/bookmarkable/BookmarkPolicyFacetFallbackFactory.java
index 10e6693..1bcbe48 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/bookmarkpolicy/bookmarkable/BookmarkPolicyFacetFallbackFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/bookmarkpolicy/bookmarkable/BookmarkPolicyFacetFallbackFactory.java
@@ -56,7 +56,7 @@ implements MetaModelRefiner {
     @Override
     public void refineProgrammingModel(ProgrammingModel programmingModel) {
         
-        programmingModel.addValidator((objectSpec, validationFailures) -> {
+        programmingModel.addValidator((objectSpec, validator) -> {
 
             final Stream<ObjectAction> objectActions = objectSpec.streamObjectActions(Contributed.EXCLUDED);
 
@@ -72,7 +72,7 @@ implements MetaModelRefiner {
             .forEach(objectAction->{
                 final ActionSemanticsFacet semanticsFacet = objectAction.getFacet(ActionSemanticsFacet.class);
                 if(semanticsFacet == null || semanticsFacet.isNoop() || !semanticsFacet.value().isSafeInNature()) {
-                    validationFailures.add(
+                    validator.onFailure(objectAction,
                             objectAction.getIdentifier(),
                             "%s: action is bookmarkable but action semantics are not explicitly indicated as being safe.  " +
                                     "Either add @Action(semantics=SemanticsOf.SAFE) or @Action(semantics=SemanticsOf.SAFE_AND_REQUEST_CACHEABLE), or remove @ActionLayout(bookmarking=...).",
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
index 3fc4b7b..5d9c333 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
@@ -73,9 +73,9 @@ import org.apache.isis.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorForValidationFailures;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 import org.apache.isis.metamodel.util.EventUtil;
 
 import lombok.val;
@@ -199,7 +199,13 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
                 .map(domainObject -> new AnnotHelper(domainObject))
                 .filter(a -> a.autoCompleteRepository != Object.class)
                 .filter(a -> {
-                    a.repositoryMethod = findRepositoryMethod(cls, "@DomainObject", a.autoCompleteRepository, a.autoCompleteAction);
+                    a.repositoryMethod = findRepositoryMethod(
+                            facetHolder, 
+                            cls, 
+                            "@DomainObject", 
+                            a.autoCompleteRepository, 
+                            a.autoCompleteAction);
+                    
                     return a.repositoryMethod != null;
                 })
                 .map(a -> new AutoCompleteFacetForDomainObjectAnnotation(
@@ -208,10 +214,12 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
     }
 
     private Method findRepositoryMethod(
+            final FacetHolder facetHolder,
             final Class<?> cls,
             final String annotationName,
             final Class<?> repositoryClass,
             final String methodName) {
+        
         final Method[] methods = repositoryClass.getMethods();
         for (Method method : methods) {
             if(method.getName().equals(methodName)) {
@@ -221,7 +229,8 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
                 }
             }
         }
-        autoCompleteMethodInvalid.addFailure(
+        autoCompleteMethodInvalid.onFailure(
+                facetHolder,
                 Identifier.classIdentifier(cls),
                 "%s annotation on %s specifies method '%s' that does not exist in repository '%s'",
                 annotationName, cls.getName(), methodName, repositoryClass.getName());
@@ -298,7 +307,7 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
             val mixinDomainObjectIfAny =
                     domainObjectIfAny
                     .filter(domainObject -> domainObject.nature() == Nature.MIXIN)
-                    .filter(domainObject -> mixinTypeValidator.ensureMixinType(cls));
+                    .filter(domainObject -> mixinTypeValidator.ensureMixinType(facetHolder, cls));
 
             val mixinFacet = 
                     MixinFacetForDomainObjectAnnotation.create(mixinDomainObjectIfAny, cls, facetHolder, getServiceInjector());
@@ -486,13 +495,13 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
 
         programmingModel.addValidator(new MetaModelValidatorVisiting.Visitor() {
             @Override
-            public boolean visit(final ObjectSpecification thisSpec, final ValidationFailures validationFailures) {
+            public boolean visit(final ObjectSpecification thisSpec, final MetaModelValidator validator) {
 
-                validate(thisSpec, validationFailures);
+                validate(thisSpec, validator);
                 return true;
             }
 
-            private void validate(final ObjectSpecification thisSpec, final ValidationFailures validationFailures) {
+            private void validate(final ObjectSpecification thisSpec, final MetaModelValidator validator) {
                 if(!thisSpec.isEntityOrViewModel()) {
                     return;
                 }
@@ -516,7 +525,8 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
                     if (existingSpec == null) {
                         continue;
                     }
-                    validationFailures.add(
+                    validator.onFailure(
+                            existingSpec,
                             existingSpec.getIdentifier(),
                             "%s: cannot have two entities with same object type (@Discriminator, @DomainObject(objectType=...), @ObjectType or @PersistenceCapable(schema=...)); %s " +
                                     "has same value (%s).",
@@ -531,7 +541,8 @@ implements MetaModelRefiner, PostConstructMethodCache, ObjectSpecIdFacetFactory
                     final Class<?> repositoryClass = facet.getRepositoryClass();
                     final boolean isResolvableBean = getServiceRegistry().isResolvableBean(repositoryClass);
                     if(!isResolvableBean) {
-                        validationFailures.add(
+                        validator.onFailure(
+                                thisSpec,
                                 thisSpec.getIdentifier(),
                                 "@DomainObject annotation on %s specifies unknown repository '%s'",
                                 thisSpec.getFullIdentifier(), repositoryClass.getName());
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainservice/annotation/DomainServiceFacetAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainservice/annotation/DomainServiceFacetAnnotationFactory.java
index 3e6e65d..948bee5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainservice/annotation/DomainServiceFacetAnnotationFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/domainservice/annotation/DomainServiceFacetAnnotationFactory.java
@@ -34,9 +34,9 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorForValidationFailures;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -84,7 +84,7 @@ implements MetaModelRefiner {
                     natureOfService,
                     "'isis.reflector.validator.mixinsOnly'");
             
-            mixinOnlyValidator.addFailure(Identifier.classIdentifier(cls), msg);
+            mixinOnlyValidator.onFailure(facetHolder, Identifier.classIdentifier(cls), msg);
             break;
         default:
             // no op
@@ -100,14 +100,14 @@ implements MetaModelRefiner {
             programmingModel.addValidator(new MetaModelValidatorVisiting.Visitor() {
 
                 @Override
-                public boolean visit(final ObjectSpecification thisSpec, final ValidationFailures validationFailures) {
-                    validate(thisSpec, validationFailures);
+                public boolean visit(final ObjectSpecification thisSpec, final MetaModelValidator validator) {
+                    validate(thisSpec, validator);
                     return true;
                 }
 
                 private void validate(
                         final ObjectSpecification thisSpec,
-                        final ValidationFailures validationFailures) {
+                        final MetaModelValidator validator) {
 
                     if(!thisSpec.containsFacet(DomainServiceFacet.class)) {
                         return;
@@ -125,7 +125,8 @@ implements MetaModelRefiner {
                         return;
                     }
 
-                    validationFailures.add(
+                    validator.onFailure(
+                            thisSpec,
                             thisSpec.getIdentifier(),
                             "%s: services can only have actions ('%s' config property), not properties or collections; annotate with @Programmatic if required.  Found: %s",
                             thisSpec.getFullIdentifier(),
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MetaModelValidatorForMixinTypes.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MetaModelValidatorForMixinTypes.java
index 1f3deac..9d37775 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MetaModelValidatorForMixinTypes.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MetaModelValidatorForMixinTypes.java
@@ -20,8 +20,11 @@ package org.apache.isis.metamodel.facets.object.mixin;
 
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.commons.internal.reflection._Reflect;
+import org.apache.isis.metamodel.facetapi.FacetHolder;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorForValidationFailures;
 
+import lombok.NonNull;
+
 public class MetaModelValidatorForMixinTypes extends MetaModelValidatorForValidationFailures {
 
     private final String annotation;
@@ -30,13 +33,16 @@ public class MetaModelValidatorForMixinTypes extends MetaModelValidatorForValida
         this.annotation = annotation;
     }
 
-    public boolean ensureMixinType(final Class<?> candidateMixinType) {
+    public boolean ensureMixinType(
+            @NonNull FacetHolder facetHolder,
+            final Class<?> candidateMixinType) {
 
         if (_Reflect.hasPublic1ArgConstructor(candidateMixinType)) {
             return true;
         }
         
-        addFailure(
+        onFailure(
+                facetHolder,
                 Identifier.classIdentifier(candidateMixinType),
                 "%s: annotated with %s annotation but does not have a public 1-arg constructor",
                 candidateMixinType.getName(), 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MixinFacetForMixinAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MixinFacetForMixinAnnotationFactory.java
index b3c9d14..aa2c283 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MixinFacetForMixinAnnotationFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/mixin/MixinFacetForMixinAnnotationFactory.java
@@ -45,11 +45,11 @@ extends FacetFactoryAbstract implements MetaModelRefiner {
             return;
         }
 
+        val facetHolder = processClassContext.getFacetHolder();
         val candidateMixinType = processClassContext.getCls();
-        if (!mixinTypeValidator.ensureMixinType(candidateMixinType)) {
+        if (!mixinTypeValidator.ensureMixinType(facetHolder, candidateMixinType)) {
             return;
         }
-        val facetHolder = processClassContext.getFacetHolder();
         
         val mixinFacet = MixinFacetForMixinAnnotation
                 .create(mixinIfAny.get(), candidateMixinType, facetHolder, getServiceInjector());
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
index 85ebdf7..f5d6adc 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
@@ -106,7 +106,7 @@ implements MetaModelRefiner {
     @Override
     public void refineProgrammingModel(ProgrammingModel programmingModel) {
 
-        programmingModel.addValidator((objectSpec, validationFailures) -> {
+        programmingModel.addValidator((objectSpec, validate) -> {
 
 
             final Class<?> cls = objectSpec.getCorrespondingClass();
@@ -119,7 +119,8 @@ implements MetaModelRefiner {
                 return true; // no conflict, continue validation processing
             } else if (evaluators.size()>1) {
 
-                validationFailures.add(
+                validate.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "%s: conflict for determining a strategy for retrieval of (navigable) parent for class, "
                                 + "contains multiple annotations '@%s' having navigable=PARENT, while at most one is allowed.",
@@ -139,7 +140,8 @@ implements MetaModelRefiner {
 
                 if(!fieldEvaluator.getGetter(cls).isPresent()) {
 
-                    validationFailures.add(
+                    validate.onFailure(
+                            objectSpec,
                             objectSpec.getIdentifier(),
                             "%s: unable to determine a strategy for retrieval of (navigable) parent for class, "
                                     + "field '%s' annotated with '@%s' having navigable=PARENT does not provide a getter.",
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java
index 0128874..2f4b275 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java
@@ -38,8 +38,8 @@ import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.metamodel.specloader.classsubstitutor.ClassSubstitutor;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -110,20 +110,23 @@ implements MetaModelRefiner, ObjectSpecIdFacetFactory {
                 @Override
                 public boolean visit(
                         ObjectSpecification objectSpec,
-                        ValidationFailures validationFailures) {
-                    validate(objectSpec, validationFailures);
+                        MetaModelValidator validator) {
+                    
+                    validate(objectSpec, validator);
                     return true;
                 }
     
                 private void validate(
                         ObjectSpecification objectSpec,
-                        ValidationFailures validationFailures) {
+                        MetaModelValidator validator) {
+                    
                     if(skip(objectSpec)) {
                         return;
                     }
                     val objectSpecIdFacet = objectSpec.getFacet(ObjectSpecIdFacet.class);
                     if(objectSpecIdFacet instanceof ObjectSpecIdFacetDerivedFromClassName) {
-                        validationFailures.add(
+                        validator.onFailure(
+                                objectSpec,
                                 objectSpec.getIdentifier(),
                                 "%s: the object type must be specified explicitly ('%s' config property). "
                                         + "Defaulting the object type from the package/class/package name can lead "
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/recreatable/RecreatableObjectFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/recreatable/RecreatableObjectFacetFactory.java
index 412fa59..fb80a32 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/recreatable/RecreatableObjectFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/recreatable/RecreatableObjectFacetFactory.java
@@ -97,12 +97,13 @@ implements MetaModelRefiner, PostConstructMethodCache {
     @Override
     public void refineProgrammingModel(ProgrammingModel programmingModel) {
 
-        programmingModel.addValidator((objectSpec, validationFailures) -> {
+        programmingModel.addValidator((objectSpec, validate) -> {
 
             final ViewModelFacet facet = objectSpec.getFacet(ViewModelFacet.class);
             final Facet underlyingFacet = facet != null ? facet.getUnderlyingFacet() : null;
             if(underlyingFacet != null && underlyingFacet.getClass() != facet.getClass()) {
-                validationFailures.add(
+                validate.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "%s: has multiple incompatible annotations/interfaces indicating that " +
                                 "it is a recreatable object of some sort (%s and %s)",
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
index 531dd7c..5aa2e59 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
@@ -149,7 +149,7 @@ implements MetaModelRefiner {
     @Override
     public void refineProgrammingModel(ProgrammingModel programmingModel) {
 
-        programmingModel.addValidator((objectSpec, validationFailures) -> {
+        programmingModel.addValidator((objectSpec, validate) -> {
 
             final Class<?> cls = objectSpec.getCorrespondingClass();
 
@@ -167,7 +167,8 @@ implements MetaModelRefiner {
             final List<Method> methods = methodsWithTitleAnnotation(cls);
             final List<Method> superClassMethods = methodsWithTitleAnnotation(supClass);
             if (methods.size() > superClassMethods.size()) {
-                validationFailures.add(
+                validate.onFailure(
+                        objectSpec,
                         objectSpec.getIdentifier(),
                         "%s: conflict for determining a strategy for retrieval of title for class, contains a method '%s' and an annotation '@%s'",
                         objectSpec.getIdentifier().getClassName(),
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/maxlength/MaxLengthFacetForMaxLengthAnnotationOnProperty.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/maxlength/MaxLengthFacetForMaxLengthAnnotationOnProperty.java
deleted file mode 100644
index 500db0f..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/maxlength/MaxLengthFacetForMaxLengthAnnotationOnProperty.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.metamodel.facets.properties.property.maxlength;
-
-import org.apache.isis.metamodel.facetapi.FacetHolder;
-import org.apache.isis.metamodel.facets.objectvalue.maxlen.MaxLengthFacetAbstract;
-
-/**
- * @deprecated
- */
-@Deprecated
-public class MaxLengthFacetForMaxLengthAnnotationOnProperty extends MaxLengthFacetAbstract {
-
-    public MaxLengthFacetForMaxLengthAnnotationOnProperty(final int value, final FacetHolder holder) {
-        super(value, holder);
-    }
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelService.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelService.java
index 4dbbe8a..1c0cd8c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelService.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelService.java
@@ -18,14 +18,15 @@
  */
 package org.apache.isis.metamodel.progmodel;
 
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
-
 /**
  * @since 2.0
  */
 public interface ProgrammingModelService {
 
+    /**
+     * 
+     * @apiNote don't call this during '@PostConstruct' phase
+     */
     ProgrammingModel getProgrammingModel();
-    ValidationFailures getValidationResult();
     
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java
index 7bc3741..7e2145e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java
@@ -29,7 +29,6 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.progmodel.ProgrammingModelInitFilter;
 import org.apache.isis.metamodel.progmodel.ProgrammingModelService;
 import org.apache.isis.metamodel.progmodels.dflt.ProgrammingModelFacetsJava8;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
@@ -42,11 +41,6 @@ public class ProgrammingModelServiceDefault implements ProgrammingModelService {
         return programmingModel.get();
     }
     
-    @Override
-    public ValidationFailures getValidationResult() {
-        return validationResult.get();
-    }
-
     // -- HELPER
 
     @Inject private ServiceRegistry serviceRegistry;
@@ -55,9 +49,6 @@ public class ProgrammingModelServiceDefault implements ProgrammingModelService {
     private _Lazy<ProgrammingModel> programmingModel = 
             _Lazy.threadSafe(this::createProgrammingModel);
 
-    private _Lazy<ValidationFailures> validationResult = 
-            _Lazy.threadSafe(this::validate);
-    
     private ProgrammingModel createProgrammingModel() {
         
         log.info("About to create the ProgrammingModel.");
@@ -95,13 +86,5 @@ public class ProgrammingModelServiceDefault implements ProgrammingModelService {
         
         return programmingModel;
     }
-    
-    private ValidationFailures validate() {
-        val failures = new ValidationFailures();
-        programmingModel.get().streamValidators()
-        .forEach(validator->validator.validateInto(failures));
-        return failures;
-    }
-
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
index 867f8f4..6a90caa 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
@@ -28,6 +28,8 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.specloader.specimpl.IntrospectionState;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
+import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -48,7 +50,14 @@ public interface SpecificationLoader {
      */
     void disposeMetaModel();
     
-    void validateMetaModel();
+    /**
+     * Returns the collected results of the various {@link MetaModelValidator}s configured with 
+     * the {@link ProgrammingModel}.
+     * 
+     * @apiNote Some of the {@link MetaModelValidator}s run during {@link #createMetaModel()}, 
+     * others are only triggered when calling this method. 
+     */
+    ValidationFailures getValidationResult();
 
     // -- LOOKUP
 
@@ -107,8 +116,6 @@ public interface SpecificationLoader {
         return loadSpecification(className, null);
     }
 
-    
-
 
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
index 243eff7..92555ac 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
@@ -29,6 +29,8 @@ import javax.inject.Inject;
 
 import org.springframework.stereotype.Service;
 
+import org.apache.isis.commons.internal.base._Blackhole;
+import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.commons.internal.base._Timing;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.environment.IsisSystemEnvironment;
@@ -48,6 +50,7 @@ import org.apache.isis.metamodel.specloader.specimpl.FacetedMethodsBuilderContex
 import org.apache.isis.metamodel.specloader.specimpl.IntrospectionState;
 import org.apache.isis.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
 import org.apache.isis.metamodel.specloader.specimpl.standalonelist.ObjectSpecificationOnStandaloneList;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorAbstract;
 import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 import org.apache.isis.schema.utils.CommonDtoUtils;
 
@@ -122,7 +125,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     @Override
     public void createMetaModel() {
         
-        log.info("About to creating the Metamodel");
+        log.info("About to create the Metamodel ...");
         
         // initialize subcomponents, only after @PostConstruct has globally completed
         facetProcessor.init();
@@ -193,6 +196,9 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             log.info("Introspecting all cached specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
             introspect(cachedSpecifications, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
         }
+        
+        log.info("Running remaining validators");
+        _Blackhole.consume(getValidationResult()); // as a side effect memoizes the validation result
 
         stopWatch.stop();
         log.info("createMetaModel() - took " + stopWatch);
@@ -200,16 +206,25 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     }
     
     @Override
-    public void validateMetaModel() {
+    public ValidationFailures getValidationResult() {
+        return validationResult.get();
+    }
+
+    private _Lazy<ValidationFailures> validationResult = 
+            _Lazy.threadSafe(this::collectFailuresFromMetaModel);
+
+    private ValidationFailures collectFailuresFromMetaModel() {
         val failures = new ValidationFailures();
         programmingModel.streamValidators()
-        .forEach(validator->validator.validateInto(failures));
-       // return failures;
+        .map(MetaModelValidatorAbstract.class::cast)
+        .forEach(validator->validator.collectFailuresInto(failures));
+        return failures;
     }
 
     @Override
     public void disposeMetaModel() {
         cache.clear();
+        validationResult.clear();
         log.info("Metamodel disposed.");
     }
     
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidator.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidator.java
index 547cc6f..e0f1366 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidator.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidator.java
@@ -19,20 +19,26 @@
 
 package org.apache.isis.metamodel.specloader.validator;
 
-import java.util.function.Consumer;
-
+import org.apache.isis.applib.Identifier;
 import org.apache.isis.metamodel.facetapi.FacetHolder;
+import org.apache.isis.metamodel.facets.all.deficiencies.DeficiencyFacet;
+
+import lombok.NonNull;
+import lombok.val;
 
 public interface MetaModelValidator {
 
-    /**
-     * Validate and then append any {@link ValidationFailure} to given validationFailures. 
-     *  
-     * @param validationFailures
-     */
-    //@Deprecated
-    void validateInto(ValidationFailures validationFailures);
-    
-    //void validateInto(FacetHolder holder, Consumer<ValidationFailure> onFailure);
+    default void onFailure(
+            @NonNull FacetHolder facetHolder,
+            @NonNull Identifier deficiencyOrigin,
+            @NonNull String deficiencyMessageFormat,
+            Object ...args) {
+        
+        val deficiencyMessage = String.format(deficiencyMessageFormat, args);
+        
+        DeficiencyFacet.appendTo(facetHolder, deficiencyOrigin, deficiencyMessage);
+    }
+
+
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorAbstract.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorAbstract.java
index 5e11eec..80ac727 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorAbstract.java
@@ -19,7 +19,11 @@
 
 package org.apache.isis.metamodel.specloader.validator;
 
+import org.apache.isis.applib.Identifier;
 import org.apache.isis.metamodel.MetaModelContext;
+import org.apache.isis.metamodel.facetapi.FacetHolder;
+
+import lombok.NonNull;
 
 public abstract class MetaModelValidatorAbstract 
 implements MetaModelValidator, MetaModelContext.Delegating {
@@ -28,5 +32,27 @@ implements MetaModelValidator, MetaModelContext.Delegating {
     public MetaModelContext getMetaModelContext() {
         return MetaModelContext.current();
     }
+    
+    protected final ValidationFailures failures = new ValidationFailures();
+    
+    /**
+     * Collect any {@link ValidationFailure} to given validationFailures. 
+     *  
+     * @param validationFailures
+     */
+    public void collectFailuresInto(@NonNull ValidationFailures validationFailures) {
+        validationFailures.addAll(failures);
+    }
+
+    @Override
+    public void onFailure(
+            @NonNull FacetHolder facetHolder, 
+            @NonNull Identifier deficiencyOrigin,
+            @NonNull String deficiencyMessageFormat, 
+            Object... args) {
+        
+        MetaModelValidator.super.onFailure(facetHolder, deficiencyOrigin, deficiencyMessageFormat, args);
+        failures.add(deficiencyOrigin, deficiencyMessageFormat, args);
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorComposite.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorComposite.java
deleted file mode 100644
index e81b208..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorComposite.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.metamodel.specloader.validator;
-
-import java.util.List;
-
-import org.apache.isis.commons.internal.collections._Lists;
-
-import lombok.val;
-
-@Deprecated
-final class MetaModelValidatorComposite extends MetaModelValidatorAbstract {
-
-    private final List<MetaModelValidator> validators = _Lists.newArrayList();
-
-    public MetaModelValidatorComposite add(MetaModelValidator validator) {
-        
-        if(validator instanceof MetaModelValidatorComposite) {
-            // flatten the structure ... don't allow composites to contain composites, 
-            // such that the size() operation is a simple one
-            val composite = (MetaModelValidatorComposite) validator;
-            validators.addAll(composite.validators);
-        } else {
-            validators.add(validator);    
-        }
-        return this;
-    }
-
-    public MetaModelValidatorComposite addAll(MetaModelValidator... validators) {
-        for (val validator : validators) {
-            add(validator);
-        }
-        return this;
-    }
-
-    @Override
-    public void validateInto(ValidationFailures validationFailures)  {
-        for (val validator : validators) {
-            validator.validateInto(validationFailures);
-        }
-    }
-
-    public static MetaModelValidatorComposite asComposite(MetaModelValidator baseMetaModelValidator) {
-        val metaModelValidatorComposite = new MetaModelValidatorComposite();
-        metaModelValidatorComposite.add(baseMetaModelValidator);
-        return metaModelValidatorComposite;
-    }
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java
index 66e077d..753f802 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java
@@ -27,28 +27,21 @@ import lombok.val;
 
 public class MetaModelValidatorForConflictingOptionality extends MetaModelValidatorAbstract {
 
-    private final ValidationFailures failures = new ValidationFailures();
-
     public MetaModelValidatorForConflictingOptionality() {
     }
-
-    @Override
-    public void validateInto(final ValidationFailures validationFailures) {
-        validationFailures.addAll(failures);
+    
+    public Facet flagIfConflict(final MandatoryFacet facet, final String message) {
+        if(conflictingOptionality(facet)) {
+            addFailure(facet, message);
+        }
+        return facet;
     }
 
     private Facet addFailure(final Facet facet, final String message) {
         if(facet != null) {
             val holder = (IdentifiedHolder) facet.getFacetHolder();
             val identifier = holder.getIdentifier();
-            failures.add(identifier, message + ": " + identifier.toFullIdentityString());
-        }
-        return facet;
-    }
-
-    public Facet flagIfConflict(final MandatoryFacet facet, final String message) {
-        if(conflictingOptionality(facet)) {
-            addFailure(facet, message);
+            super.onFailure(holder, identifier, "%s : %s", message, identifier.toFullIdentityString());
         }
         return facet;
     }
@@ -67,4 +60,6 @@ public class MetaModelValidatorForConflictingOptionality extends MetaModelValida
     }
 
 
+
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedAbstract.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedAbstract.java
index 0511d6a..1953a7f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedAbstract.java
@@ -26,8 +26,6 @@ import lombok.val;
 
 public abstract class MetaModelValidatorForDeprecatedAbstract extends MetaModelValidatorAbstract {
 
-    private final ValidationFailures failures = new ValidationFailures();
-
     /**
      * @param facet
      */
@@ -40,10 +38,12 @@ public abstract class MetaModelValidatorForDeprecatedAbstract extends MetaModelV
      * @param processMethodContext - can be null if none available.
      */
     public <T extends Facet> T flagIfPresent(T facet, FacetFactory.AbstractProcessWithMethodContext<?> processMethodContext) {
-        if(facet != null) {
+        if(facet != null && 
+                !getConfiguration().getReflector().getValidator().isAllowDeprecated()) {
+            
             val holder = (IdentifiedHolder) facet.getFacetHolder();
             val identifier = holder.getIdentifier();
-            failures.add(identifier, failureMessageFor(facet, processMethodContext));
+            super.onFailure(holder, identifier, failureMessageFor(facet, processMethodContext));
         }
         return facet;
     }
@@ -62,13 +62,6 @@ public abstract class MetaModelValidatorForDeprecatedAbstract extends MetaModelV
 
     protected abstract String failureMessageFor(Facet facet, FacetFactory.AbstractProcessWithMethodContext<?> processMethodContext);
 
-    @Override
-    public void validateInto(ValidationFailures validationFailures) {
-
-        if(getConfiguration().getReflector().getValidator().isAllowDeprecated()) {
-            return;
-        }
-        validationFailures.addAll(failures);
-    }
+  
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedMethodPrefix.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedMethodPrefix.java
index d8acc84..9d83cdb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedMethodPrefix.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForDeprecatedMethodPrefix.java
@@ -32,7 +32,9 @@ public class MetaModelValidatorForDeprecatedMethodPrefix extends MetaModelValida
     }
 
     @Override
-    protected String failureMessageFor(final Facet facet, final FacetFactory.AbstractProcessWithMethodContext processMethodContext) {
+    protected String failureMessageFor(
+            final Facet facet, 
+            final FacetFactory.AbstractProcessWithMethodContext<?> processMethodContext) {
 
         final boolean inherited = isInherited(processMethodContext);
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForValidationFailures.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForValidationFailures.java
index f53626a..67febcd 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForValidationFailures.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorForValidationFailures.java
@@ -27,19 +27,12 @@ import lombok.val;
 
 public class MetaModelValidatorForValidationFailures extends MetaModelValidatorAbstract {
 
-    private final ValidationFailures failures = new ValidationFailures();
-
     public MetaModelValidatorForValidationFailures() {
     }
 
-    @Override
-    public void validateInto(ValidationFailures validationFailures) {
-        validationFailures.addAll(failures);
-    }
-
-    public void addFailure(Identifier identifier, String pattern, Object... arguments) {
-        failures.add(identifier, pattern, arguments);
-    }
+//    public void addFailure(Identifier identifier, String pattern, Object... arguments) {
+//        failures.add(identifier, pattern, arguments);
+//    }
 
 //    public Facet addFailure(Facet facet, String message) {
 //        if(facet != null) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorVisiting.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorVisiting.java
index 48d19c8..3d752f2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorVisiting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/validator/MetaModelValidatorVisiting.java
@@ -27,6 +27,7 @@ import org.apache.isis.metamodel.spec.ObjectSpecification;
 
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
+import lombok.val;
 
 @RequiredArgsConstructor(staticName = "of")
 public class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
@@ -36,41 +37,39 @@ public class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
         /**
          * @return <tt>true</tt> continue visiting specs.
          */
-        boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures);
+        boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator);
     }
     
     public interface SummarizingVisitor extends Visitor {
-        void summarize(ValidationFailures validationFailures);
+        void summarize(MetaModelValidator validator);
     }
     
     @NonNull private final Visitor visitor;
 
     @Override
-    public final void validateInto(ValidationFailures validationFailures) {
-
-        validateAll(validationFailures);
-
-        summarize(validationFailures);
+    public void collectFailuresInto(@NonNull ValidationFailures validationFailures) {
+        validateAll();
+        summarize();
+        super.collectFailuresInto(validationFailures);
     }
 
-    private void validateAll(final ValidationFailures validationFailures) {
+    private void validateAll() {
 
-        final List<ObjectSpecification> specsValidated = _Lists.newArrayList();
+        val specsValidated = _Lists.<ObjectSpecification>newArrayList();
 
-        while(validateSpecs(specsValidated, validationFailures)) {
+        while(validateSpecs(specsValidated)) {
             // validate in a loop, because the act of validating might cause additional specs to be uncovered
         }
 
     }
 
-    private boolean validateSpecs(
-            final List<ObjectSpecification> specsAlreadyValidated,
-            final ValidationFailures validationFailures) {
+    private boolean validateSpecs(List<ObjectSpecification> specsAlreadyValidated) {
 
         // all currently known specs
         // (previously we took a protective copy to avoid a concurrent modification exception,
         // but this is now done by SpecificationLoader itself)
-        final Collection<ObjectSpecification> specsToValidate = getSpecificationLoader().snapshotSpecifications();
+        final Collection<ObjectSpecification> specsToValidate = 
+                getSpecificationLoader().snapshotSpecifications();
 
         // don't validate any specs already processed
         specsToValidate.removeAll(specsAlreadyValidated);
@@ -80,8 +79,8 @@ public class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
         }
 
         // validate anything new
-        for (final ObjectSpecification objSpec : specsToValidate) {
-            if(!visitor.visit(objSpec, validationFailures)) {
+        for (val objSpec : specsToValidate) {
+            if(!visitor.visit(objSpec, this)) {
                 break;
             }
         }
@@ -93,10 +92,10 @@ public class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
         return true;
     }
 
-    private void summarize(final ValidationFailures validationFailures) {
+    private void summarize() {
         if(visitor instanceof SummarizingVisitor) {
             SummarizingVisitor summarizingVisitor = (SummarizingVisitor) visitor;
-            summarizingVisitor.summarize(validationFailures);
+            summarizingVisitor.summarize(this);
         }
     }
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java
index e63afb2..573a1ee 100644
--- a/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java
@@ -30,9 +30,11 @@ import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.config.IsisConfiguration;
 import org.apache.isis.config.internal._Config;
 import org.apache.isis.metamodel.MetaModelContext;
+import org.apache.isis.metamodel.facetapi.FacetHolderImpl;
 import org.apache.isis.metamodel.facets.FacetFactory;
 import org.apache.isis.metamodel.progmodel.ProgrammingModelAbstract;
 import org.apache.isis.metamodel.progmodel.ProgrammingModelInitFilterDefault;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorAbstract;
 import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 import org.apache.isis.unittestsupport.jmocking.JUnitRuleMockery2;
 
@@ -59,12 +61,12 @@ public class ViewModelSemanticCheckingFacetFactoryTest {
         facetFactory.refineProgrammingModel(programmingModel);
         programmingModel.init(new ProgrammingModelInitFilterDefault());
         
-        facetFactory.process(new FacetFactory.ProcessClassContext(cls, null, null));
+        facetFactory.process(new FacetFactory.ProcessClassContext(cls, null, new FacetHolderImpl()));
         
         val validationFailures = new ValidationFailures();
         
         programmingModel.streamValidators()
-        .forEach(validator->validator.validateInto(validationFailures));
+        .forEach(validator->((MetaModelValidatorAbstract)validator).collectFailuresInto(validationFailures));
 
         return validationFailures;
     }
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/JdoProgrammingModelPlugin.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/JdoProgrammingModelPlugin.java
index a54bb07..b714d1e 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/JdoProgrammingModelPlugin.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/JdoProgrammingModelPlugin.java
@@ -27,9 +27,10 @@ import javax.jdo.annotations.IdentityType;
 
 import org.springframework.stereotype.Component;
 
-import org.apache.isis.applib.Identifier;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.commons.internal.collections._Multimaps;
+import org.apache.isis.commons.internal.collections._Multimaps.ListMultimap;
 import org.apache.isis.config.IsisConfiguration;
 import org.apache.isis.jdo.metamodel.facets.object.datastoreidentity.JdoDatastoreIdentityAnnotationFacetFactory;
 import org.apache.isis.jdo.metamodel.facets.object.discriminator.JdoDiscriminatorAnnotationFacetFactory;
@@ -53,8 +54,8 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.progmodel.ProgrammingModel.Marker;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import static org.apache.isis.commons.internal.base._NullSafe.stream;
 
@@ -121,7 +122,7 @@ public class JdoProgrammingModelPlugin implements MetaModelRefiner {
 
     private void addValidatorToEnsureIdentityType() {
 
-        pm.addValidator((objSpec, validationFailures) -> {
+        pm.addValidator((objSpec, validation) -> {
 
             final JdoPersistenceCapableFacet jpcf = objSpec.getFacet(JdoPersistenceCapableFacet.class);
             if(jpcf == null) {
@@ -141,7 +142,8 @@ public class JdoProgrammingModelPlugin implements MetaModelRefiner {
             } else {
                 // in fact, at the time of writing there are no others, so this is theoretical in case there is
                 // a future change to the JDO spec
-                validationFailures.add(
+                validation.onFailure(
+                        objSpec,
                         objSpec.getIdentifier(),
                         "%s: is annotated with @PersistenceCapable but with an unrecognized identityType (%s)",
                         objSpec.getFullIdentifier(),
@@ -155,9 +157,10 @@ public class JdoProgrammingModelPlugin implements MetaModelRefiner {
 
     private void addValidatorToCheckForUnsupportedAnnotations() {
 
-        pm.addValidator((objSpec, validationFailures) -> {
+        pm.addValidator((objSpec, validation) -> {
             if (objSpec.containsDoOpFacet(ParentedCollectionFacet.class) && !objSpec.containsDoOpFacet(CollectionFacet.class)) {
-                validationFailures.add(
+                validation.onFailure(
+                        objSpec,
                         objSpec.getIdentifier(),
                         "%s: DataNucleus object store currently does not supported Aggregated or EmbeddedOnly annotations",
                         objSpec.getFullIdentifier());
@@ -169,34 +172,37 @@ public class JdoProgrammingModelPlugin implements MetaModelRefiner {
 
     private void addValidatorToEnsureUniqueObjectIds() {
 
-        final Map<ObjectSpecId, List<ObjectSpecification>> specsById = _Maps.newHashMap();
+        final ListMultimap<ObjectSpecId, ObjectSpecification> collidingSpecsById = 
+                _Multimaps.newListMultimap();
 
-        MetaModelValidatorVisiting.SummarizingVisitor ensureUniqueObjectIds = new MetaModelValidatorVisiting.SummarizingVisitor(){
+        final MetaModelValidatorVisiting.SummarizingVisitor ensureUniqueObjectIds = 
+                new MetaModelValidatorVisiting.SummarizingVisitor(){
 
             @Override
-            public boolean visit(ObjectSpecification objSpec, ValidationFailures validationFailures) {
-                ObjectSpecId specId = objSpec.getSpecId();
-                List<ObjectSpecification> objectSpecifications = specsById.get(specId);
-                if(objectSpecifications == null) {
-                    objectSpecifications = _Lists.newArrayList();
-                    specsById.put(specId, objectSpecifications);
-                }
-                objectSpecifications.add(objSpec);
+            public boolean visit(ObjectSpecification objSpec, MetaModelValidator validator) {
+                val specId = objSpec.getSpecId();
+                collidingSpecsById.putElement(specId, objSpec);
                 return true;
             }
 
             @Override
-            public void summarize(final ValidationFailures validationFailures) {
-                for (final ObjectSpecId specId : specsById.keySet()) {
-                    val specList = specsById.get(specId);
-                    int numSpecs = specList.size();
-                    if(numSpecs > 1) {
-                        val csv = asCsv(specList);
-                        validationFailures.add(
-                                Identifier.classIdentifier(specId.asString()),
-                                "Object type '%s' mapped to multiple classes: %s", 
-                                specId.asString(), 
-                                csv);
+            public void summarize(MetaModelValidator validator) {
+                for (val specId : collidingSpecsById.keySet()) {
+                    val collidingSpecs = collidingSpecsById.get(specId);
+                    val isCollision = collidingSpecs.size()>1;
+                    if(isCollision) {
+                        val csv = asCsv(collidingSpecs);
+                        
+                        collidingSpecs.forEach(spec->{
+                            validator.onFailure(
+                                    spec,
+                                    spec.getIdentifier(),
+                                    "Object type '%s' mapped to multiple classes: %s", 
+                                    specId.asString(), 
+                                    csv);    
+                        });
+                        
+                        
                     }
                 }
             }
@@ -219,7 +225,7 @@ public class JdoProgrammingModelPlugin implements MetaModelRefiner {
         MetaModelValidatorVisiting.SummarizingVisitor visitor = new MetaModelValidatorVisiting.SummarizingVisitor(){
 
             @Override
-            public boolean visit(ObjectSpecification objSpec, ValidationFailures validationFailures) {
+            public boolean visit(ObjectSpecification objSpec, MetaModelValidator validator) {
                 Class<?> correspondingClass = objSpec.getCorrespondingClass();
                 if(correspondingClass == null) {
                     return true;
@@ -248,7 +254,7 @@ public class JdoProgrammingModelPlugin implements MetaModelRefiner {
             }
 
             @Override
-            public void summarize(final ValidationFailures validationFailures) {
+            public void summarize(final MetaModelValidator validator) {
                 //FIXME[2112] module (legacy) specific, remove?
                 //                final Set<String> modulePackageNames = modulePackageNamesFrom(appManifest);
                 //
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForClauseAbstract.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForClauseAbstract.java
index 6f32389..95873bc 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForClauseAbstract.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForClauseAbstract.java
@@ -23,8 +23,8 @@ import java.util.List;
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Visitor {
 
@@ -34,6 +34,7 @@ abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Vi
     VisitorForClauseAbstract(
             final JdoQueryAnnotationFacetFactory facetFactory,
             final String clause) {
+        
         this.facetFactory = facetFactory;
         this.clause = clause;
     }
@@ -41,14 +42,16 @@ abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Vi
     @Override
     public boolean visit(
             final ObjectSpecification objectSpec,
-            final ValidationFailures validationFailures) {
-        validate(objectSpec, validationFailures);
+            final MetaModelValidator validator) {
+        
+        validate(objectSpec, validator);
         return true;
     }
 
     private void validate(
             final ObjectSpecification objectSpec,
-            final ValidationFailures validationFailures) {
+            final MetaModelValidator validator) {
+        
         final JdoQueryFacet facet = objectSpec.getFacet(JdoQueryFacet.class);
         if(facet == null) {
             return;
@@ -58,7 +61,7 @@ abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Vi
             if(namedQuery.getLanguage().equals("JDOQL")) {
                 final String query = namedQuery.getQuery();
                 final String fromClassName = deriveClause(query);
-                interpretJdoql(fromClassName, objectSpec, query, validationFailures);
+                interpretJdoql(fromClassName, objectSpec, query, validator);
             }
         }
     }
@@ -67,7 +70,7 @@ abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Vi
             final String classNameFromClause,
             final ObjectSpecification objectSpec,
             final String query,
-            final ValidationFailures validationFailures) {
+            final MetaModelValidator validator) {
 
         if (classNameFromClause == null) {
             return;
@@ -75,14 +78,15 @@ abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Vi
 
         final String className = objectSpec.getCorrespondingClass().getName();
         if (getSpecificationLoader().loadSpecification(classNameFromClause)==null) {
-            validationFailures.add(
+            validator.onFailure(
+                    objectSpec,
                     Identifier.classIdentifier(className),
                     "%s: error in JDOQL query, class name for '%s' clause not recognized (JDOQL : %s)",
                     className, clause, query);
             return;
         }
 
-        postInterpretJdoql(classNameFromClause, objectSpec, query, validationFailures);
+        postInterpretJdoql(classNameFromClause, objectSpec, query, validator);
     }
 
     abstract String deriveClause(final String query);
@@ -91,7 +95,7 @@ abstract class VisitorForClauseAbstract implements MetaModelValidatorVisiting.Vi
             final String classNameFromClause,
             final ObjectSpecification objectSpec,
             final String query,
-            final ValidationFailures validationFailures);
+            final MetaModelValidator validator);
 
 
     SpecificationLoader getSpecificationLoader() {
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForFromClause.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForFromClause.java
index ffe3eb3..92df70a 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForFromClause.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForFromClause.java
@@ -23,7 +23,7 @@ import java.util.Objects;
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.metamodel.spec.Hierarchical;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 
 import lombok.val;
 
@@ -44,7 +44,7 @@ class VisitorForFromClause extends VisitorForClauseAbstract {
             final String classNameFromClause,
             final ObjectSpecification objectSpec,
             final String query,
-            final ValidationFailures validationFailures) {
+            final MetaModelValidator validator) {
 
         val className = objectSpec.getCorrespondingClass().getName();
         if (Objects.equals(classNameFromClause, className)) {
@@ -55,7 +55,8 @@ class VisitorForFromClause extends VisitorForClauseAbstract {
         if(subclasses.contains(objectSpec)) {
             return;
         }
-        validationFailures.add(
+        validator.onFailure(
+                objectSpec,
                 Identifier.classIdentifier(className),
                 "%s: error in JDOQL query, class name after '%s' clause should be same as class name on which annotated, or one of its supertypes (JDOQL : %s)",
                 className, clause, query);
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForVariablesClause.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForVariablesClause.java
index 24118ec..5a608c5 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForVariablesClause.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/query/VisitorForVariablesClause.java
@@ -22,7 +22,7 @@ import org.apache.isis.applib.Identifier;
 import org.apache.isis.jdo.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.specloader.specimpl.IntrospectionState;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 
 class VisitorForVariablesClause extends VisitorForClauseAbstract {
 
@@ -40,7 +40,7 @@ class VisitorForVariablesClause extends VisitorForClauseAbstract {
             final String classNameFromClause,
             final ObjectSpecification objectSpec,
             final String query,
-            final ValidationFailures validationFailures) {
+            final MetaModelValidator validator) {
 
 
         final String className = objectSpec.getCorrespondingClass().getName();
@@ -51,7 +51,8 @@ class VisitorForVariablesClause extends VisitorForClauseAbstract {
                 objectSpecification.getFacet(JdoPersistenceCapableFacet.class);
 
         if(persistenceCapableFacet == null) {
-            validationFailures.add(
+            validator.onFailure(
+                    objectSpec,
                     Identifier.classIdentifier(className),
                     "%s: error in JDOQL query, class name for '%s' clause is not annotated as @PersistenceCapable (JDOQL : %s)",
                     className, clause, query);
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactory.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactory.java
index fb5a804..5435c25 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactory.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactory.java
@@ -29,9 +29,9 @@ import org.apache.isis.metamodel.facets.Annotations;
 import org.apache.isis.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 public class JdoVersionAnnotationFacetFactory extends FacetFactoryAbstract
 implements MetaModelRefiner {
@@ -66,12 +66,12 @@ implements MetaModelRefiner {
         return new MetaModelValidatorVisiting.Visitor() {
 
             @Override
-            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
-                validate(objectSpec, validationFailures);
+            public boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator) {
+                validate(objectSpec, validator);
                 return true;
             }
 
-            private void validate(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+            private void validate(ObjectSpecification objectSpec, MetaModelValidator validator) {
                 if(!declaresVersionAnnotation(objectSpec)) {
                     return;
                 }
@@ -79,7 +79,8 @@ implements MetaModelRefiner {
                 ObjectSpecification superclassSpec = objectSpec.superclass();
                 while(superclassSpec != null) {
                     if(declaresVersionAnnotation(superclassSpec)) {
-                        validationFailures.add(
+                        validator.onFailure(
+                                objectSpec,
                                 objectSpec.getIdentifier(),
                                 "%s: cannot have @Version annotated on this subclass and any of its supertypes; superclass: %s",
                                 objectSpec.getFullIdentifier(),
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/BigDecimalDerivedFromJdoColumnAnnotationFacetFactory.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/BigDecimalDerivedFromJdoColumnAnnotationFacetFactory.java
index e8a37ae..8e50b77 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/BigDecimalDerivedFromJdoColumnAnnotationFacetFactory.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/BigDecimalDerivedFromJdoColumnAnnotationFacetFactory.java
@@ -38,9 +38,9 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -112,12 +112,12 @@ implements MetaModelRefiner {
         return new MetaModelValidatorVisiting.Visitor() {
 
             @Override
-            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
-                validate(objectSpec, validationFailures);
+            public boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator) {
+                validate(objectSpec, validator);
                 return true;
             }
 
-            private void validate(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+            private void validate(ObjectSpecification objectSpec, MetaModelValidator validator) {
 
                 // only consider persistent entities
                 final JdoPersistenceCapableFacet pcFacet = objectSpec.getFacet(JdoPersistenceCapableFacet.class);
@@ -132,12 +132,12 @@ implements MetaModelRefiner {
                 // skip checks if annotated with JDO @NotPersistent
                 .filter(association->!association.containsDoOpFacet(JdoNotPersistentFacet.class))
                 .forEach(association->{
-                    validateBigDecimalValueFacet(association, validationFailures);
+                    validateBigDecimalValueFacet(association, validator);
                 });
 
             }
 
-            private void validateBigDecimalValueFacet(ObjectAssociation association, ValidationFailures validationFailures) {
+            private void validateBigDecimalValueFacet(ObjectAssociation association, MetaModelValidator validator) {
                 BigDecimalValueFacet facet = association.getFacet(BigDecimalValueFacet.class);
                 if(facet == null) {
                     return;
@@ -153,14 +153,16 @@ implements MetaModelRefiner {
                     if(underlying instanceof BigDecimalFacetOnPropertyFromJavaxValidationDigitsAnnotation) {
 
                         if(notNullButNotEqual(facet.getPrecision(), underlying.getPrecision())) {
-                            validationFailures.add(
+                            validator.onFailure(
+                                    association,
                                     association.getIdentifier(),
                                     "%s: @javax.jdo.annotations.Column(length=...) different from @javax.validation.constraint.Digits(...); should equal the sum of its integer and fraction attributes",
                                     association.getIdentifier().toClassAndNameIdentityString());
                         }
 
                         if(notNullButNotEqual(facet.getScale(), underlying.getScale())) {
-                            validationFailures.add(
+                            validator.onFailure(
+                                    association,
                                     association.getIdentifier(),
                                     "%s: @javax.jdo.annotations.Column(scale=...) different from @javax.validation.constraint.Digits(fraction=...)",
                                     association.getIdentifier().toClassAndNameIdentityString());
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java
index 7a9b710..718f865 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java
@@ -40,9 +40,9 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -122,12 +122,12 @@ implements MetaModelRefiner {
         return new MetaModelValidatorVisiting.Visitor() {
 
             @Override
-            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
-                validate(objectSpec, validationFailures);
+            public boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator) {
+                validate(objectSpec, validator);
                 return true;
             }
 
-            private void validate(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+            private void validate(ObjectSpecification objectSpec, MetaModelValidator validator) {
 
                 final JdoPersistenceCapableFacet pcFacet = objectSpec.getFacet(JdoPersistenceCapableFacet.class);
                 if(pcFacet==null || pcFacet.getIdentityType() == IdentityType.NONDURABLE) {
@@ -141,10 +141,10 @@ implements MetaModelRefiner {
                 associations
                 // skip checks if annotated with JDO @NotPersistent
                 .filter(association->!association.containsDoOpFacet(JdoNotPersistentFacet.class))
-                .forEach(association->validateMandatoryFacet(association, validationFailures));
+                .forEach(association->validateMandatoryFacet(association, validator));
             }
 
-            private void validateMandatoryFacet(ObjectAssociation association, ValidationFailures validationFailures) {
+            private void validateMandatoryFacet(ObjectAssociation association, MetaModelValidator validator) {
                 MandatoryFacet facet = association.getFacet(MandatoryFacet.class);
 
                 MandatoryFacet underlying = (MandatoryFacet) facet.getUnderlyingFacet();
@@ -155,7 +155,8 @@ implements MetaModelRefiner {
                 if(facet instanceof MandatoryFacetDerivedFromJdoColumn) {
 
                     if(association.isNotPersisted()) {
-                        validationFailures.add(
+                        validator.onFailure(
+                                association,
                                 association.getIdentifier(),
                                 "%s: @javax.jdo.annotations.Column found on non-persisted property; please remove)",
                                 association.getIdentifier().toClassAndNameIdentityString());
@@ -168,12 +169,14 @@ implements MetaModelRefiner {
 
                     if(underlying.isInvertedSemantics()) {
                         // ie @Optional
-                        validationFailures.add(
+                        validator.onFailure(
+                                association,
                                 association.getIdentifier(),
                                 "%s: incompatible usage of Isis' @Optional annotation and @javax.jdo.annotations.Column; use just @javax.jdo.annotations.Column(allowsNull=\"...\")", 
                                 association.getIdentifier().toClassAndNameIdentityString());
                     } else {
-                        validationFailures.add(
+                        validator.onFailure(
+                                association,
                                 association.getIdentifier(),
                                 "%s: incompatible Isis' default of required/optional properties vs JDO; add @javax.jdo.annotations.Column(allowsNull=\"...\")", 
                                 association.getIdentifier().toClassAndNameIdentityString());
@@ -192,12 +195,14 @@ implements MetaModelRefiner {
                     }
                     if(underlying.isInvertedSemantics()) {
                         // ie @Optional
-                        validationFailures.add(
+                        validator.onFailure(
+                                association,
                                 association.getIdentifier(),
                                 "%s: incompatible usage of Isis' @Optional annotation and @javax.jdo.annotations.Column; use just @javax.jdo.annotations.Column(allowsNull=\"...\")", 
                                 association.getIdentifier().toClassAndNameIdentityString());
                     } else {
-                        validationFailures.add(
+                        validator.onFailure(
+                                association,
                                 association.getIdentifier(),
                                 "%s: incompatible default handling of required/optional properties between Isis and JDO; add @javax.jdo.annotations.Column(allowsNull=\"...\")", 
                                 association.getIdentifier().toClassAndNameIdentityString());
diff --git a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MaxLengthDerivedFromJdoColumnAnnotationFacetFactory.java b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MaxLengthDerivedFromJdoColumnAnnotationFacetFactory.java
index 956096e..8361ff2 100644
--- a/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MaxLengthDerivedFromJdoColumnAnnotationFacetFactory.java
+++ b/core/plugins/jdo/common/src/main/java/org/apache/isis/jdo/metamodel/facets/prop/column/MaxLengthDerivedFromJdoColumnAnnotationFacetFactory.java
@@ -32,15 +32,14 @@ import org.apache.isis.metamodel.facetapi.MetaModelRefiner;
 import org.apache.isis.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.metamodel.facets.FacetedMethod;
 import org.apache.isis.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
-import org.apache.isis.metamodel.facets.properties.property.maxlength.MaxLengthFacetForMaxLengthAnnotationOnProperty;
 import org.apache.isis.metamodel.facets.properties.property.maxlength.MaxLengthFacetForPropertyAnnotation;
 import org.apache.isis.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
-import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 
 import lombok.val;
 
@@ -98,12 +97,12 @@ implements MetaModelRefiner {
         return new MetaModelValidatorVisiting.Visitor() {
 
             @Override
-            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
-                validate(objectSpec, validationFailures);
+            public boolean visit(ObjectSpecification objectSpec, MetaModelValidator validator) {
+                validate(objectSpec, validator);
                 return true;
             }
 
-            private void validate(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+            private void validate(ObjectSpecification objectSpec, MetaModelValidator validator) {
 
                 final JdoPersistenceCapableFacet pcFacet = objectSpec.getFacet(JdoPersistenceCapableFacet.class);
                 if(pcFacet==null || pcFacet.getIdentityType() == IdentityType.NONDURABLE) {
@@ -126,17 +125,19 @@ implements MetaModelRefiner {
                         return;
                     }
 
-                    if(facet instanceof MaxLengthFacetDerivedFromJdoColumn && underlying instanceof MaxLengthFacetForMaxLengthAnnotationOnProperty) {
-                        if(facet.value() != underlying.value()) {
-                            validationFailures.add(
-                                    association.getIdentifier(),
-                                    "%s: inconsistent lengths specified in Isis' @MaxLength(...) and @javax.jdo.annotations.Column(length=...); use just @javax.jdo.annotations.Column(length=...)",
-                                    association.getIdentifier().toClassAndNameIdentityString());
-                        }
-                    }
+//                    if(facet instanceof MaxLengthFacetDerivedFromJdoColumn && underlying instanceof MaxLengthFacetForMaxLengthAnnotationOnProperty) {
+//                        if(facet.value() != underlying.value()) {
+//                            validator.onFailure(
+//                                    association,
+//                                    association.getIdentifier(),
+//                                    "%s: inconsistent lengths specified in Isis' @MaxLength(...) and @javax.jdo.annotations.Column(length=...); use just @javax.jdo.annotations.Column(length=...)",
+//                                    association.getIdentifier().toClassAndNameIdentityString());
+//                        }
+//                    }
                     if(facet instanceof MaxLengthFacetDerivedFromJdoColumn && underlying instanceof MaxLengthFacetForPropertyAnnotation) {
                         if(facet.value() != underlying.value()) {
-                            validationFailures.add(
+                            validator.onFailure(
+                                    association,
                                     association.getIdentifier(),
                                     "%s: inconsistent lengths specified in Isis' @Property(maxLength=...) and @javax.jdo.annotations.Column(length=...); use just @javax.jdo.annotations.Column(length=...)",
                                     association.getIdentifier().toClassAndNameIdentityString());
diff --git a/core/plugins/jdo/common/src/test/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactoryTest_refineMetaModel.java b/core/plugins/jdo/common/src/test/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactoryTest_refineMetaModel.java
index 9ae60b6..1195be8 100644
--- a/core/plugins/jdo/common/src/test/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactoryTest_refineMetaModel.java
+++ b/core/plugins/jdo/common/src/test/java/org/apache/isis/jdo/metamodel/facets/object/version/JdoVersionAnnotationFacetFactoryTest_refineMetaModel.java
@@ -27,7 +27,10 @@ import org.junit.Rule;
 import org.junit.Test;
 
 import org.apache.isis.applib.Identifier;
+import org.apache.isis.metamodel.facets.all.deficiencies.DeficiencyFacet;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidator;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorAbstract;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
 import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
 import org.apache.isis.unittestsupport.jmocking.JUnitRuleMockery2;
@@ -46,7 +49,8 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
     private ObjectSpecification mockGrandParentType;
 
     private Visitor newValidatorVisitor;
-    private ValidationFailures validationFailures;
+    private ValidationFailures failures;
+    private MetaModelValidator validator;
 
     private Sequence sequence;
 
@@ -58,7 +62,8 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
 
         sequence = context.sequence("inorder");
 
-        validationFailures = new ValidationFailures();
+        failures = new ValidationFailures();
+        validator = new MetaModelValidatorAbstract() {};
         newValidatorVisitor = new JdoVersionAnnotationFacetFactory().newValidatorVisitor();
     }
 
@@ -74,9 +79,9 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
             }
         });
 
-        newValidatorVisitor.visit(mockChildType, validationFailures);
+        newValidatorVisitor.visit(mockChildType, validator);
 
-        assertThat(validationFailures.getNumberOfFailures(), is(0));
+        assertThat(failures.getNumberOfFailures(), is(0));
     }
 
     @Test
@@ -97,9 +102,9 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
             }
         });
 
-        newValidatorVisitor.visit(mockChildType, validationFailures);
+        newValidatorVisitor.visit(mockChildType, validator);
 
-        assertThat(validationFailures.getNumberOfFailures(), is(0));
+        assertThat(failures.getNumberOfFailures(), is(0));
     }
 
     @Test
@@ -130,9 +135,9 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
             }
         });
 
-        newValidatorVisitor.visit(mockChildType, validationFailures);
+        newValidatorVisitor.visit(mockChildType, validator);
 
-        assertThat(validationFailures.getNumberOfFailures(), is(0));
+        assertThat(failures.getNumberOfFailures(), is(0));
     }
 
 
@@ -171,15 +176,17 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
                 inSequence(sequence);
                 will(returnValue("mockParentType"));
                 
+                oneOf(mockChildType).getFacet(DeficiencyFacet.class);
+                will(returnValue(null));
 
 
             }
         });
 
-        newValidatorVisitor.visit(mockChildType, validationFailures);
+        newValidatorVisitor.visit(mockChildType, validator);
 
-        assertThat(validationFailures.getNumberOfFailures(), is(1));
-        assertThat(validationFailures.getMessages().iterator().next(), is("mockChildType: cannot have @Version annotated on this subclass and any of its supertypes; superclass: mockParentType"));
+        assertThat(failures.getNumberOfFailures(), is(1));
+        assertThat(failures.getMessages().iterator().next(), is("mockChildType: cannot have @Version annotated on this subclass and any of its supertypes; superclass: mockParentType"));
     }
 
 
@@ -232,10 +239,10 @@ public class JdoVersionAnnotationFacetFactoryTest_refineMetaModel {
             }
         });
 
-        newValidatorVisitor.visit(mockChildType, validationFailures);
+        newValidatorVisitor.visit(mockChildType, validator);
 
-        assertThat(validationFailures.getNumberOfFailures(), is(1));
-        assertThat(validationFailures.getMessages().iterator().next(), is("mockChildType: cannot have @Version annotated on this subclass and any of its supertypes; superclass: mockGrandParentType"));
+        assertThat(failures.getNumberOfFailures(), is(1));
+        assertThat(failures.getMessages().iterator().next(), is("mockChildType: cannot have @Version annotated on this subclass and any of its supertypes; superclass: mockGrandParentType"));
     }
 
 }
diff --git a/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java b/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java
index 936c9d7..dffc810 100644
--- a/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java
@@ -89,7 +89,7 @@ public class IsisSessionFactoryDefault implements IsisSessionFactory {
 
         val taskList = _ConcurrentTaskList.named("IsisSessionFactoryDefault Init")
         
-        .addRunnable("SpecificationLoader::createThenValidateMetaModel", this::createThenValidateMetaModel)
+        .addRunnable("SpecificationLoader::createMetaModel", specificationLoader::createMetaModel)
         .addRunnable("ChangesDtoUtils::init", ChangesDtoUtils::init)
         .addRunnable("InteractionDtoUtils::init", InteractionDtoUtils::init)
         .addRunnable("CommandDtoUtils::init", CommandDtoUtils::init)
@@ -103,11 +103,6 @@ public class IsisSessionFactoryDefault implements IsisSessionFactory {
 
     }
 
-    private void createThenValidateMetaModel() {
-        specificationLoader.createMetaModel();
-        specificationLoader.validateMetaModel();
-    }
-
     @PreDestroy
     public void shutdown() {
         // call might originate from a different thread than main
diff --git a/core/testsupport/integtestsupport/src/main/java/org/apache/isis/integtestsupport/validate/ValidateDomainModel.java b/core/testsupport/integtestsupport/src/main/java/org/apache/isis/integtestsupport/validate/ValidateDomainModel.java
index c825ade..1ee1a2a 100644
--- a/core/testsupport/integtestsupport/src/main/java/org/apache/isis/integtestsupport/validate/ValidateDomainModel.java
+++ b/core/testsupport/integtestsupport/src/main/java/org/apache/isis/integtestsupport/validate/ValidateDomainModel.java
@@ -54,10 +54,8 @@ public class ValidateDomainModel implements Runnable {
 
         val specificationLoader = IsisContext.getSpecificationLoader();
         
-        val programmingModelService = IsisContext.getServiceRegistry()
-                .lookupServiceElseFail(ProgrammingModelService.class);
-        this.validationFailures = programmingModelService.getValidationResult();
-        
+        this.validationFailures = specificationLoader.getValidationResult();
+      
 
         val objectSpecifications = specificationLoader.snapshotSpecifications();
         
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java
index 1f2c6c2..0262253 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java
@@ -66,8 +66,8 @@ class SpecloaderPerformanceTest {
         IsisBeanTypeRegistry.repeatedTesting = true;
     }
     
-    static long ITERATIONS = 400; /* should typically run in ~10s */
-    static long EXPECTED_MILLIS_PER_ITERATION = 500;
+    static long ITERATIONS = 100; /* should typically run in ~10s */
+    static long EXPECTED_MILLIS_PER_ITERATION = 100;
     
     @Test 
     void repeatedConcurrentSpecloading_shouldNotDeadlock() {
diff --git a/extensions/incubator/src/main/java/org/apache/isis/metamodel/facets/actions/support/SupportingMethodValidatorRefinerFactory.java b/extensions/incubator/src/main/java/org/apache/isis/metamodel/facets/actions/support/SupportingMethodValidatorRefinerFactory.java
index e3d613f..5a7d55a 100644
--- a/extensions/incubator/src/main/java/org/apache/isis/metamodel/facets/actions/support/SupportingMethodValidatorRefinerFactory.java
+++ b/extensions/incubator/src/main/java/org/apache/isis/metamodel/facets/actions/support/SupportingMethodValidatorRefinerFactory.java
@@ -88,7 +88,8 @@ implements MetaModelRefiner {
 
                 val messageFormat = "%s#%s: has annotion %s, is assumed to support "
                         + "a property, collection or action. Unmet constraint(s): %s";
-                validationFailures.add(
+                validationFailures.onFailure(
+                        spec,
                         spec.getIdentifier(),
                         messageFormat,
                         spec.getIdentifier().getClassName(),