You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2025/08/01 18:14:25 UTC

(causeway) 01/06: CAUSEWAY-2297: apps work - some tests do not compile

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

ahuber pushed a commit to branch 3900-immutable.config
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 5d593cd4eaade97f15908b82136966801bb8a34a
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Aug 1 05:44:02 2025 +0200

    CAUSEWAY-2297: apps work - some tests do not compile
---
 .../applib/services/registry/ServiceRegistry.java  |   10 +-
 .../value/semantics/TemporalValueSemantics.java    |   62 +-
 bom/pom.xml                                        |    7 +
 ...eanProvider.java => SingletonBeanProvider.java} |   25 +-
 ...tainer_Spring.java => SpringContextHolder.java} |  138 +-
 .../commons/internal/ioc/_IocContainer.java        |  129 -
 .../core/config/CausewayConfiguration.java         | 3895 ++++++++++----------
 .../core/config/CausewayModuleCoreConfig.java      |   37 +-
 .../core/config/applib/RestfulPathProvider.java    |    2 +-
 .../environment/CausewayLocaleInitializer.java     |    2 +-
 .../environment/CausewaySystemEnvironment.java     |   23 +-
 .../environment/CausewayTimeZoneInitializer.java   |    2 +-
 .../metamodel/facets/ActionConfigOptions.java      |    4 +-
 .../facets/AssociationLayoutConfigOptions.java     |    8 +-
 .../facets/CollectionLayoutConfigOptions.java      |    2 +-
 .../facets/DomainObjectConfigOptions.java          |    4 +-
 .../metamodel/facets/ParameterConfigOptions.java   |    2 +-
 .../metamodel/facets/PropertyConfigOptions.java    |    4 +-
 .../metamodel/specloader/IntrospectionMode.java    |    2 +-
 ...ion_getMaxTitleLengthInParentedTables_Test.java |   86 +-
 ...n_getMaxTitleLengthInStandaloneTables_Test.java |   85 +-
 .../causeway/core/config/ConfigurationFactory.java |   43 +
 core/configtest/pom.xml                            |   65 +
 .../core/configtest/ConfigurationTester.java       |   66 +
 core/metamodel/pom.xml                             |    6 +
 .../metamodel/CausewayModuleCoreMetamodel.java     |    2 +-
 .../_testing/MetaModelContext_forTesting.java      |   68 +-
 .../_testing/ServiceRegistry_forTesting.java       |   22 +-
 .../metamodel/context/HasMetaModelContext.java     |   10 +-
 .../metamodel/context/MetaModelContextFactory.java |    3 +-
 .../context/MetaModelContext_usingSpring.java      |   10 +-
 .../causeway/core/metamodel/context/MmcRecord.java |   85 +
 .../layout/DefaultViewFacetAsConfigured.java       |    4 +-
 ...ecoratorFacetForCollectionLayoutAnnotation.java |    2 +-
 .../facets/fallback/FallbackFacetFactory.java      |    8 +-
 .../metamodel/facets/jaxb/JaxbFacetFactory.java    |   10 +-
 .../CssClassFacetOnActionFromConfiguredRegex.java  |    4 +-
 .../FaFacetOnMemberFromConfiguredRegex.java        |    2 +-
 .../ViewModelSemanticCheckingFacetFactory.java     |    2 +-
 .../DomainObjectAnnotationFacetFactory.java        |   16 +-
 .../ImmutableFacetForDomainObjectAnnotation.java   |    2 +-
 ...ObjectLayoutAnnotationUsingCssClassUiEvent.java |    4 +-
 ...mainObjectLayoutAnnotationUsingIconUiEvent.java |    4 +-
 ...ainObjectLayoutAnnotationUsingTitleUiEvent.java |   10 +-
 ...oratorFacetForDomainObjectLayoutAnnotation.java |    2 +-
 .../RemoveAnnotatedMethodsFacetFactory.java        |    2 +-
 .../IntrospectionPolicyFacetAbstract.java          |    4 +-
 .../object/layout/LayoutPrefixFacetForUiEvent.java |    4 +-
 .../LogicalTypeFacetFromClassNameFactory.java      |    2 +-
 .../object/promptStyle/PromptStyleFacet.java       |    2 +-
 .../PropertyOrCollectionAccessorFacetAbstract.java |    2 +-
 .../ValueSemanticsAnnotationFacetFactory.java      |    2 +-
 .../metamodel/interactions/InteractionUtils.java   |    4 +-
 .../core/metamodel/object/MmEventUtils.java        |    6 +-
 .../objectmanager/ObjectLoaderFactory.java         |    4 +-
 .../progmodel/ProgrammingModelAbstract.java        |   38 +-
 .../ProgrammingModelInitFilterDefault.java         |    2 +-
 .../ApplicationFeatureRepositoryDefault.java       |    2 +-
 .../services/registry/ServiceRegistryDefault.java  |   28 +-
 .../core/metamodel/spec/feature/ObjectAction.java  |    4 +-
 ...wayModuleCoreMetamodelConfigurationDefault.java |    2 +-
 .../spec/impl/ObjectSpecificationDefault.java      |    2 +-
 .../spec/impl/ProgrammingModelDefault.java         |   10 +-
 .../spec/impl/SpecificationLoaderDefault.java      |    4 +-
 .../valuesemantics/BigDecimalValueSemantics.java   |   16 +-
 .../temporal/TemporalValueSemanticsProvider.java   |   12 +-
 .../metamodel/MetaModelContext_configTest.java     |   20 +-
 .../facets/AbstractTestWithMetaModelContext.java   |    2 +-
 .../metamodel/facets/FacetFactoryTestAbstract.java |   10 +-
 .../action/ActionAnnotationFacetFactoryTest.java   |    2 +-
 .../ViewModelSemanticCheckingFacetFactoryTest.java |    6 +-
 .../DomainObjectAnnotationFacetFactoryTest.java    |    8 +-
 ...PromptStyleFacetFromPropertyAnnotationTest.java |    4 +-
 .../BigDecimalValueSemanticsProviderTest.java      |    6 +-
 .../ValueSemanticsProviderAbstractTestCase.java    |    2 +-
 ...anticsProvider_configureDecimalFormat_Test.java |   11 +-
 .../TemporalValueSemanticsProviderTest.java        |    2 +-
 core/pom.xml                                       |    1 +
 .../command/CommandDtoFactoryDefault.java          |    3 +-
 .../executor/MemberExecutorServiceDefault.java     |    2 +-
 .../factory/FactoryServiceDefault.java             |    2 +-
 .../i18n/po/TranslationServicePo.java              |    2 +-
 .../menubars/MenuBarsLoaderServiceDefault.java     |    4 +-
 .../EntityPropertyChangePublisherDefault.java      |    3 +-
 .../ExceptionRecognizerForDataAccessException.java |    4 +-
 .../RuntimeServicesTestAbstract.java               |   10 +-
 .../menubars/bootstrap/MenuBarsServiceBSTest.java  |    2 +-
 ...eptionRecognizerForDataAccessExceptionTest.java |    2 +-
 .../manager/AuthorizationManager.java              |    2 +-
 .../confmenu/ConfigurationViewServiceDefault.java  |   10 +-
 .../applib/job/RunBackgroundCommandsJob.java       |    6 +-
 .../subscriber/CommandSubscriberForCommandLog.java |    2 +-
 .../ExecutionSubscriberForExecutionLog.java        |    2 +-
 .../ExecutionOutboxEntryRepositoryAbstract.java    |   10 +-
 .../ExecutionSubscriberForExecutionOutbox.java     |    2 +-
 .../CausewayModuleExtLayoutLoadersGithub.java      |    6 +-
 .../github/menu/LayoutLoadersGitHubMenu.java       |    6 +-
 .../spiimpl/LayoutResourceLoaderFromGithub.java    |    2 +-
 ...oaderFromGithub_tryLoadLayoutResource_Test.java |    9 +-
 .../caffeine/dom/TitleCacheSubscriber.java         |    6 +-
 ...ntityPropertyChangeSubscriberForAuditTrail.java |    4 +-
 .../dom/ApplicationRoleRepositoryAbstract.java     |    2 +-
 .../mixins/ApplicationRole_removePermissions.java  |    6 +-
 .../AbstractRoleAndPermissionsFixtureScript.java   |    3 +-
 .../CausewayExtSecmanAdminRoleAndPermissions.java  |   14 +-
 ...ewayExtSecmanRegularUserRoleAndPermissions.java |   18 +-
 .../scripts/SeedUsersAndRolesFixtureScript.java    |    6 +-
 .../secman/applib/user/dom/ApplicationUser.java    |    6 +-
 .../dom/ApplicationUserRepositoryAbstract.java     |    2 +-
 .../user/dom/mixins/ApplicationUser_lock.java      |    2 +-
 .../ApplicationUserManager_newDelegateUser.java    |    2 +-
 .../ApplicationUserManager_newLocalUser.java       |    2 +-
 .../secman/applib/user/menu/MeService.java         |    2 +-
 .../user/seed/CausewayExtSecmanAdminUser.java      |   10 +-
 .../dom/ApplicationUserAutoCreationService.java    |   15 +-
 .../PermissionsEvaluationServiceForSecman.java     |    4 +-
 .../userreg/UserRegistrationServiceForSecman.java  |    2 +-
 .../spiimpl/SessionLogServiceInitializer.java      |    2 +-
 .../cors/impl/CausewayModuleExtCors.java           |   14 +-
 .../primary/config/PrimaryConfig.java              |    6 +-
 .../CausewayModuleExtCommandReplaySecondary.java   |    6 +-
 .../analyser/CommandReplayAnalyserException.java   |    2 +-
 .../analyser/CommandReplayAnalyserResult.java      |    2 +-
 .../secondary/config/SecondaryConfig.java          |   18 +-
 .../secondary/fetch/CommandFetcher_Test.java       |   20 +-
 .../changetracking/EntityChangeTrackerDefault.java |    6 +-
 .../repository/RepositoryServiceDefault.java       |    2 +-
 .../CausewayModulePersistenceJpaEclipselink.java   |   14 +-
 ...DecimalFromJpaColumnAnnotationFacetFactory.java |    2 +-
 .../testdomain/jpa/JpaTestDomainPersona.java       |    2 +-
 .../SpringServiceProvisioningTest.java             |    8 +-
 .../testdomain/config/CausewayConfigBeanTest.java  |    2 +-
 .../CausewayConfigBeanTest_usingHeadless.java      |    2 +-
 ...elTest_usingBadDomain_noAnnotationEnforced.java |    2 +-
 ...InteractionTest_notUsingAllowSafeSemantics.java |    2 +-
 ...ionInteractionTest_usingAllowSafeSemantics.java |    2 +-
 .../interact/CollectionInteractionTest.java        |    8 +-
 .../keycloak/CausewayModuleSecurityKeycloak.java   |    4 +-
 .../services/KeycloakOauth2UserService.java        |   12 +-
 .../spring/CausewayModuleSecuritySpring.java       |    2 +-
 ...iptsSpecificationProviderAutoConfiguration.java |   16 +-
 .../services/InitialFixtureScriptsInstaller.java   |    2 +-
 .../h2console/ui/webmodule/WebModuleH2Console.java |    4 +-
 .../applib/validate/DomainModelValidator.java      |    2 +-
 .../applib/CausewayModuleValAsciidocApplib.java    |    4 +-
 .../branding/BrandingUiServiceDefault.java         |   10 +-
 .../services/i8n/TranslationsResolverDefault.java  |    2 +-
 .../viewer/graphql/model/context/Context.java      |    2 +-
 .../viewer/graphql/model/domain/Element.java       |    2 +-
 .../viewer/graphql/model/domain/ElementCustom.java |    4 +-
 .../domain/common/query/CommonDomainObject.java    |    8 +-
 .../domain/common/query/CommonDomainService.java   |    4 +-
 .../model/domain/common/query/CommonMeta.java      |    6 +-
 .../common/query/meta/CommonMetaFetcher.java       |    2 +-
 .../model/domain/rich/SchemaStrategyRich.java      |    5 +-
 .../rich/mutation/RichMutationForAction.java       |    4 +-
 .../rich/mutation/RichMutationForProperty.java     |    6 +-
 .../model/domain/rich/query/RichAction.java        |    2 +-
 .../model/domain/rich/query/RichProperty.java      |    3 +-
 .../domain/rich/query/RichPropertyGetBlob.java     |    4 +-
 .../domain/rich/query/RichPropertyGetClob.java     |    4 +-
 .../model/domain/rich/query/RichTopLevelQuery.java |    4 +-
 .../model/domain/simple/SchemaStrategySimple.java  |    7 +-
 .../simple/mutation/SimpleMutationForAction.java   |    4 +-
 .../simple/mutation/SimpleMutationForProperty.java |    4 +-
 .../domain/simple/query/SimpleTopLevelQuery.java   |    2 +-
 .../marshallers/ScalarMarshallerJdk8LocalDate.java |    4 +-
 .../marshallers/ScalarMarshallerJdk8LocalTime.java |    4 +-
 .../ScalarMarshallerJdk8OffsetDateTime.java        |    3 -
 .../ScalarMarshallerJdk8OffsetTime.java            |    3 -
 .../ScalarMarshallerJdk8ZonedDateTime.java         |    4 +-
 .../viewer/auth/UserMementoProviderDefault.java    |    4 +-
 .../viewer/controller/ResourceController.java      |   14 +-
 .../integration/GraphQlSourceForCauseway.java      |    6 +-
 .../restfulobjects/rendering/IResourceContext.java |    2 +-
 .../AbstractObjectMemberReprRenderer.java          |    8 +-
 .../domainobjects/DomainObjectReprRenderer.java    |   12 +-
 .../domainobjects/ObjectActionReprRenderer.java    |    2 +-
 .../ObjectCollectionReprRenderer.java              |    6 +-
 .../domainobjects/ObjectPropertyReprRenderer.java  |   24 +-
 .../domainobjects/ScalarValueReprRenderer.java     |    2 +-
 ...entNegotiationServiceForRestfulObjectsV1_0.java |    2 +-
 .../viewer/resources/ResourceAbstract.java         |    2 +-
 .../viewer/resources/VersionReprRenderer.java      |    3 +-
 .../CausewayRestfulObjectsInteractionFilter2.java  |    2 +-
 .../viewer/webmodule/WebModuleRestfulObjects.java  |    4 +-
 .../wicket/model/models/BookmarkedPagesModel.java  |    2 +-
 .../viewer/wicket/model/models/WelcomeModel.java   |    2 +-
 .../viewer/wicket/model/whereAmI/WhereAmI.java     |    6 +-
 .../test/components/scalars/ConverterTester.java   |    2 +-
 .../scalars/jdkmath/BigDecimalConverterTest.java   |   10 +-
 .../wicket/ui/components/about/AboutPanel.java     |    6 +-
 .../attributes/AttributePanelAdditionalButton.java |    4 +-
 .../attributes/AttributePanelWithFormField.java    |    2 +-
 .../temporal/TextFieldWithDateTimePicker.java      |    8 +-
 .../CollectionContentsAsAjaxTablePanel.java        |    6 +-
 .../present/ajaxtable/columns/ActionColumn.java    |    4 +-
 .../wicket/ui/components/footer/FooterPanel.java   |   16 +-
 .../object/icontitle/ObjectIconAndTitlePanel.java  |    4 +-
 .../ui/components/table/CausewayAjaxDataTable.java |    2 +-
 .../table/head/HeadersToolbarAbstract.java         |    2 +-
 .../components/widgets/actionlink/ActionLink.java  |    6 +-
 .../widgets/breadcrumbs/BreadcrumbPanel.java       |    2 +-
 .../CausewayWicketThemeSupportDefault.java         |    4 +-
 .../widgets/themepicker/ThemeChooser.java          |    2 +-
 .../viewer/wicket/ui/errors/JGrowlUtil.java        |   12 +-
 .../viewer/wicket/ui/pages/PageAbstract.java       |   18 +-
 .../viewer/wicket/ui/pages/WebPageBase.java        |    2 +-
 .../accmngt/AccountManagementPageAbstract.java     |    6 +-
 .../password_reset/PasswordResetEmailPanel.java    |    2 +-
 .../accmngt/password_reset/PasswordResetPage.java  |   17 +-
 .../pages/accmngt/signup/RegistrationFormPage.java |    2 +-
 .../accmngt/signup/RegistrationFormPanel.java      |    2 +-
 .../wicket/ui/pages/login/SignInPanelAbstract.java |    6 +-
 .../wicket/ui/pages/login/WicketSignInPage.java    |    8 +-
 .../wicket/ui/pages/mmverror/MmvErrorPage.java     |    8 +-
 .../apache/causeway/viewer/wicket/ui/util/Wkt.java |    4 +-
 .../viewer/wicket/ui/util/WktDecorators.java       |    4 +-
 .../viewer/wicket/ui/util/WktTooltips.java         |    2 +-
 .../integration/WebRequestCycleForCauseway.java    |    2 +-
 .../wicket/viewer/webmodule/WebModuleWicket.java   |    4 +-
 .../wicketapp/CausewayWicketApplication.java       |    8 +-
 .../viewer/wicketapp/config/DebugInitWkt.java      |    4 +-
 223 files changed, 2986 insertions(+), 2985 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/registry/ServiceRegistry.java b/api/applib/src/main/java/org/apache/causeway/applib/services/registry/ServiceRegistry.java
index ecf7639e46f..c0929d38c81 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/services/registry/ServiceRegistry.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/services/registry/ServiceRegistry.java
@@ -29,7 +29,7 @@
 import org.apache.causeway.commons.internal._Constants;
 import org.apache.causeway.commons.internal.base._Reduction;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 
 /**
  * Collects together methods for injecting or looking up domain services
@@ -64,7 +64,7 @@ default <T> Can<T> select(final Class<T> type){
     /**
      * Streams all registered bean adapters implementing the requested type.
      */
-    default Stream<_SingletonBeanProvider> streamRegisteredBeansOfType(final Class<?> requiredType) {
+    default Stream<SingletonBeanProvider> streamRegisteredBeansOfType(final Class<?> requiredType) {
         return streamRegisteredBeans()
                 .filter(beanAdapter->beanAdapter.isCandidateFor(requiredType));
     }
@@ -72,21 +72,21 @@ default Stream<_SingletonBeanProvider> streamRegisteredBeansOfType(final Class<?
     /**
      * Returns all bean adapters that have been registered.
      */
-    Stream<_SingletonBeanProvider> streamRegisteredBeans();
+    Stream<SingletonBeanProvider> streamRegisteredBeans();
 
     /**
      * Returns a registered bean of given {@code name}.
      *
      * @param id - corresponds to the ObjectSpecificationId of the bean's type
      */
-    Optional<_SingletonBeanProvider> lookupRegisteredBeanById(LogicalType id);
+    Optional<SingletonBeanProvider> lookupRegisteredBeanById(LogicalType id);
 
     /**
      * Returns a registered bean of given {@code name}, or throws when no such bean.
      *
      * @param id - corresponds to the ObjectSpecificationId of the bean's type
      */
-    default _SingletonBeanProvider lookupRegisteredBeanByIdElseFail(final LogicalType id) {
+    default SingletonBeanProvider lookupRegisteredBeanByIdElseFail(final LogicalType id) {
         return lookupRegisteredBeanById(id).orElseThrow(
                 ()->_Exceptions.unrecoverable(
                         "Failed to lookup BeanAdapter by id '" + id + "'"));
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
index 3cf05375d69..4e8b309cf77 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
@@ -21,11 +21,13 @@
 import java.time.Duration;
 import java.time.temporal.Temporal;
 
+import org.jspecify.annotations.NonNull;
+
 import org.apache.causeway.applib.annotation.TimePrecision;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 
-import lombok.Data;
-import org.jspecify.annotations.NonNull;
+import lombok.Getter;
+import lombok.experimental.Accessors;
 
 /**
  * Common base for {@link java.time.temporal.Temporal} value types.
@@ -57,29 +59,25 @@ static enum EditingFormatDirection {
         public boolean isOutput() {return this == OUTPUT;}
     }
 
-    @Data
-    public static class TemporalDisplayPattern {
-
+    public record TemporalDisplayPattern(
         /**
          * The locale-independent (canonical) pattern used for displaying dates in the UI.
          *
-         * <p>
-         *     By default, a locale-specific date pattern is used (using {@link java.time.format.FormatStyle#MEDIUM} format).
-         * </p>
+         * <p>By default, a locale-specific date pattern is used (using {@link java.time.format.FormatStyle#MEDIUM} format).
          */
-        private String datePattern;
-
+        String datePattern,
         /**
          * The locale-independent (canonical) pattern used for displaying date/times in the UI.
          *
-         * <p>
-         *     By default, a locale-specific date/time pattern is used (using {@link java.time.format.FormatStyle#MEDIUM} format) for both the date and time parts.
-         * </p>
+         * <p>By default, a locale-specific date/time pattern is used (using {@link java.time.format.FormatStyle#MEDIUM} format)
+         * for both the date and time parts.
          */
-        private String dateTimePattern;
+        String dateTimePattern
+        ) {
+
     }
 
-    @Data
+    @Getter @Accessors(fluent=true)
     public static class TemporalEditingPattern {
 
         /**
@@ -249,24 +247,24 @@ public String getEditingFormatAsPattern(
             switch (temporalCharacteristic) {
             case DATE_TIME:
                 var dateTimePattern =
-                    String.format(getDateTimeJoiningPattern(),
-                            getDatePattern(),
+                    String.format(dateTimeJoiningPattern(),
+                            datePattern(),
                             timePattern(timePrecision, direction));
                 return offsetCharacteristic.isLocal()
                         ? dateTimePattern
-                        : String.format(getZoneJoiningPattern(),
+                        : String.format(zoneJoiningPattern(),
                                 dateTimePattern,
                                 zonePattern(offsetCharacteristic, direction));
             case DATE_ONLY:
                 return offsetCharacteristic.isLocal()
-                        ? getDatePattern()
-                        : String.format(getZoneJoiningPattern(),
-                                getDatePattern(),
+                        ? datePattern()
+                        : String.format(zoneJoiningPattern(),
+                                datePattern(),
                                 zonePattern(offsetCharacteristic,direction));
             case TIME_ONLY:
                 return offsetCharacteristic.isLocal()
                         ? timePattern(timePrecision, direction)
-                        : String.format(getZoneJoiningPattern(),
+                        : String.format(zoneJoiningPattern(),
                                 timePattern(timePrecision, direction),
                                 zonePattern(offsetCharacteristic, direction));
             default:
@@ -281,12 +279,12 @@ private String zonePattern(
             switch(offsetCharacteristic) {
             case OFFSET:
                 return direction.isInput()
-                        ? getOffsetPatternForInput()
-                        : getOffsetPatternForOutput();
+                        ? offsetPatternForInput()
+                        : offsetPatternForOutput();
             case ZONED:
                 return direction.isInput()
-                        ? getZoneIdPatternForInput()
-                        : getZoneIdPatternForOutput();
+                        ? zoneIdPatternForInput()
+                        : zoneIdPatternForOutput();
             default:
                 throw _Exceptions.unexpectedCodeReach();
             }
@@ -310,18 +308,18 @@ private String timePattern(
         private String timePattern(final @NonNull TimePrecision timePrecision) {
             switch (timePrecision) {
             case NANO_SECOND:
-                return getTimePatternNanoSecond();
+                return timePatternNanoSecond();
             case MICRO_SECOND:
-                return getTimePatternMicroSecond();
+                return timePatternMicroSecond();
             case MILLI_SECOND:
-                return getTimePatternMilliSecond();
+                return timePatternMilliSecond();
             case UNSPECIFIED:
             case SECOND:
-                return getTimePatternSecond();
+                return timePatternSecond();
             case MINUTE:
-                return getTimePatternMinute();
+                return timePatternMinute();
             case HOUR:
-                return getTimePatternHour();
+                return timePatternHour();
             }
             throw _Exceptions.unmatchedCase(timePrecision);
         }
diff --git a/bom/pom.xml b/bom/pom.xml
index 80a079b1d5f..5563e1bda39 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -244,6 +244,13 @@ identified
                 <type>jar</type>
                 <scope>compile</scope>
             </dependency>
+            <dependency>
+                <groupId>org.apache.causeway.core</groupId>
+                <artifactId>causeway-core-configtest</artifactId>
+                <version>${project.version}</version>
+                <type>jar</type>
+                <scope>compile</scope>
+            </dependency>
             <dependency>
                 <groupId>org.apache.causeway.core</groupId>
                 <artifactId>causeway-core-interaction</artifactId>
diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_SingletonBeanProvider.java b/commons/src/main/java/org/apache/causeway/commons/internal/ioc/SingletonBeanProvider.java
similarity index 76%
rename from commons/src/main/java/org/apache/causeway/commons/internal/ioc/_SingletonBeanProvider.java
rename to commons/src/main/java/org/apache/causeway/commons/internal/ioc/SingletonBeanProvider.java
index 829d21f1f5e..fe945ca20f3 100644
--- a/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_SingletonBeanProvider.java
+++ b/commons/src/main/java/org/apache/causeway/commons/internal/ioc/SingletonBeanProvider.java
@@ -29,9 +29,16 @@
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 
 /**
+ * <h1>- internal use only -</h1>
+ * <p>
+ * Framework internal Bean provider support.
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/>
+ * These may be changed or removed without notice!
+ *
  * @since 2.0
  */
-public record _SingletonBeanProvider(
+public record SingletonBeanProvider(
         /**
          * Unique bean name (not an alias).
          * Corresponds to the logical-type-name (Causeway semantics).
@@ -44,18 +51,18 @@ public record _SingletonBeanProvider(
 
     // -- TEST FACTORIES
 
-    public static <T> _SingletonBeanProvider forTestingLazy(
+    public static <T> SingletonBeanProvider forTestingLazy(
             final String logicalTypeName, final Class<T> beanClass, final Supplier<T> beanProvider) {
-        return new _SingletonBeanProvider(logicalTypeName, beanClass, beanProvider);
+        return new SingletonBeanProvider(logicalTypeName, beanClass, beanProvider);
     }
 
-    public static <T> _SingletonBeanProvider forTestingLazy(
+    public static <T> SingletonBeanProvider forTestingLazy(
             final Class<T> beanClass, final Supplier<T> beanProvider) {
-        return new _SingletonBeanProvider(beanClass.getName(), beanClass, beanProvider);
+        return new SingletonBeanProvider(beanClass.getName(), beanClass, beanProvider);
     }
 
-    public static <T> _SingletonBeanProvider forTesting(final T bean) {
-        return new _SingletonBeanProvider(bean.getClass().getName(), bean.getClass(), ()->bean);
+    public static <T> SingletonBeanProvider forTesting(final T bean) {
+        return new SingletonBeanProvider(bean.getClass().getName(), bean.getClass(), ()->bean);
     }
 
     public Optional<?> lookupInstance() {
@@ -76,7 +83,7 @@ public boolean isCandidateFor(final @Nullable Class<?> requiredType) {
 
     @Override
     public final boolean equals(final Object obj) {
-        return obj instanceof _SingletonBeanProvider other
+        return obj instanceof SingletonBeanProvider other
                 ? Objects.equals(this.id(), other.id())
                         && Objects.equals(this.beanClass(), other.beanClass())
                 : false;
@@ -92,7 +99,7 @@ public final String toString() {
 
     // -- UTILITY
 
-    public static Predicate<_SingletonBeanProvider> satisfying(final Class<?> requiredType) {
+    public static Predicate<SingletonBeanProvider> satisfying(final Class<?> requiredType) {
         return singletonProvider->singletonProvider.isCandidateFor(requiredType);
     }
 
diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_IocContainer_Spring.java b/commons/src/main/java/org/apache/causeway/commons/internal/ioc/SpringContextHolder.java
similarity index 50%
rename from commons/src/main/java/org/apache/causeway/commons/internal/ioc/_IocContainer_Spring.java
rename to commons/src/main/java/org/apache/causeway/commons/internal/ioc/SpringContextHolder.java
index 41716dd386e..edae73de62a 100644
--- a/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_IocContainer_Spring.java
+++ b/commons/src/main/java/org/apache/causeway/commons/internal/ioc/SpringContextHolder.java
@@ -20,65 +20,92 @@
 
 import java.lang.annotation.Annotation;
 import java.util.Collections;
+import java.util.NoSuchElementException;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.jspecify.annotations.NonNull;
-import org.jspecify.annotations.Nullable;
-
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Primary;
 import org.springframework.util.ClassUtils;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.collections.Cardinality;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
 /**
- * Spring specific implementation of _IocContainer (package private)
+ * <h1>- internal use only -</h1>
+ * <p>
+ * Framework internal holder of Spring's {@link ApplicationContext}.
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/>
+ * These may be changed or removed without notice!
+ *
  * @since 2.0
  */
-record _IocContainer_Spring(
-        @NonNull ApplicationContext springContext
-        ) implements _IocContainer {
+public record SpringContextHolder(
+    @NonNull ApplicationContext springContext
+    ) {
 
-    @Override
-    public <T> Optional<T> get(final @NonNull Class<T> requiredType) {
-        var provider = springContext.getBeanProvider(requiredType);
-        try {
-            return Optional.ofNullable(provider.getIfUnique());
-        } catch (Exception cause) {
-            throw _Exceptions.unrecoverable(cause, "Failed to create an instance of type %s", requiredType);
-        }
-    }
-
-    @Override
-    public Stream<_SingletonBeanProvider> streamAllBeans() {
+    public Stream<SingletonBeanProvider> streamAllBeans() {
         return Stream.of(springContext.getBeanDefinitionNames())
                 .map(name->{
                     var type = ClassUtils.getUserClass(springContext.getType(name));
-                    var beanAdapter = new _SingletonBeanProvider(name, type, ()->springContext.getBean(name));
+                    var beanAdapter = new SingletonBeanProvider(name, type, ()->springContext.getBean(name));
                     return beanAdapter;
                 });
     }
 
-    @Override
     public boolean containsBean(final String id) {
         return springContext.containsBean(id);
     }
 
-    @Override
     public Optional<?> lookupBean(final String id) {
         return springContext.containsBean(id)
                 ? Optional.of(springContext.getBean(id))
                 : Optional.empty();
     }
 
-    @Override
+    /**
+     * Return an instance (possibly shared or independent) of the object managed by the IoC container.
+     * @param <T>
+     * @param requiredType
+     * @return an instance of the bean, or null if not available or not unique
+     * (i.e. multiple candidates found with none marked as primary)
+     * @throws RuntimeException if instance creation failed
+     */
+    public <T> Optional<T> get(final @NonNull Class<T> requiredType) {
+        var provider = springContext.getBeanProvider(requiredType);
+        try {
+            return Optional.ofNullable(provider.getIfUnique());
+        } catch (Exception cause) {
+            throw _Exceptions.unrecoverable(cause, "Failed to create an instance of type %s", requiredType);
+        }
+    }
+
+    /**
+     * Returns all available implementations of the service, ordered by priority.
+     *
+     * <p>If there is more than one implementation, then the one with the &quot;highest&quot;
+     * priority (either annotated with {@link org.springframework.context.annotation.Primary},
+     * else with encountered with earliest {@link org.apache.causeway.applib.annotation.PriorityPrecedence precedence})
+     * is used instead.
+     *
+     * @param <T> - the generic type parameter (to save the caller from having to downcast)
+     * @param requiredType - the required type
+     * @throws NoSuchElementException - if the singleton is not resolvable
+     *
+     * @see #select(Class, Annotation[])
+     * @see #getSingletonElseFail(Class)
+     */
+    @SuppressWarnings("javadoc")
     public <T> Can<T> select(final @NonNull Class<T> requiredType) {
         var allMatchingBeans = springContext.getBeanProvider(requiredType)
                 .orderedStream()
@@ -86,35 +113,68 @@ public <T> Can<T> select(final @NonNull Class<T> requiredType) {
         return allMatchingBeans;
     }
 
-    @Override
+    /**
+     * Returns all available implementations of the service that match the additional qualifiers, ordered by priority.
+     *
+     * <p>If there is more than one implementation, then the one with the &quot;highest&quot;
+     * priority (either annotated with {@link org.springframework.context.annotation.Primary},
+     * else with encountered with earliest {@link org.apache.causeway.applib.annotation.PriorityPrecedence precedence})
+     * is used instead.
+     *
+     * @param <T> - the generic type parameter (to save the caller from having to downcast)
+     * @param requiredType - the required type
+     * @param qualifiersRequired - if contains annotations, that are not qualifiers, these are just ignored
+     * @throws NoSuchElementException - if the singleton is not resolvable
+     *
+     * @see #select(Class)
+     */
+    @SuppressWarnings("javadoc")
     public <T> Can<T> select(
-            final @NonNull Class<T> requiredType,
-            final @Nullable Annotation[] qualifiers) {
+        final @NonNull Class<T> requiredType,
+        final @Nullable Annotation[] qualifiers) {
 
         var qualifiersRequired = filterQualifiers(qualifiers);
 
         if(_NullSafe.isEmpty(qualifiersRequired)) {
-
             var allMatchingBeans = springContext.getBeanProvider(requiredType)
-                    .orderedStream()
-                    .collect(Can.toCan());
-
+                .orderedStream()
+                .collect(Can.toCan());
             return allMatchingBeans;
         }
 
         var allMatchingBeans = springContext.getBeanProvider(requiredType)
-                .orderedStream()
-                .filter(t->{
-                    var qualifiersPresent = _Sets.of(t.getClass().getAnnotations());
-                    return qualifiersPresent.containsAll(qualifiersRequired);
-                })
-                .collect(Can.toCan());
-
+            .orderedStream()
+            .filter(t->{
+                var qualifiersPresent = _Sets.of(t.getClass().getAnnotations());
+                return qualifiersPresent.containsAll(qualifiersRequired);
+            })
+            .collect(Can.toCan());
         return allMatchingBeans;
+    }
 
+    /**
+     * Requires that there is AT LEAST one implementation of the service, and returns it.
+     *
+     * <p>If there is more than one implementation, then the one with the &quot;highest&quot;
+     * priority (either annotated with {@link org.springframework.context.annotation.Primary},
+     * else with encountered with earliest {@link org.apache.causeway.applib.annotation.PriorityPrecedence precedence})
+     * is used instead.
+     *
+     * @param type - the required type
+     * @param <T>  - the generic type parameter (to save the caller from having to downcast)
+     * @return IoC managed singleton
+     * @throws NoSuchElementException - if the singleton is not resolvable
+     */
+    @SuppressWarnings("javadoc")
+    public <T> T getSingletonElseFail(final @NonNull Class<T> type) {
+        var candidates = select(type);
+        if (candidates.getCardinality() == Cardinality.ZERO) {
+            throw _Exceptions.noSuchElement("Cannot resolve singleton '%s'", type);
+        }
+        return candidates.getFirstElseFail();
     }
 
-    // -- QUALIFIER PROCESSING
+    // -- HELPER - QUALIFIER PROCESSING
 
     /**
      * Filters the input array into a collection, such that only annotations are retained,
@@ -127,7 +187,7 @@ private static Set<Annotation> filterQualifiers(final @Nullable Annotation[] ann
             return Collections.emptySet();
         }
         return _NullSafe.stream(annotations)
-                .filter(_IocContainer_Spring::isGenericQualifier)
+                .filter(SpringContextHolder::isGenericQualifier)
                 .collect(Collectors.toSet());
     }
 
diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_IocContainer.java b/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_IocContainer.java
deleted file mode 100644
index 0268467859a..00000000000
--- a/commons/src/main/java/org/apache/causeway/commons/internal/ioc/_IocContainer.java
+++ /dev/null
@@ -1,129 +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.causeway.commons.internal.ioc;
-
-import java.lang.annotation.Annotation;
-import java.util.NoSuchElementException;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.springframework.context.ApplicationContext;
-
-import org.apache.causeway.commons.collections.Can;
-import org.apache.causeway.commons.collections.Cardinality;
-import org.apache.causeway.commons.internal.exceptions._Exceptions;
-
-import org.jspecify.annotations.NonNull;
-
-/**
- * <h1>- internal use only -</h1>
- * <p>
- * Framework internal IoC Container support.
- * <p>
- * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/>
- * These may be changed or removed without notice!
- *
- * @since 2.0
- */
-public interface _IocContainer {
-
-    Stream<_SingletonBeanProvider> streamAllBeans();
-
-    boolean containsBean(String id);
-    Optional<?> lookupBean(String id);
-
-    /**
-     * Return an instance (possibly shared or independent) of the object managed by the IoC container.
-     * @param <T>
-     * @param requiredType
-     * @return an instance of the bean, or null if not available or not unique
-     * (i.e. multiple candidates found with none marked as primary)
-     * @throws RuntimeException if instance creation failed
-     */
-    <T> Optional<T> get(Class<T> requiredType);
-
-    /**
-     * Returns all available implementations of the service, ordered by priority.
-     *
-     * <p>
-     *     If there is more than one implementation, then the one with the &quot;highest&quot;
-     *     priority (either annotated with {@link org.springframework.context.annotation.Primary},
-     *     else with encountered with earliest {@link org.apache.causeway.applib.annotation.PriorityPrecedence precedence})
-     *     is used instead.
-     * </p>
-     *
-     * @param <T> - the generic type parameter (to save the caller from having to downcast)
-     * @param requiredType - the required type
-     * @throws NoSuchElementException - if the singleton is not resolvable
-     *
-     * @see #select(Class, Annotation[])
-     * @see #getSingletonElseFail(Class)
-     */
-    <T> Can<T> select(Class<T> requiredType);
-
-    /**
-     * Returns all available implementations of the service that match the additional qualifiers, ordered by priority.
-     *
-     * <p>
-     *     If there is more than one implementation, then the one with the &quot;highest&quot;
-     *     priority (either annotated with {@link org.springframework.context.annotation.Primary},
-     *     else with encountered with earliest {@link org.apache.causeway.applib.annotation.PriorityPrecedence precedence})
-     *     is used instead.
-     * </p>
-     *
-     * @param <T> - the generic type parameter (to save the caller from having to downcast)
-     * @param requiredType - the required type
-     * @param qualifiersRequired - if contains annotations, that are not qualifiers, these are just ignored
-     * @throws NoSuchElementException - if the singleton is not resolvable
-     *
-     * @see #select(Class)
-     */
-    <T> Can<T> select(Class<T> requiredType, Annotation[] qualifiersRequired);
-
-    /**
-     * Requires that there is AT LEAST one implementation of the service, and returns it.
-     *
-     * <p>
-     *     If there is more than one implementation, then the one with the &quot;highest&quot;
-     *     priority (either annotated with {@link org.springframework.context.annotation.Primary},
-     *     else with encountered with earliest {@link org.apache.causeway.applib.annotation.PriorityPrecedence precedence})
-     *     is used instead.
-     * </p>
-     *
-     * @param type - the required type
-     * @param <T>  - the generic type parameter (to save the caller from having to downcast)
-     * @return IoC managed singleton
-     * @throws NoSuchElementException - if the singleton is not resolvable
-     */
-    default <T> T getSingletonElseFail(final @NonNull Class<T> type) {
-
-        var candidates = select(type);
-        if (candidates.getCardinality() == Cardinality.ZERO) {
-            throw _Exceptions.noSuchElement("Cannot resolve singleton '%s'", type);
-        }
-        return candidates.getFirstElseFail();
-    }
-
-    // -- FACTORIES
-
-    static _IocContainer spring(final ApplicationContext springContext) {
-        return new _IocContainer_Spring(springContext);
-    }
-
-}
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
index 6d9e3d32b43..3c5cb04f867 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
@@ -23,16 +23,15 @@
 import java.lang.annotation.Target;
 import java.net.URL;
 import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -59,6 +58,7 @@
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 
+import org.jspecify.annotations.NonNull;
 import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.annotation.Autowired;
@@ -67,10 +67,10 @@
 import org.springframework.core.env.ConfigurableEnvironment;
 import org.springframework.core.env.EnumerablePropertySource;
 import org.springframework.core.env.MutablePropertySources;
+import org.springframework.stereotype.Component;
 import org.springframework.validation.annotation.Validated;
 
 import org.apache.causeway.applib.CausewayModuleApplib;
-import org.apache.causeway.applib.annotation.ActionLayout;
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.DomainService;
 import org.apache.causeway.applib.annotation.Introspection.IntrospectionPolicy;
@@ -78,24 +78,17 @@
 import org.apache.causeway.applib.annotation.PromptStyle;
 import org.apache.causeway.applib.annotation.SemanticsOf;
 import org.apache.causeway.applib.annotation.TableDecorator;
-import org.apache.causeway.applib.query.Query;
 import org.apache.causeway.applib.services.i18n.Mode;
 import org.apache.causeway.applib.services.iactn.Execution;
-import org.apache.causeway.applib.services.publishing.spi.CommandSubscriber;
-import org.apache.causeway.applib.services.publishing.spi.EntityChangesSubscriber;
-import org.apache.causeway.applib.services.publishing.spi.EntityPropertyChangeSubscriber;
 import org.apache.causeway.applib.services.publishing.spi.ExecutionSubscriber;
-import org.apache.causeway.applib.services.userreg.EmailNotificationService;
-import org.apache.causeway.applib.services.userreg.UserRegistrationService;
-import org.apache.causeway.applib.services.userui.UserMenu;
 import org.apache.causeway.applib.value.semantics.TemporalValueSemantics.TemporalDisplayPattern;
 import org.apache.causeway.applib.value.semantics.TemporalValueSemantics.TemporalEditingPattern;
 import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.context._Context;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.config.metamodel.facets.ActionConfigOptions;
 import org.apache.causeway.core.config.metamodel.facets.AssociationLayoutConfigOptions;
-import org.apache.causeway.core.config.metamodel.facets.AssociationLayoutConfigOptions.SequencePolicy;
 import org.apache.causeway.core.config.metamodel.facets.CollectionLayoutConfigOptions;
 import org.apache.causeway.core.config.metamodel.facets.DomainObjectConfigOptions;
 import org.apache.causeway.core.config.metamodel.facets.ParameterConfigOptions;
@@ -107,9 +100,8 @@
 import org.apache.causeway.schema.cmd.v2.ActionDto;
 import org.apache.causeway.schema.cmd.v2.ParamDto;
 
-import lombok.Builder;
-import lombok.Data;
-import lombok.Getter;
+import org.springframework.boot.context.properties.bind.DefaultValue;
+import org.springframework.boot.convert.DurationUnit;
 
 /**
  * Configuration 'beans' with meta-data (IDE-support).
@@ -118,17 +110,12 @@
  *
  * @since 2.0
  */
-@ConfigurationProperties(CausewayConfiguration.ROOT_PREFIX)
-@Data
-@Validated
-public class CausewayConfiguration {
-
-    public static final String ROOT_PREFIX = "causeway";
-
-    private final ConfigurableEnvironment environment;
-
+@Component
+public record CausewayConfiguration(
+    @Autowired
+    ConfigurableEnvironment environment,
     /**
-     * To ensure that {@link #getBuildProperties()} is full populated, configure the <code>spring-boot-maven-plugin</code>
+     * To ensure that {@link #buildProperties()} is full populated, configure the <code>spring-boot-maven-plugin</code>
      * as follows:
      *
      * <pre>
@@ -154,33 +141,45 @@ public class CausewayConfiguration {
      * &lt;/plugin&gt;
      * </pre>
      */
-    @Getter
-    private final Optional<BuildProperties> buildProperties;
-
-    @Builder // for testing
     @Autowired
-    public CausewayConfiguration(final ConfigurableEnvironment environment, final Optional<BuildProperties> buildProperties) {
-        this.environment = environment;
-        this.buildProperties = buildProperties;
+    Optional<BuildProperties> buildProperties,
+    @Autowired
+    Causeway causeway
+    ) {
+
+    // TODO remove
+    @Deprecated
+    public static CausewayConfiguration defaults() {
+        throw _Exceptions.unsupportedOperation();
     }
 
+    public static final String ROOT_PREFIX = "causeway";
+
+
+    //@Builder // for testing
+    //     @Autowired
+    //     public CausewayConfiguration(final ConfigurableEnvironment environment, final Optional<BuildProperties> buildProperties) {
+    //         this.environment = environment;
+    //         this.buildProperties = buildProperties;
+    //     }
+
     /**
      * All known configuration property names.
      *
      * <p>
      *     Or at least, from the {@link org.springframework.core.env.PropertySource} obtained from
      *     {@link ConfigurableEnvironment#getPropertySources()} that are also {@link EnumerablePropertySource}s.
-     * </p>
+     *
      */
     public Stream<String> streamConfigurationPropertyNames() {
         MutablePropertySources propertySources = environment.getPropertySources();
         return StreamSupport
-                .stream(propertySources.spliterator(), false)
-                .filter(EnumerablePropertySource.class::isInstance)
-                .map(EnumerablePropertySource.class::cast)
-                .filter(ps->!"systemEnvironment".equalsIgnoreCase(ps.getName())) // exclude system env
-                .map(EnumerablePropertySource::getPropertyNames)
-                .flatMap(_NullSafe::stream);
+            .stream(propertySources.spliterator(), false)
+            .filter(EnumerablePropertySource.class::isInstance)
+            .map(EnumerablePropertySource.class::cast)
+            .filter(ps->!"systemEnvironment".equalsIgnoreCase(ps.getName())) // exclude system env
+            .map(EnumerablePropertySource::getPropertyNames)
+            .flatMap(_NullSafe::stream);
     }
 
     /**
@@ -207,41 +206,62 @@ public Optional<String> valueOf(final String configurationPropertyName, final @N
             }
             return Optional.empty();
         }
-        
     }
 
-    private final Security security = new Security();
-    @Data
-    public static class Security {
+    public Security security() {return causeway.security(); }
+    public Schema schema() {return causeway.schema(); }
+    public Applib applib() {return causeway.applib(); }
+    public Core core() {return causeway.core(); }
+    public Persistence persistence() {return causeway.persistence(); }
+    public Prototyping prototyping() {return causeway.prototyping(); }
+    public Viewer viewer() {return causeway.viewer(); }
+    public ValueTypes valueTypes() {return causeway.valueTypes(); }
+    public Testing testing() {return causeway.testing(); }
+    public Extensions extensions() {return causeway.extensions(); }
+
+    @ConfigurationProperties(CausewayConfiguration.ROOT_PREFIX)
+    @Validated
+    public record Causeway(
+        @DefaultValue
+        Security security,
+        @DefaultValue
+        Schema schema,
+        @DefaultValue
+        Applib applib,
+        @DefaultValue
+        Core core,
+        @DefaultValue
+        Persistence persistence,
+        @DefaultValue
+        Prototyping prototyping,
+        @DefaultValue
+        Viewer viewer,
+        @DefaultValue
+        ValueTypes valueTypes,
+        @DefaultValue
+        Testing testing,
+        @DefaultValue
+        Extensions extensions
+        ) {
+    }
 
+    public record Security(
         /**
          * If set, allows <i>Actions</i> with SAFE Semantics to be invoked with only VIEWING permissions.
-         * <p>
-         * default: false
+         *
+         * <p>default: false
          *
          * @see org.apache.causeway.applib.annotation.Action#semantics()
          * @see SemanticsOf
          */
-        private boolean actionsWithSafeSemanticsRequireOnlyViewingPermission = false;
-
-        private final Shiro shiro = new Shiro();
-        @Data
-        public static class Shiro {
-            /**
-             * If the Shiro subject is found to be still authenticated, then will be logged out anyway and then
-             * re-authenticated.
-             *
-             * <p>
-             * Applies only to the Restful Objects viewer.
-             * </p>
-             */
-            private boolean autoLogoutIfAlreadyAuthenticated = false;
-
-        }
-
-        private final Spring spring = new Spring();
-        @Data
-        public static class Spring {
+        @DefaultValue("false")
+        boolean actionsWithSafeSemanticsRequireOnlyViewingPermission,
+        @DefaultValue
+        Spring spring,
+        @DefaultValue
+        Keycloak keycloak) {
+
+        public record Spring(
             /**
              * The framework on initialization by default disables any {@code CsrfFilter}(s) it finds
              * with <i>Spring Security</i> registered filters.
@@ -252,24 +272,22 @@ public static class Spring {
              * @see org.springframework.security.web.csrf.CsrfFilter
              * @see <a href="https://www.baeldung.com/spring-security-registered-filters">baeldung</a>
              */
-            private boolean allowCsrfFilters = false;
-
+            @SuppressWarnings("javadoc")
+            @DefaultValue("false")
+            boolean allowCsrfFilters) {
         }
 
-        private final Keycloak keycloak = new Keycloak();
-        @Data
-        public static class Keycloak {
+        public record Keycloak(
             /**
              * The name of the realm for the Apache Causeway application, as configured in
              * Keycloak.
              */
-            private String realm;
+            String realm,
 
             /**
              * The base URL for the keycloak server.
              *
-             * <p>
-             *     For example, if running a keycloak using Docker container, such as:
+             * <p> For example, if running a keycloak using Docker container, such as:
              *     <pre>
              *         docker run -p 9090:8080 \
              *             -e KEYCLOAK_USER=admin \
@@ -278,118 +296,140 @@ public static class Keycloak {
              *     </pre>,
              *
              *     then the URL would be "http://localhost:9090/auth".
-             * </p>
+             *
              */
-            private String baseUrl;
+            String baseUrl,
 
             /**
              * Specifies where users will be redirected after authenticating successfully if they
              * have not visited a secured page prior to authenticating or {@code alwaysUse} is
              * true.
              */
-            private String loginSuccessUrl = "/wicket";
+            @DefaultValue("/wicket")
+            String loginSuccessUrl,
 
             /**
              * Whether to (attempt to) extract realm roles and copy into the <code>DefaultOidcUser</code>.
              *
-             * <p>
-             *     By default, realm roles are obtained from the token claims using the "User Realm Role" mapping type, into a token claim name "realm_access.roles"
-             * </p>
+             * <p>By default, realm roles are obtained from the token claims using the "User Realm Role" mapping type, into a token claim name "realm_access.roles"
              *
-             * <p>
-             *     This has been made a configuration option because some versions of Keycloak seemingly do not correctly extract these roles, see for example
-             *     <a href="https://keycloak.discourse.group/t/resource-access-claim-missing-from-userinfo-until-i-change-the-name/1238/3">this discussion</a> and
-             *     <a href="https://issues.redhat.com/browse/KEYCLOAK-9874">KEYCLOAK-9874</a>.
-             * </p>
+             * <p>This has been made a configuration option because some versions of Keycloak seemingly do not correctly extract these roles, see for example
+             * <a href="https://keycloak.discourse.group/t/resource-access-claim-missing-from-userinfo-until-i-change-the-name/1238/3">this discussion</a> and
+             * <a href="https://issues.redhat.com/browse/KEYCLOAK-9874">KEYCLOAK-9874</a>.
              */
-            private boolean extractRealmRoles = true;
+            @DefaultValue("true")
+            boolean extractRealmRoles,
 
             /**
              * If {@link #isExtractRealmRoles() realm roles are to be extracted}, this allows the resultant role to be optionally prefixed.
              */
-            private String realmRolePrefix = null;
+            String realmRolePrefix,
 
             /**
              * Whether to (attempt to) extract client roles and copy into the <code>DefaultOidcUser</code>.
              *
              * <p>
              *     By default, client roles are extracted using the "User Client Role" mapping type, into a token claim name "resource_access.${client_id}.roles"
-             * </p>
+             *
              *
              * <p>
              *     This has been made a configuration option because some versions of Keycloak seemingly do not correctly extract these roles, see for example
              *     <a href="https://keycloak.discourse.group/t/resource-access-claim-missing-from-userinfo-until-i-change-the-name/1238/3">this discussion</a> and
              *     <a href="https://issues.redhat.com/browse/KEYCLOAK-9874">KEYCLOAK-9874</a>.
-             * </p>
+             *
              */
-            private boolean extractClientRoles = true;
+            @DefaultValue("true")
+            boolean extractClientRoles,
             /**
              * If {@link #isExtractClientRoles()}  client roles are to be extracted}, this allows the resultant role to be optionally prefixed.
              */
-            private String clientRolePrefix = null;
+            String clientRolePrefix,
 
             /**
              * Whether to (attempt to) extract any available roles and into the <code>DefaultOidcUser</code>.
              *
              * <p>
              *     This is to support any custom mapping type which maps into a token claim name called simply "roles"
-             * </p>
+             *
              *
              * <p>
              *     This has been made a configuration option so that the workaround described in
              *     <a href="https://keycloak.discourse.group/t/resource-access-claim-missing-from-userinfo-until-i-change-the-name/1238/3">this discussion</a> and
              *     <a href="https://issues.redhat.com/browse/KEYCLOAK-9874">KEYCLOAK-9874</a> can be implemented.
-             * </p>
+             *
              */
-            private boolean extractRoles = false;
+            @DefaultValue("false")
+            boolean extractRoles,
             /**
-             * If {@link #isExtractRoles()}  roles are to be extracted}, this allows the resultant role to be optionally prefixed.
+             * If {@link #extractRoles()}  roles are to be extracted}, this allows the resultant role to be optionally prefixed.
              */
-            private String rolePrefix = null;
+            String rolePrefix) {
         }
     }
 
-    private final Schema schema = new Schema();
-    @Data
-    public static class Schema {
-
-        private final Command command = new Command();
-        @Data
-        public static class Command {
-
-            public enum ParamIdentifierStrategy {
-                BY_ID,
-                /**
-                 * For backward compatibility with v1 behaviour
-                 */
-                BY_CANONICAL_FRIENDLY_NAME;
-            }
+    public record Schema(
+        @DefaultValue
+        Command command) {
 
+        public record Command(
             /**
              * Whether the {@link ParamDto#getName()} field - which uniquely identifies a parameter within the
              * {@link org.apache.causeway.schema.cmd.v2.ActionDto action}'s
              * {@link ActionDto#getParameters() list of parameters} - is populated with the parameter's formal Id
-             * (eg &quot;firstName&quot;) or instead using the parameter's friendly name (eg &quot;First Name&quot;).
+             * (eg 'firstName') or instead using the parameter's friendly name (eg 'First Name').
              *
              * <p>
              *     The default is to use the {@link ParamIdentifierStrategy#BY_ID formal Id}, but the name is provided
              *     as an alternative for compatibility with v1.  Note that the name is potentially translated, so this
              *     could also cause issues within integration scenarios.
-             * </p>
+             *
              */
-            private ParamIdentifierStrategy paramIdentifierStrategy = ParamIdentifierStrategy.BY_ID;
-        }
-    }
+            @DefaultValue("BY_ID")
+            ParamIdentifierStrategy paramIdentifierStrategy) {
 
-    private final Applib applib = new Applib();
-    @Data
-    public static class Applib {
+            public enum ParamIdentifierStrategy {
+                BY_ID,
+                /**
+                 * For backward compatibility with v1 behaviour
+                 */
+                BY_CANONICAL_FRIENDLY_NAME;
+            }
 
-        private final Annotation annotation = new Annotation();
-        @Data
-        public static class Annotation {
+        }
+    }
 
-            private final DomainObject domainObject = new DomainObject();
+    public record Applib(
+        @DefaultValue
+        Annotation annotation,
+        @DefaultValue
+        Service service) {
+
+        public record Annotation(
+            @DefaultValue
+            DomainObject domainObject,
+            @DefaultValue
+            DomainObjectLayout domainObjectLayout,
+            @DefaultValue
+            Action action,
+            @DefaultValue
+            ActionLayout actionLayout,
+            @DefaultValue
+            Parameter parameter,
+            @DefaultValue
+            ParameterLayout parameterLayout,
+            @DefaultValue
+            Property property,
+            @DefaultValue
+            PropertyLayout propertyLayout,
+            @DefaultValue
+            Collection collection,
+            @DefaultValue
+            CollectionLayout collectionLayout,
+            @DefaultValue
+            ViewModel viewModel,
+            @DefaultValue
+            ViewModelLayout viewModelLayout
+            ) {
 
             public interface ConfigPropsForPropertyOrParameterLayout {
                 /**
@@ -398,14 +438,12 @@ public interface ConfigPropsForPropertyOrParameterLayout {
                  * <p>
                  *     If left as {@link LabelPosition#NOT_SPECIFIED} and not overridden, then the position depends
                  *     upon the viewer implementation.
-                 * </p>
+                 *
                  */
                 LabelPosition getLabelPosition();
             }
 
-            @Data
-            public static class DomainObject {
-
+            public record DomainObject(
                 /**
                  * The default for whether <i>domain entities</i> should be audited or not (meaning that any changes are
                  * sent through to {@link EntityChangesSubscriber}s and
@@ -413,27 +451,42 @@ public static class DomainObject {
                  *
                  * <p>
                  * This setting can be overridden on a case-by-case basis using {@link org.apache.causeway.applib.annotation.DomainObject#entityChangePublishing()}
-                 * </p>
+                 *
                  *
                  * <p>
                  *     Note: this applies only to domain entities, not view models.
-                 * </p>
+                 *
                  */
-                private DomainObjectConfigOptions.EntityChangePublishingPolicy entityChangePublishing = DomainObjectConfigOptions.EntityChangePublishingPolicy.NONE;
+                @DefaultValue("NONE")
+                DomainObjectConfigOptions.EntityChangePublishingPolicy entityChangePublishing,
 
                 /**
                  * The default for whether the properties of domain objects can be edited, or whether instead they
                  * can be modified only using actions (or programmatically as a side-effect of actions on other objects).
                  *
                  * <p>
-                 * This setting can be overridden on a case-by-case basis using {@link DomainObject#getEditing()  DomainObject#getEditing()}
-                 * </p>
+                 * This setting can be overridden on a case-by-case basis using {@link DomainObject#editing()}
+                 *
                  */
-                private DomainObjectConfigOptions.EditingObjectsConfiguration editing = DomainObjectConfigOptions.EditingObjectsConfiguration.FALSE;
-
-                private final CreatedLifecycleEvent createdLifecycleEvent = new CreatedLifecycleEvent();
-                @Data
-                public static class CreatedLifecycleEvent {
+                @DefaultValue("FALSE")
+                DomainObjectConfigOptions.EditingObjectsConfiguration editing,
+
+                @DefaultValue
+                CreatedLifecycleEvent createdLifecycleEvent,
+                @DefaultValue
+                LoadedLifecycleEvent loadedLifecycleEvent,
+                @DefaultValue
+                PersistingLifecycleEvent persistingLifecycleEvent,
+                @DefaultValue
+                RemovingLifecycleEvent removingLifecycleEvent,
+                @DefaultValue
+                PersistedLifecycleEvent persistedLifecycleEvent,
+                @DefaultValue
+                UpdatingLifecycleEvent updatingLifecycleEvent,
+                @DefaultValue
+                UpdatedLifecycleEvent updatedLifecycleEvent) {
+
+                public record CreatedLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectCreatedEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -443,7 +496,7 @@ public static class CreatedLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#createdLifecycleEvent() @DomainObject(createdLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -461,12 +514,11 @@ public static class CreatedLifecycleEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final LoadedLifecycleEvent loadedLifecycleEvent = new LoadedLifecycleEvent();
-                @Data
-                public static class LoadedLifecycleEvent {
+                public record LoadedLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectLoadedEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -476,7 +528,7 @@ public static class LoadedLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#loadedLifecycleEvent() @DomainObject(loadedLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -496,14 +548,13 @@ public static class LoadedLifecycleEvent {
                      *
                      * <p>
                      *     Note: this applies only to domain entities, not to view models.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final PersistingLifecycleEvent persistingLifecycleEvent = new PersistingLifecycleEvent();
-                @Data
-                public static class PersistingLifecycleEvent {
+                public record PersistingLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectPersistingEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -513,7 +564,7 @@ public static class PersistingLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#persistingLifecycleEvent() @DomainObject(persistingLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -533,14 +584,13 @@ public static class PersistingLifecycleEvent {
                      *
                      * <p>
                      *     Note: this applies only to domain entities, not to view models.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final PersistedLifecycleEvent persistedLifecycleEvent = new PersistedLifecycleEvent();
-                @Data
-                public static class PersistedLifecycleEvent {
+                public record PersistedLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectPersistedEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -550,7 +600,7 @@ public static class PersistedLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#persistedLifecycleEvent() @DomainObject(persistedLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -570,14 +620,13 @@ public static class PersistedLifecycleEvent {
                      *
                      * <p>
                      *     Note: this applies only to domain entities, not to view models.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final RemovingLifecycleEvent removingLifecycleEvent = new RemovingLifecycleEvent();
-                @Data
-                public static class RemovingLifecycleEvent {
+                public record RemovingLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectRemovingEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -588,7 +637,7 @@ public static class RemovingLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#removingLifecycleEvent() @DomainObject(removingLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -608,19 +657,18 @@ public static class RemovingLifecycleEvent {
                      *
                      * <p>
                      *     Note: this applies only to domain entities, not to view models.
-                     * </p>
+                     *
                      *
                      * <p>
                      *     Note: There is no corresponding <code>removed</code> callback, because (for the JDO persistence store at least)
                      *     it is not possible to interact with a domain entity once it has been deleted.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final UpdatedLifecycleEvent updatedLifecycleEvent = new UpdatedLifecycleEvent();
-                @Data
-                public static class UpdatedLifecycleEvent {
+                public record UpdatedLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectUpdatedEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -630,7 +678,7 @@ public static class UpdatedLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#updatedLifecycleEvent() @DomainObject(updatedLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -650,14 +698,13 @@ public static class UpdatedLifecycleEvent {
                      *
                      * <p>
                      *     Note: this applies only to domain entities, not to view models.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final UpdatingLifecycleEvent updatingLifecycleEvent = new UpdatingLifecycleEvent();
-                @Data
-                public static class UpdatingLifecycleEvent {
+                public record UpdatingLifecycleEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -667,7 +714,7 @@ public static class UpdatingLifecycleEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObject#updatingLifecycleEvent() @DomainObject(updatingLifecycleEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -687,36 +734,41 @@ public static class UpdatingLifecycleEvent {
                      *
                      * <p>
                      *     Note: this applies only to domain entities, not to view models.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
             }
 
-            private final DomainObjectLayout domainObjectLayout = new DomainObjectLayout();
-            @Data
-            public static class DomainObjectLayout {
-
+            public record DomainObjectLayout(
                 /**
-                 * Defines the default number of objects that are shown in a &quot;standalone&quot; collection obtained as the
+                 * Defines the default number of objects that are shown in a 'standalone' collection obtained as the
                  * result of invoking an action.
                  *
                  * <p>
                  *     This can be overridden on a case-by-case basis using {@link org.apache.causeway.applib.annotation.DomainObjectLayout#paged()}.
-                 * </p>
+                 *
                  */
-                private int paged = 25;
-
+                @DefaultValue("25")
+                int paged,
                 /**
                  * Defines whether the table representation of a standalone collection of this domain class should be
                  * decorated using a client-side Javascript library, eg for client-side paging and filtering.
                  */
-                private Class<? extends TableDecorator> tableDecorator = TableDecorator.Default.class;
-
-                private final CssClassUiEvent cssClassUiEvent = new CssClassUiEvent();
-                @Data
-                public static class CssClassUiEvent {
+                @DefaultValue("org.apache.causeway.applib.annotation.TableDecorator.Default")
+                Class<? extends TableDecorator> tableDecorator,
+                @DefaultValue
+                CssClassUiEvent cssClassUiEvent,
+                @DefaultValue
+                IconUiEvent iconUiEvent,
+                @DefaultValue
+                LayoutUiEvent layoutUiEvent,
+                @DefaultValue
+                TitleUiEvent titleUiEvent) {
+
+                public record CssClassUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.CssClassUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -728,7 +780,7 @@ public static class CssClassUiEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#cssClassUiEvent()}  @DomainObjectLayout(cssClassEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -750,14 +802,13 @@ public static class CssClassUiEvent {
                      *     The default is <tt>false</tt>, because otherwise the mere presence of <tt>@DomainObjectLayout</tt>
                      *     (perhaps for some attribute other than this one) will cause any imperative <code>cssClass()</code>
                      *     method to be ignored.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = false;
+                    @DefaultValue("false")
+                    boolean postForDefault) {
                 }
 
-                private final IconUiEvent iconUiEvent = new IconUiEvent();
-                @Data
-                public static class IconUiEvent {
+                public record IconUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.IconUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -769,7 +820,7 @@ public static class IconUiEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#iconUiEvent()}  @DomainObjectLayout(iconEvent=...)} for the
                      *     domain object in question.
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -791,14 +842,13 @@ public static class IconUiEvent {
                      *     The default is <tt>false</tt>, because otherwise the mere presence of <tt>@DomainObjectLayout</tt>
                      *     (perhaps for some attribute other than this one) will cause any imperative <code>iconName()</code>
                      *     method to be ignored.
-                     * </p>
+                     *
                      */
-                    private boolean postForDefault = false;
+                    @DefaultValue("false")
+                    boolean postForDefault) {
                 }
 
-                private final LayoutUiEvent layoutUiEvent = new LayoutUiEvent();
-                @Data
-                public static class LayoutUiEvent {
+                public record LayoutUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.LayoutUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -806,16 +856,12 @@ public static class LayoutUiEvent {
                      * optionally {@link org.apache.causeway.applib.events.ui.LayoutUiEvent#setLayout(String)} change)
                      * the layout that is used.
                      *
-                     * <p>
-                     *     If a different layout value has been set, then a layout in the form <code>Xxx.layout-zzz.xml</code>
-                     *     use used (where <code>zzz</code> is the name of the layout).
-                     * </p>
+                     * <p>If a different layout value has been set, then a layout in the form <code>Xxx.layout-zzz.xml</code>
+                     * use used (where <code>zzz</code> is the name of the layout).
                      *
-                     * <p>
-                     *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
-                     *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#layoutUiEvent()}  @DomainObjectLayout(layoutEvent=...)} for the
-                     *     domain object in question.
-                     * </p>
+                     * <p>The algorithm for determining whether (and what type of) an event is sent depends on the value of the
+                     * {@link org.apache.causeway.applib.annotation.DomainObjectLayout#layoutUiEvent()}  @DomainObjectLayout(layoutEvent=...)} for the
+                     * domain object in question.
                      *
                      * <ul>
                      *     <li>
@@ -833,18 +879,16 @@ public static class LayoutUiEvent {
                      *     </li>
                      * </ul>
                      *
-                     * <p>
-                     *     The default is <tt>false</tt>, because otherwise the mere presence of <tt>@DomainObjectLayout</tt>
-                     *     (perhaps for some attribute other than this one) will cause any imperative <code>layout()</code>
-                     *     method to be ignored.
-                     * </p>
+                     * <p>The default is <tt>false</tt>, because otherwise the mere presence of <tt>@DomainObjectLayout</tt>
+                     * (perhaps for some attribute other than this one) will cause any imperative <code>layout()</code>
+                     * method to be ignored.
                      */
-                    private boolean postForDefault = false;
+                    @DefaultValue("false")
+                    boolean postForDefault
+                    ) {
                 }
 
-                private final TitleUiEvent titleUiEvent = new TitleUiEvent();
-                @Data
-                public static class TitleUiEvent {
+                public record TitleUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.TitleUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -852,11 +896,9 @@ public static class TitleUiEvent {
                      * optionally {@link org.apache.causeway.applib.events.ui.TitleUiEvent#setTitle(String)} change)
                      * the title that is used.
                      *
-                     * <p>
-                     *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
-                     *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#titleUiEvent()}  @DomainObjectLayout(titleEvent=...)} for the
-                     *     domain object in question.
-                     * </p>
+                     * <p>The algorithm for determining whether (and what type of) an event is sent depends on the value of the
+                     * {@link org.apache.causeway.applib.annotation.DomainObjectLayout#titleUiEvent()}  @DomainObjectLayout(titleEvent=...)} for the
+                     * domain object in question.
                      *
                      * <ul>
                      *     <li>
@@ -874,20 +916,16 @@ public static class TitleUiEvent {
                      *     </li>
                      * </ul>
                      *
-                     * <p>
-                     *     The default is <tt>false</tt>, because otherwise the mere presence of <tt>@DomainObjectLayout</tt>
-                     *     (perhaps for some attribute other than this one) will cause any imperative <code>title()</code>
-                     *     method to be ignored.
-                     * </p>
+                     * <p>The default is <tt>false</tt>, because otherwise the mere presence of <tt>@DomainObjectLayout</tt>
+                     * (perhaps for some attribute other than this one) will cause any imperative <code>title()</code>
+                     * method to be ignored.
                      */
-                    private boolean postForDefault = false;
+                    @DefaultValue("false")
+                    boolean postForDefault) {
                 }
             }
 
-            private final Action action = new Action();
-            @Data
-            public static class Action {
-
+            public record Action(
                 /**
                  * The default for whether action invocations should be reified
                  * as a {@link org.apache.causeway.applib.services.command.Command},
@@ -895,51 +933,44 @@ public static class Action {
                  * {@link CommandSubscriber}s,
                  * typically for auditing purposes.
                  *
-                 * <p>
-                 *  This setting can be overridden on a case-by-case basis using
-                 *  {@link org.apache.causeway.applib.annotation.Action#commandPublishing()}.
-                 * </p>
+                 * <p>This setting can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.Action#commandPublishing()}.
+                 *
                  */
-                private ActionConfigOptions.PublishingPolicy commandPublishing = ActionConfigOptions.PublishingPolicy.NONE;
-
+                @DefaultValue("NONE")
+                ActionConfigOptions.PublishingPolicy commandPublishing,
                 /**
                  * The default for whether action invocations should be sent through to the
                  * {@link ExecutionSubscriber} for publishing.
                  *
-                 * <p>
-                 *     The service's {@link ExecutionSubscriber#onExecution(Execution) onExecution}
-                 *     method is called only once per transaction, with
-                 *     {@link Execution} collecting details of
-                 *     the identity of the target object, the action invoked, the action arguments and the returned
-                 *     object (if any).
-                 * </p>
+                 * <p>The service's {@link ExecutionSubscriber#onExecution(Execution) onExecution}
+                 * method is called only once per transaction, with
+                 * {@link Execution} collecting details of
+                 * the identity of the target object, the action invoked, the action arguments and the returned
+                 * object (if any).
+                 *
+                 * <p>This setting can be overridden on a case-by-case basis using {@link org.apache.causeway.applib.annotation.Action#executionPublishing()  Action#executionPublishing()}.
                  *
-                 * <p>
-                 *  This setting can be overridden on a case-by-case basis using {@link org.apache.causeway.applib.annotation.Action#executionPublishing()  Action#executionPublishing()}.
-                 * </p>
                  */
-                private ActionConfigOptions.PublishingPolicy executionPublishing = ActionConfigOptions.PublishingPolicy.NONE;
+                @DefaultValue("NONE")
+                ActionConfigOptions.PublishingPolicy executionPublishing,
+                @DefaultValue
+                DomainEvent domainEvent) {
 
-                private final DomainEvent domainEvent = new DomainEvent();
-                @Data
-                public static class DomainEvent {
+                public record DomainEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.domain.ActionDomainEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
                      * whenever an action is being interacted with.
                      *
-                     * <p>
-                     *     Up to five different events can be fired during an interaction, with the event's
-                     *     {@link org.apache.causeway.applib.events.domain.ActionDomainEvent#getEventPhase() phase}
-                     *     determining which (hide, disable, validate, executing and executed).  Subscribers can
-                     *     influence the behaviour at each of these phases.
-                     * </p>
+                     * <p>Up to five different events can be fired during an interaction, with the event's
+                     * {@link org.apache.causeway.applib.events.domain.ActionDomainEvent#getEventPhase() phase}
+                     * determining which (hide, disable, validate, executing and executed).  Subscribers can
+                     * influence the behaviour at each of these phases.
                      *
-                     * <p>
-                     *     The algorithm for determining whether (and what type of) an event is actually sent depends
-                     *     on the value of the {@link org.apache.causeway.applib.annotation.Action#domainEvent()} for the
-                     *     action in question
-                     * </p>
+                     * <p>The algorithm for determining whether (and what type of) an event is actually sent depends
+                     * on the value of the {@link org.apache.causeway.applib.annotation.Action#domainEvent()} for the
+                     * action in question
                      *
                      * <ul>
                      *     <li>
@@ -957,194 +988,182 @@ public static class DomainEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
-
             }
 
-            private final ActionLayout actionLayout = new ActionLayout();
-            @Data
-            public static class ActionLayout {
+            public record ActionLayout(
+                @DefaultValue
+                CssClass cssClass,
+                @DefaultValue
+                CssClassFa cssClassFa) {
 
-                private final CssClass cssClass = new CssClass();
-                @Data
-                public static class CssClass {
+                public record CssClass(
                     /**
                      * Provides a mapping of patterns to CSS classes, where the pattern is used to match against the
                      * name of the action method in order to determine a CSS class to use, for example on the action's
                      * button if rendered by the Wicket viewer.
                      *
-                     * <p>
-                     *     Providing a default set of patterns encourages a common set of verbs to be used.
-                     * </p>
+                     * <p>Providing a default set of patterns encourages a common set of verbs to be used.
                      *
-                     * <p>
-                     *     The CSS class for individual actions can be overridden using
-                     *     {@link org.apache.causeway.applib.annotation.ActionLayout#cssClass()}.
-                     * </p>
+                     * <p>The CSS class for individual actions can be overridden using
+                     * {@link org.apache.causeway.applib.annotation.ActionLayout#cssClass()}.
                      */
-                    private String[] patterns = {
-                            "add.*:btn-info",
-                            "remove.*:btn-warning",
-
-                            "start.*:btn-info",
-                            "play.*:btn-info",
-                            "stop.*:btn-warning",
+                    @DefaultValue({
+                        "add.*:btn-info",
+                        "remove.*:btn-warning",
 
-                            "reset.*:btn-warning",
+                        "start.*:btn-info",
+                        "play.*:btn-info",
+                        "stop.*:btn-warning",
 
-                            "new.*:btn-info",
-                            "create.*:btn-info",
-                            "delete.*:btn-danger",
+                        "reset.*:btn-warning",
 
-                            "verify.*:btn-success",
-                            "decline.*:btn-danger",
+                        "new.*:btn-info",
+                        "create.*:btn-info",
+                        "delete.*:btn-danger",
 
-                            "save.*:btn-success",
+                        "verify.*:btn-success",
+                        "decline.*:btn-danger",
 
-                            "approve.*:btn-success",
-                            "reject.*:btn-danger",
+                        "save.*:btn-success",
 
-                    };
+                        "approve.*:btn-success",
+                        "reject.*:btn-danger",
 
-                    @Getter(lazy = true)
-                    private final Map<Pattern, String> patternsAsMap = asMap(getPatterns());
+                    })
+                    String[] patterns) {
 
+                    //(lazy = true) //TODO
+                    public Map<Pattern, String> patternsAsMap() { return asMap(patterns()); }
                 }
 
-                private final CssClassFa cssClassFa = new CssClassFa();
-                @Data
-                public static class CssClassFa {
+                public record CssClassFa(
                     /**
                      * Provides a mapping of patterns to font-awesome CSS classes, where the pattern is used to match
                      * against the name of the action method in order to determine a CSS class to use, for example on
                      * the action's menu icon if rendered by the Wicket viewer.
                      *
-                     * <p>
-                     *     Providing a default set of patterns encourages a common set of verbs to be used.
-                     * </p>
+                     * <p>Providing a default set of patterns encourages a common set of verbs to be used.
                      *
-                     * <p>
-                     *     The font awesome class for individual actions can be overridden using
-                     *     {@link org.apache.causeway.applib.annotation.ActionLayout#cssClassFa()}.
-                     * </p>
+                     * <p>The font awesome class for individual actions can be overridden using
+                     * {@link org.apache.causeway.applib.annotation.ActionLayout#cssClassFa()}.
                      */
-                    private String[] patterns = {
+                    @DefaultValue({
 
-                            "all.*:fa-solid fa-list",
-                            "list.*:fa-solid fa-list",
+                        "all.*:fa-solid fa-list",
+                        "list.*:fa-solid fa-list",
 
-                            "find.*:fa-search",
-                            "lookup.*:fa-search",
-                            "search.*:fa-search",
+                        "find.*:fa-search",
+                        "lookup.*:fa-search",
+                        "search.*:fa-search",
 
-                            "send.*:fa-regular fa-paper-plane",
+                        "send.*:fa-regular fa-paper-plane",
 
-                            "open.*:fa-solid fa-arrow-up-right-from-square",
-                            "close.*:fa-solid fa-regular fa-rectangle-xmark",
+                        "open.*:fa-solid fa-arrow-up-right-from-square",
+                        "close.*:fa-solid fa-regular fa-rectangle-xmark",
 
-                            "recent.*:fa-solid fa-clock-rotate-left",
+                        "recent.*:fa-solid fa-clock-rotate-left",
 
-                            "lock.*:fa-solid fa-lock",
-                            "unlock.*:fa-solid fa-unlock",
+                        "lock.*:fa-solid fa-lock",
+                        "unlock.*:fa-solid fa-unlock",
 
-                            "permit.*:fa-solid fa-unlock",
-                            "review.*:fa-solid fa-eye",
+                        "permit.*:fa-solid fa-unlock",
+                        "review.*:fa-solid fa-eye",
 
-                            "add.*:fa-regular fa-square-plus",
-                            "plus.*:fa-regular fa-square-plus",
-                            "remove.*:fa-regular fa-square-minus",
-                            "minus.*:fa-regular fa-square-minus",
+                        "add.*:fa-regular fa-square-plus",
+                        "plus.*:fa-regular fa-square-plus",
+                        "remove.*:fa-regular fa-square-minus",
+                        "minus.*:fa-regular fa-square-minus",
 
-                            "sign.*:fa-solid fa-signature",
+                        "sign.*:fa-solid fa-signature",
 
-                            "clear.*:fa-solid fa-broom",
+                        "clear.*:fa-solid fa-broom",
 
-                            "create.*:fa-regular fa-square-plus",
-                            "new.*:fa-regular fa-square-plus",
-                            "delete.*:fa-solid fa-trash",
+                        "create.*:fa-regular fa-square-plus",
+                        "new.*:fa-regular fa-square-plus",
+                        "delete.*:fa-solid fa-trash",
 
-                            "change.*:fa-regular fa-pen-to-square",
-                            "edit.*:fa-regular fa-pen-to-square",
-                            "maintain.*:fa-regular fa-pen-to-square",
-                            "update.*:fa-regular fa-pen-to-square",
+                        "change.*:fa-regular fa-pen-to-square",
+                        "edit.*:fa-regular fa-pen-to-square",
+                        "maintain.*:fa-regular fa-pen-to-square",
+                        "update.*:fa-regular fa-pen-to-square",
 
-                            "cut.*:fa-solid fa-scissors",
-                            "move.*:fa-solid fa-angles-right",
-                            "copy.*:fa-regular fa-copy",
-                            "duplicate.*:fa-solid fa-clone",
-                            "clone.*:fa-solid fa-clone",
-                            "categorise.*:fa-regular fa-folder-open",
+                        "cut.*:fa-solid fa-scissors",
+                        "move.*:fa-solid fa-angles-right",
+                        "copy.*:fa-regular fa-copy",
+                        "duplicate.*:fa-solid fa-clone",
+                        "clone.*:fa-solid fa-clone",
+                        "categorise.*:fa-regular fa-folder-open",
 
-                            "download.*:fa-solid fa-download",
-                            "upload.*:fa-solid fa-upload",
+                        "download.*:fa-solid fa-download",
+                        "upload.*:fa-solid fa-upload",
 
-                            "execute.*:fa-solid fa-bolt",
-                            "run.*:fa-solid fa-bolt",
-                            "trigger.*:fa-solid fa-bolt",
+                        "execute.*:fa-solid fa-bolt",
+                        "run.*:fa-solid fa-bolt",
+                        "trigger.*:fa-solid fa-bolt",
 
-                            "link.*:fa-solid fa-link",
-                            "unlink.*:fa-solid fa-link-slash",
+                        "link.*:fa-solid fa-link",
+                        "unlink.*:fa-solid fa-link-slash",
 
-                            "start.*:fa-solid fa-play",
-                            "play.*:fa-solid fa-play",
-                            "resume.*:fa-solid fa-play",
-                            "pause.*:fa-solid fa-pause",
-                            "suspend.*:fa-solid fa-pause",
-                            "stop.*:fa-solid fa-stop",
-                            "terminate.*:fa-solid fa-stop",
+                        "start.*:fa-solid fa-play",
+                        "play.*:fa-solid fa-play",
+                        "resume.*:fa-solid fa-play",
+                        "pause.*:fa-solid fa-pause",
+                        "suspend.*:fa-solid fa-pause",
+                        "stop.*:fa-solid fa-stop",
+                        "terminate.*:fa-solid fa-stop",
 
-                            "previous.*:fa-backward-step",
-                            "next.*:fa-forward-step",
+                        "previous.*:fa-backward-step",
+                        "next.*:fa-forward-step",
 
-                            "approve.*:fa-regular fa-thumbs-up",
-                            "reject.*:fa-regular fa-thumbs-down",
+                        "approve.*:fa-regular fa-thumbs-up",
+                        "reject.*:fa-regular fa-thumbs-down",
 
-                            "verify.*:fa-solid fa-check",
-                            "decline.*:fa-solid fa-xmark",
-                            "cancel.*:fa-solid fa-xmark",
+                        "verify.*:fa-solid fa-check",
+                        "decline.*:fa-solid fa-xmark",
+                        "cancel.*:fa-solid fa-xmark",
 
-                            "discard.*:fa-regular fa-trash-can",
+                        "discard.*:fa-regular fa-trash-can",
 
-                            "assign.*:fa-regular fa-hand-point-right",
+                        "assign.*:fa-regular fa-hand-point-right",
 
-                            "calculate.*:fa-calculator",
+                        "calculate.*:fa-calculator",
 
-                            "import.*:fa-solid fa-file-import",
-                            "export.*:fa-solid fa-file-export",
+                        "import.*:fa-solid fa-file-import",
+                        "export.*:fa-solid fa-file-export",
 
-                            "first.*:fa-regular fa-star",
+                        "first.*:fa-regular fa-star",
 
-                            "install.*:fa-solid fa-wrench",
+                        "install.*:fa-solid fa-wrench",
 
-                            "setup.*:fa-solid fa-gear",
-                            "configure.*:fa-solid fa-gear",
+                        "setup.*:fa-solid fa-gear",
+                        "configure.*:fa-solid fa-gear",
 
-                            "refresh.*:fa-sync",
-                            "renew.*:fa-rotate-right",
-                            "reset.*:fa-rotate-left",
+                        "refresh.*:fa-sync",
+                        "renew.*:fa-rotate-right",
+                        "reset.*:fa-rotate-left",
 
-                            "save.*:fa-regular fa-floppy-disk",
+                        "save.*:fa-regular fa-floppy-disk",
 
-                            "switch.*:fa-exchange",
-                            "random.*:fa-shuffle",
+                        "switch.*:fa-exchange",
+                        "random.*:fa-shuffle",
 
-                            "view.*:fa-regular fa-eye",
+                        "view.*:fa-regular fa-eye",
 
-                            "wizard.*:fa-solid fa-wand-magic-sparkles"
+                        "wizard.*:fa-solid fa-wand-magic-sparkles"
 
-                    };
+                    })
+                    String[] patterns) {
 
-                    @Getter(lazy = true)
-                    private final Map<Pattern, String> patternsAsMap = asMap(getPatterns());
+                    //(lazy = true) //TODO
+                    public Map<Pattern, String> patternsAsMap() { return asMap(patterns()); }
 
                 }
             }
 
-            private final Property property = new Property();
-            @Data
-            public static class Property {
-
+            public record Property(
                 /**
                  * The default for whether property edits should be reified
                  * as a {@link org.apache.causeway.applib.services.command.Command},
@@ -1153,51 +1172,44 @@ public static class Property {
                  * either for auditing or for replayed against a secondary
                  * system, eg for regression testing.
                  *
-                 * <p>
-                 *  This setting can be overridden on a case-by-case basis using
-                 *  {@link org.apache.causeway.applib.annotation.Property#commandPublishing()}.
-                 * </p>
+                 * <p>This setting can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.Property#commandPublishing()}.
                  */
-                private PropertyConfigOptions.PublishingPolicy commandPublishing = PropertyConfigOptions.PublishingPolicy.NONE;
+                @DefaultValue("NONE")
+                PropertyConfigOptions.PublishingPolicy commandPublishing,
 
                 /**
                  * The default for whether property edits should be sent through to the
                  * {@link ExecutionSubscriber} for publishing.
                  *
-                 * <p>
-                 * The service's {@link ExecutionSubscriber#onExecution(Execution)}  publish}
+                 * <p>The service's {@link ExecutionSubscriber#onExecution(Execution)}  publish}
                  * method is called only once per transaction, with
                  * {@link Execution} collecting details of
                  * the identity of the target object, the property edited, and the new value of the property.
-                 * </p>
                  *
-                 * <p>
-                 * This setting can be overridden on a case-by-case basis using
+                 * <p>This setting can be overridden on a case-by-case basis using
                  * {@link org.apache.causeway.applib.annotation.Property#executionPublishing()}.
-                 * </p>
                  */
-                private PropertyConfigOptions.PublishingPolicy executionPublishing = PropertyConfigOptions.PublishingPolicy.NONE;
+                @DefaultValue("NONE")
+                PropertyConfigOptions.PublishingPolicy executionPublishing,
 
-                private final DomainEvent domainEvent = new DomainEvent();
-                @Data
-                public static class DomainEvent {
+                @DefaultValue
+                DomainEvent domainEvent) {
+
+                public record DomainEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.domain.PropertyDomainEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
                      * whenever an property is being interacted with.
                      *
-                     * <p>
-                     *     Up to five different events can be fired during an interaction, with the event's
-                     *     {@link org.apache.causeway.applib.events.domain.PropertyDomainEvent#getEventPhase() phase}
-                     *     determining which (hide, disable, validate, executing and executed).  Subscribers can
-                     *     influence the behaviour at each of these phases.
-                     * </p>
+                     * <p>Up to five different events can be fired during an interaction, with the event's
+                     * {@link org.apache.causeway.applib.events.domain.PropertyDomainEvent#getEventPhase() phase}
+                     * determining which (hide, disable, validate, executing and executed).  Subscribers can
+                     * influence the behaviour at each of these phases.
                      *
-                     * <p>
-                     *     The algorithm for determining whether (and what type of) an event is actually sent depends
-                     *     on the value of the {@link org.apache.causeway.applib.annotation.Property#domainEvent()} for the
-                     *     property in question:
-                     * </p>
+                     * <p>The algorithm for determining whether (and what type of) an event is actually sent depends
+                     * on the value of the {@link org.apache.causeway.applib.annotation.Property#domainEvent()} for the
+                     * property in question:
                      *
                      * <ul>
                      *     <li>
@@ -1215,64 +1227,60 @@ public static class DomainEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
-
             }
 
-            private final PropertyLayout propertyLayout = new PropertyLayout();
-            @Data
-            public static class PropertyLayout implements Applib.Annotation.ConfigPropsForPropertyOrParameterLayout {
+            public record PropertyLayout(
                 /**
                  * Defines the default position for the label for a domain object property.
                  *
-                 * <p>
-                 *     Can be overridden on a case-by-case basis using
-                 *     {@link org.apache.causeway.applib.annotation.ParameterLayout#labelPosition()}.
-                 * </p>
+                 * <p>Can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.ParameterLayout#labelPosition()}.
                  *
-                 * <p>
-                 *     If left as {@link LabelPosition#NOT_SPECIFIED} and not overridden, then the position depends
-                 *     upon the viewer implementation.
-                 * </p>
+                 * <p>If left as {@link LabelPosition#NOT_SPECIFIED} and not overridden, then the position depends
+                 * upon the viewer implementation.
                  */
-                private LabelPosition labelPosition = LabelPosition.NOT_SPECIFIED;
+                @DefaultValue("NOT_SPECIFIED")
+                LabelPosition labelPosition,
 
                 /**
                  * How {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()}
                  * should be handled when calculating the slot-in order for unreferenced <i>Properties</i>.
                  * {@code AS_PER_SEQUENCE} will use Dewey order based on available 'sequence' attributes,
                  * whereas {@code ALPHABETICALLY} will use alphabetical order based on member names.
-                 * <p>
-                 * default: {@code AS_PER_SEQUENCE}
+                 *
+                 * <p>default: {@code AS_PER_SEQUENCE}
                  */
-                private AssociationLayoutConfigOptions.SequencePolicy sequencePolicyIfUnreferenced = SequencePolicy.AS_PER_SEQUENCE;
+                @DefaultValue("AS_PER_SEQUENCE")
+                AssociationLayoutConfigOptions.SequencePolicy sequencePolicyIfUnreferenced
+                ) implements Applib.Annotation.ConfigPropsForPropertyOrParameterLayout {
+
+                @Override @Deprecated
+                public LabelPosition getLabelPosition() {
+                    return labelPosition();
+                }
             }
 
-            private final Collection collection = new Collection();
-            @Data
-            public static class Collection {
+            public record Collection(
+                @DefaultValue
+                DomainEvent domainEvent) {
 
-                private final DomainEvent domainEvent = new DomainEvent();
-                @Data
-                public static class DomainEvent {
+                public record DomainEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.domain.CollectionDomainEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
                      * whenever a collection is being interacted with.
                      *
-                     * <p>
-                     *     Up to two different events can be fired during an interaction, with the event's
-                     *     {@link org.apache.causeway.applib.events.domain.CollectionDomainEvent#getEventPhase() phase}
-                     *     determining which (hide, disable)Subscribers can influence the behaviour at each of these
-                     *     phases.
-                     * </p>
+                     * <p>Up to two different events can be fired during an interaction, with the event's
+                     * {@link org.apache.causeway.applib.events.domain.CollectionDomainEvent#getEventPhase() phase}
+                     * determining which (hide, disable)Subscribers can influence the behaviour at each of these
+                     * phases.
                      *
-                     * <p>
-                     *     The algorithm for determining whether (and what type of) an event is actually sent depends
-                     *     on the value of the {@link org.apache.causeway.applib.annotation.Collection#domainEvent()} for the
-                     *     collection action in question:
-                     * </p>
+                     * <p>The algorithm for determining whether (and what type of) an event is actually sent depends
+                     * on the value of the {@link org.apache.causeway.applib.annotation.Collection#domainEvent()} for the
+                     * collection action in question:
                      *
                      * <ul>
                      *     <li>
@@ -1290,86 +1298,92 @@ public static class DomainEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault = true;
+                    @DefaultValue("true")
+                    boolean postForDefault
+                    ) {
                 }
             }
 
-            private final CollectionLayout collectionLayout = new CollectionLayout();
-            @Data
-            public static class CollectionLayout {
-
+            public record CollectionLayout(
                 /**
                  * Defines the initial view to display collections when rendered.
                  *
-                 * <p>
-                 *     The value of this can be overridden on a case-by-case basis using
-                 *     {@link org.apache.causeway.applib.annotation.CollectionLayout#defaultView()}.
-                 *     Note that this default configuration property is an enum and so defines only a fixed number of
-                 *     values, whereas the annotation returns a string; this is to allow for flexibility that
-                 *     individual viewers might support their own additional types.  For example, the Wicket viewer
-                 *     supports <codefullcalendar</code> which can render objects that have a date on top of a calendar
-                 *     view.
-                 * </p>
+                 * <p>The value of this can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.CollectionLayout#defaultView()}.
+                 * Note that this default configuration property is an enum and so defines only a fixed number of
+                 * values, whereas the annotation returns a string; this is to allow for flexibility that
+                 * individual viewers might support their own additional types.  For example, the Wicket viewer
+                 * supports <codefullcalendar</code> which can render objects that have a date on top of a calendar
+                 * view.
                  */
-                private CollectionLayoutConfigOptions.DefaultView defaultView = CollectionLayoutConfigOptions.DefaultView.TABLE;
+                @DefaultValue("TABLE")
+                CollectionLayoutConfigOptions.DefaultView defaultView,
 
                 /**
-                 * Defines the default number of objects that are shown in a &quot;parented&quot; collection of a
+                 * Defines the default number of objects that are shown in a 'parented' collection of a
                  * domain object,
                  * result of invoking an action.
                  *
-                 * <p>
-                 *     This can be overridden on a case-by-case basis using
-                 *     {@link org.apache.causeway.applib.annotation.CollectionLayout#paged()}.
-                 * </p>
+                 * <p>This can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.CollectionLayout#paged()}.
                  */
-                private int paged = 12;
+                @DefaultValue("12")
+                int paged,
 
                 /**
                  * How {@link org.apache.causeway.applib.annotation.CollectionLayout#sequence()}
                  * should be handled when calculating the slot-in order for unreferenced <i>Collections</i>.
                  * {@code AS_PER_SEQUENCE} will use Dewey order based on available 'sequence' attributes,
                  * whereas {@code ALPHABETICALLY} will use alphabetical order based on member names.
-                 * <p>
-                 * default: {@code AS_PER_SEQUENCE}
+                 *
+                 * <p>default: {@code AS_PER_SEQUENCE}
                  */
-                private AssociationLayoutConfigOptions.SequencePolicy sequencePolicyIfUnreferenced =
-                        SequencePolicy.AS_PER_SEQUENCE;
+                @DefaultValue("AS_PER_SEQUENCE")
+                AssociationLayoutConfigOptions.SequencePolicy sequencePolicyIfUnreferenced,
 
                 /**
                  * Defines whether the table representation of a collection should be decorated using a client-side
                  * Javascript library, eg for client-side paging and filtering.
                  */
-                private Class<? extends TableDecorator> tableDecorator = TableDecorator.Default.class;
+                @DefaultValue("org.apache.causeway.applib.annotation.TableDecorator.Default")
+                Class<? extends TableDecorator> tableDecorator
+                ) {
 
             }
 
-            private final ViewModel viewModel = new ViewModel();
-            @Data
-            public static class ViewModel {
-                private final Validation validation = new Validation();
-                @Data
-                public static class Validation {
-                    private final SemanticChecking semanticChecking = new SemanticChecking();
-                    @Data
-                    public static class SemanticChecking {
+            public record ViewModel(
+                @DefaultValue
+                Validation validation) {
+
+                public record Validation(
+                    @DefaultValue
+                    SemanticChecking semanticChecking) {
+
+                    public record SemanticChecking(
                         /**
                          * Whether to check for inconsistencies between the usage of
                          * {@link org.apache.causeway.applib.annotation.DomainObject} and
                          * {@link org.apache.causeway.applib.annotation.DomainObjectLayout}.
                          */
-                        private boolean enable = false;
+                        @DefaultValue("false")
+                        boolean enable) {
+
                     }
                 }
             }
 
-            private final ViewModelLayout viewModelLayout = new ViewModelLayout();
-            @Data
-            public static class ViewModelLayout {
-
-                private final CssClassUiEvent cssClassUiEvent = new CssClassUiEvent();
-                @Data
-                public static class CssClassUiEvent {
+            public record ViewModelLayout(
+                @DefaultValue
+                CssClassUiEvent cssClassUiEvent,
+                @DefaultValue
+                IconUiEvent iconUiEvent,
+                @DefaultValue
+                LayoutUiEvent layoutUiEvent,
+                @DefaultValue
+                TitleUiEvent titleUiEvent
+                ) {
+
+                public record CssClassUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.CssClassUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -1384,7 +1398,7 @@ public static class CssClassUiEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#cssClassUiEvent() @DomainObjectLayout(cssClassEvent=...)}
                      *     for the domain object in question:
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -1402,12 +1416,11 @@ public static class CssClassUiEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault =true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
                 }
 
-                private final IconUiEvent iconUiEvent = new IconUiEvent();
-                @Data
-                public static class IconUiEvent {
+                public record IconUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.IconUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -1422,7 +1435,7 @@ public static class IconUiEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#iconUiEvent() @ViewModelLayout(iconEvent=...)}
                      *     for the domain object in question:
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -1440,12 +1453,12 @@ public static class IconUiEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault =true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
+
                 }
 
-                private final LayoutUiEvent layoutUiEvent = new LayoutUiEvent();
-                @Data
-                public static class LayoutUiEvent {
+                public record LayoutUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.LayoutUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -1458,13 +1471,13 @@ public static class LayoutUiEvent {
                      * <p>
                      *     If a different layout value has been set, then a layout in the form <code>Xxx.layout-zzz.xml</code>
                      *     use used (where <code>zzz</code> is the name of the layout).
-                     * </p>
+                     *
                      *
                      * <p>
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#layoutUiEvent() @DomainObjectLayout(layoutEvent=...)}
                      *     for the domain object in question:
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -1482,12 +1495,12 @@ public static class LayoutUiEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault =true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
+
                 }
 
-                private final TitleUiEvent titleUiEvent = new TitleUiEvent();
-                @Data
-                public static class TitleUiEvent {
+                public record TitleUiEvent(
                     /**
                      * Influences whether an {@link org.apache.causeway.applib.events.ui.TitleUiEvent} should
                      * be published (on the internal {@link org.apache.causeway.applib.services.eventbus.EventBusService})
@@ -1502,7 +1515,7 @@ public static class TitleUiEvent {
                      *     The algorithm for determining whether (and what type of) an event is sent depends on the value of the
                      *     {@link org.apache.causeway.applib.annotation.DomainObjectLayout#titleUiEvent() @DomainObjectLayout(titleEvent=...)} for the
                      *     domain object in question:
-                     * </p>
+                     *
                      *
                      * <ul>
                      *     <li>
@@ -1520,56 +1533,55 @@ public static class TitleUiEvent {
                      *     </li>
                      * </ul>
                      */
-                    private boolean postForDefault =true;
+                    @DefaultValue("true")
+                    boolean postForDefault) {
+
                 }
             }
 
-            private final Parameter parameter = new Parameter();
-            @Data
-            public static class Parameter {
-
+            public record Parameter(
                 /**
                  * Whether parameters should be reset to their default if an earlier parameter changes its
                  * value, or whether instead a parameter value, once changed by the end-user, should never be
                  * overwritten even if the end-user changes an earlier parameter value.
                  *
-                 * <p>
-                 *     This setting can be overridden on a case-by-case basis using
-                 *     {@link org.apache.causeway.applib.annotation.Parameter#precedingParamsPolicy() Parameter#precedingParametersPolicy()}.
-                 * </p>
+                 * <p>This setting can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.Parameter#precedingParamsPolicy() Parameter#precedingParametersPolicy()}.
                  */
-                private ParameterConfigOptions.PrecedingParametersPolicy precedingParametersPolicy = ParameterConfigOptions.PrecedingParametersPolicy.RESET;
-
+                @DefaultValue("RESET")
+                ParameterConfigOptions.PrecedingParametersPolicy precedingParametersPolicy) {
             }
 
-            private final ParameterLayout parameterLayout = new ParameterLayout();
-            @Data
-            public static class ParameterLayout implements Applib.Annotation.ConfigPropsForPropertyOrParameterLayout {
+            public record ParameterLayout(
                 /**
                  * Defines the default position for the label for an action parameter.
                  *
-                 * <p>
-                 *     Can be overridden on a case-by-case basis using
-                 *     {@link org.apache.causeway.applib.annotation.ParameterLayout#labelPosition()}.
-                 * </p>
+                 * <p>Can be overridden on a case-by-case basis using
+                 * {@link org.apache.causeway.applib.annotation.ParameterLayout#labelPosition()}.
                  *
-                 * <p>
-                 *     If left as {@link LabelPosition#NOT_SPECIFIED} and not overridden, then the position depends
-                 *     upon the viewer implementation.
-                 * </p>
+                 * <p>If left as {@link LabelPosition#NOT_SPECIFIED} and not overridden, then the position depends
+                 * upon the viewer implementation.
                  */
-                private LabelPosition labelPosition = LabelPosition.NOT_SPECIFIED;
+                @DefaultValue("NOT_SPECIFIED")
+                LabelPosition labelPosition)
+            implements Applib.Annotation.ConfigPropsForPropertyOrParameterLayout {
+                @Override @Deprecated
+                public LabelPosition getLabelPosition() {
+                    return labelPosition();
+                }
             }
 
         }
 
-        private final Service service = new Service();
-        @Data
-        public static class Service {
+        public record Service(@DefaultValue
+            MetricsService metricsService) {
 
-            private final MetricsService metricsService = new MetricsService();
-            @Data
-            public static class MetricsService {
+            public record MetricsService(
+                /**
+                 * What level of detail the MetricsService should capture.
+                 */
+                @DefaultValue("COUNTERS_ONLY")
+                Level level) {
                 public enum Level {
                     COUNTERS_ONLY,
                     COUNTERS_AND_DETAIL;
@@ -1577,373 +1589,342 @@ public enum Level {
                     public boolean isCountersOnly() { return this == COUNTERS_ONLY; }
                     public boolean isCountersAndDetail() { return this == COUNTERS_AND_DETAIL; }
                 }
-
-                /**
-                 * What level of detail the MetricsService should capture.
-                 */
-                private Level level = Level.COUNTERS_ONLY;
             }
         }
     }
 
-    private final Core core = new Core();
-    @Data
-    public static class Core {
-
-        private final Config config = new Config();
-        @Data
-        public static class Config {
-
-            public static enum ConfigurationPropertyVisibilityPolicy {
-                NEVER_SHOW,
-                SHOW_ONLY_IN_PROTOTYPE,
-                ALWAYS_SHOW
-            }
-
+    public record Core(
+        @DefaultValue
+        Config config,
+        @DefaultValue
+        MetaModel metaModel,
+        @DefaultValue
+        Runtime runtime,
+        @DefaultValue
+        RuntimeServices runtimeServices) {
+
+        public record Config(
             /**
              * Configuration values might contain sensitive data, hence per default,
              * configuration properties are only visible with the configuration-page
              * when <i>prototyping</i>.
              *
-             * <p>
-             * Alternatively this policy can be set to either <b>always</b> show or <b>never</b> show.
-             * </p>
+             * <p>Alternatively this policy can be set to either <b>always</b> show or <b>never</b> show.
              *
              * @see ConfigurationPropertyVisibilityPolicy
              */
-            private ConfigurationPropertyVisibilityPolicy configurationPropertyVisibilityPolicy
-                    = ConfigurationPropertyVisibilityPolicy.SHOW_ONLY_IN_PROTOTYPE;
+            @DefaultValue("SHOW_ONLY_IN_PROTOTYPE")
+            ConfigurationPropertyVisibilityPolicy configurationPropertyVisibilityPolicy) {
 
-        }
+            public static enum ConfigurationPropertyVisibilityPolicy {
+                NEVER_SHOW,
+                SHOW_ONLY_IN_PROTOTYPE,
+                ALWAYS_SHOW
+            }
 
-        private final MetaModel metaModel = new MetaModel();
-        @Data
-        public static class MetaModel {
+            // TODO remove
+            @Deprecated
+            public static Config defaults() {
+                throw _Exceptions.unsupportedOperation();
+            }
+        }
 
+        public record MetaModel(
             /**
              * Whether domain objects to which the current user does not have visibility access should be rendered
              * within collections or drop-down choices/autocompletes.
              *
-             * <p>
-             *     One reason this filtering may be necessary is for multi-tenanted applications, whereby an end-user
-             *     should only be able to "see" what data that they own.  For efficiency, the application should
-             *     only query for objects that the end-user owns.  This configuration property acts as a safety net to
-             *     prevent the end-user from viewing domain objects <i>even if</i> those domain objects were rehydrated
-             *     from the persistence store.
-             * </p>
+             * <p>One reason this filtering may be necessary is for multi-tenanted applications, whereby an end-user
+             * should only be able to "see" what data that they own.  For efficiency, the application should
+             * only query for objects that the end-user owns.  This configuration property acts as a safety net to
+             * prevent the end-user from viewing domain objects <i>even if</i> those domain objects were rehydrated
+             * from the persistence store.
              */
-            private boolean filterVisibility = true;
-
-            private final ProgrammingModel programmingModel = new ProgrammingModel();
-            @Data
-            public static class ProgrammingModel {
-
+            @DefaultValue("true")
+            boolean filterVisibility,
+            @DefaultValue
+            ProgrammingModel programmingModel,
+            @DefaultValue
+            Introspector introspector,
+            @DefaultValue
+            Validator validator) {
+
+            public record ProgrammingModel(
                 /**
                  * If set, then any aspects of the programming model (as implemented by <code>FacetFactory</code>s that
                  * have been indicated as deprecated will simply be ignored/excluded from the metamodel.
                  */
-                private boolean ignoreDeprecated = false;
+                @DefaultValue("false")
+                boolean ignoreDeprecated) {
             }
 
-            private final Introspector introspector = new Introspector();
-            @Data
-            public static class Introspector {
-
+            public record Introspector(
                 /**
                  * Policy as to how introspection should process
                  * class members and supporting methods.
-                 * <p>
-                 * Default is to only introspect public class members, while annotating these is optional.
+                 *
+                 * <p>Default is to only introspect public class members, while annotating these is optional.
                  */
-                private IntrospectionPolicy policy;
-                public IntrospectionPolicy getPolicy() {
-                    return Optional.ofNullable(policy)
-                            .orElse(IntrospectionPolicy.ANNOTATION_OPTIONAL);
-                }
+                @DefaultValue("ANNOTATION_OPTIONAL")
+                IntrospectionPolicy policy,
+
 
                 /**
                  * Whether to perform metamodel introspection in parallel, intended to speed up bootstrapping.
                  *
-                 * <p>
-                 *     For now this is <i>experimental</i>.
-                 *     We recommend this is left as disabled (the default).
-                 * </p>
+                 * <p>For now this is <i>experimental</i>.
+                 * We recommend this is left as disabled (the default).
                  */
-                private boolean parallelize = false; //TODO[CAUSEWAY-2382] concurrent spec-loading is experimental
+                @DefaultValue("false") //TODO[CAUSEWAY-2382] concurrent spec-loading is experimental
+                boolean parallelize,
 
                 /**
                  * Whether all known types should be fully introspected as part of the bootstrapping, or should only be
                  * partially introspected initially.
                  *
-                 * <p>
-                 * Leaving this as lazy means that there's a chance that metamodel validation errors will not be
+                 * <p>Leaving this as lazy means that there's a chance that metamodel validation errors will not be
                  * discovered during bootstrap.  That said, metamodel validation is still run incrementally for any
                  * classes introspected lazily after initial bootstrapping (unless {@link #isValidateIncrementally()} is
                  * disabled.
-                 * </p>
+                 *
                  */
-                private IntrospectionMode mode = IntrospectionMode.LAZY_UNLESS_PRODUCTION;
+                @DefaultValue("LAZY_UNLESS_PRODUCTION")
+                IntrospectionMode mode,
 
                 /**
                  * If true, then no new specifications will be allowed to be loaded once introspection has been complete.
                  *
-                 * <p>
-                 * Only applies if the introspector is configured to perform full introspection up-front (either because of
+                 * <p>Only applies if the introspector is configured to perform full introspection up-front (either because of
                  * {@link IntrospectionMode#FULL} or {@link IntrospectionMode#LAZY_UNLESS_PRODUCTION} when in production);
                  * otherwise is ignored.
-                 * </p>
                  */
-                private boolean lockAfterFullIntrospection = true;
+                @DefaultValue("true")
+                boolean lockAfterFullIntrospection,
 
                 /**
                  * If true, then metamodel validation is performed after any new specification has been loaded (after the
                  * initial bootstrapping).
                  *
-                 * <p>
-                 * This does <i>not</i> apply if the introspector is configured to perform full introspection up-front
-                 * AND when the metamodel is {@link Core.MetaModel.Introspector#isLockAfterFullIntrospection() locked} after initial bootstrapping
+                 * <p>This does <i>not</i> apply if the introspector is configured to perform full introspection up-front
+                 * AND when the metamodel is {@link Core.MetaModel.Introspector#lockAfterFullIntrospection() locked} after initial bootstrapping
                  * (because in that case the lock check will simply prevent any new specs from being loaded).
                  * But it will apply otherwise.
-                 * </p>
                  *
-                 * <p>In particular, this setting <i>can</i> still apply even if the {@link Core.MetaModel.Introspector#getMode() introspection mode}
+                 * <p>In particular, this setting <i>can</i> still apply even if the {@link Core.MetaModel.Introspector#mode() introspection mode}
                  * is set to {@link IntrospectionMode#FULL full}, because that in itself does not preclude some code
                  * from attempting to load some previously unknown type.  For example, a fixture script could attempt to
                  * invoke an action on some new type using the
                  * {@link org.apache.causeway.applib.services.wrapper.WrapperFactory} - this will cause introspection of that
                  * new type to be performed.
-                 * </p>
                  */
-                private boolean validateIncrementally = true;
-
+                @DefaultValue("true")
+                boolean validateIncrementally) {
             }
 
-            private final Validator validator = new Validator();
-            @Data
-            public static class Validator {
-
+            public record Validator(
                 /**
                  * Whether to perform metamodel validation in parallel.
                  */
-                private boolean parallelize = true;
+                @DefaultValue("true")
+                boolean parallelize,
 
                 /**
                  * This setting is used to determine whether the use of such deprecated features is
                  * allowed.
                  *
-                 * <p>
-                 *     If not allowed, then metamodel validation errors will be flagged.
-                 * </p>
+                 * <p>If not allowed, then metamodel validation errors will be flagged.
                  *
-                 * <p>
-                 *     Note that this settings has no effect if the programming model has been configured to
-                 *     {@link ProgrammingModel#isIgnoreDeprecated() ignore deprecated} features (because in this case
-                 *     the programming model facets simply won't be included in the introspection process.
-                 * </p>
+                 * <p>Note that this settings has no effect if the programming model has been configured to
+                 * {@link ProgrammingModel#isIgnoreDeprecated() ignore deprecated} features (because in this case
+                 * the programming model facets simply won't be included in the introspection process.
                  */
-                private boolean allowDeprecated = true;
+                @DefaultValue("true")
+                boolean allowDeprecated,
 
                 /**
                  * Whether to validate that any actions that accept action parameters have either a corresponding
                  * choices or auto-complete for that action parameter, or are associated with a collection of the
                  * appropriate type.
                  */
-                private boolean actionCollectionParameterChoices = true;
+                @DefaultValue("true")
+                boolean actionCollectionParameterChoices,
 
                 /**
                  * Whether to ensure that the logical-type-name of all objects must be specified explicitly, using either
                  * {@link Named}.
                  *
-                 * <p>
-                 *     It is <i>highly advisable</i> to leave this set as enabled (the default). These logical-type-names
-                 *     should also (of course) be unique - among non-abstract types.
-                 * </p>
+                 * <p>It is <i>highly advisable</i> to leave this set as enabled (the default). These logical-type-names
+                 * should also (of course) be unique - among non-abstract types.
                  */
-                private boolean explicitLogicalTypeNames = false;
+                @DefaultValue("false")
+                boolean explicitLogicalTypeNames,
 
                 /**
                  * Allows logical type name in {@link Named} also be included in the list of {@link DomainObject#aliased()}
                  * or {@link DomainService#aliased()}.
                  *
-                 * <p>
-                 *     It is <i>highly advisable</i> to leave this disabled. This option is meant as a practical way to
-                 *     enable to transition from old names to new logical type names. Especially when you have a large
-                 *     number of files that have to migrated and you want to do the migration in incremental steps.
-                 * </p>
+                 * <p>It is <i>highly advisable</i> to leave this disabled. This option is meant as a practical way to
+                 * enable to transition from old names to new logical type names. Especially when you have a large
+                 * number of files that have to migrated and you want to do the migration in incremental steps.
                  */
-                private boolean allowLogicalTypeNameAsAlias = false;
+                @DefaultValue("false")
+                boolean allowLogicalTypeNameAsAlias,
 
-                private final JaxbViewModel jaxbViewModel = new JaxbViewModel();
-                @Data
-                public static class JaxbViewModel {
+                @DefaultValue
+                JaxbViewModel jaxbViewModel
+                ) {
+
+                public record JaxbViewModel(
                     /**
                      * If set, then ensures that all JAXB-style view models are concrete classes, not abstract.
                      */
-                    private boolean notAbstract = true;
+                    @DefaultValue("true")
+                    boolean notAbstract,
                     /**
                      * If set, then ensures that all JAXB-style view models are either top-level classes or nested
                      * static classes (in other words, checks that they are not anonymous, local nor nested
                      * non-static classes).
                      */
-                    private boolean notInnerClass = true;
+                    @DefaultValue("true")
+                    boolean notInnerClass,
                     /**
                      * If set, then ensures that all JAXB-style view models have a no-arg constructor.
                      */
-                    private boolean noArgConstructor = false;
+                    @DefaultValue("false")
+                    boolean noArgConstructor,
                     /**
                      * If set, then ensures that for all properties of JAXB-style view models where the property's type
                      * is an entity, then that entity's type has been correctly annotated with
                      * @{@link jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter} (so that the property's value can
                      * be converted into a serializable form).
                      */
-                    private boolean referenceTypeAdapter = true;
+                    @DefaultValue("true")
+                    boolean referenceTypeAdapter,
                     /**
                      * If set, then ensures that for all properties of JAXB-style view models where the property's type
                      * is a date or time, then that property has been correctly annotated with
                      * @{@link jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter} (so that the property's value can
                      * be converted into a serializable form).
                      */
-                    private boolean dateTimeTypeAdapter = true;
+                    @DefaultValue("true")
+                    boolean dateTimeTypeAdapter) {
                 }
 
-                private final Jdoql jdoql = new Jdoql();
-                @Data
-                public static class Jdoql {
-                    /**
-                     * If set, then ensures that the 'FROM' clause within any JDOQL <code>@Query</code>s annotations
-                     * relates to a known entity type, and moreover that that type is compatible with the type on
-                     * which the annotation appears: meaning its either a supertype of or the same type as the
-                     * annotated type.
-                     */
-                    private boolean fromClause = true;
-                    /**
-                     * If set, then ensures that the 'VARIABLES' clause within any JDOQL <code>@Query</code>s relates
-                     * to a known entity type.
-                     */
-                    private boolean variablesClause = true;
-                }
             }
         }
 
-        private final Runtime runtime = new Runtime();
-        @Data
-        public static class Runtime {
-
+        public record Runtime(
             /**
              * If set, then overrides the application's {@link Locale#getDefault()}
              */
-            private Optional<String> locale = Optional.empty();
+            @DefaultValue
+            Optional<String> locale,
 
             /**
              * If set, then override's the application's timezone.
              */
-            private String timezone;
-
+            String timezone) {
         }
 
-        private final RuntimeServices runtimeServices = new RuntimeServices();
-        @Data
-        public static class RuntimeServices {
+        public record RuntimeServices(
+            @DefaultValue
+            Email email,
+            @DefaultValue
+            ApplicationFeatures applicationFeatures,
+            @DefaultValue
+            ExceptionRecognizer exceptionRecognizer,
+            @DefaultValue
+            Translation translation,
+            @DefaultValue
+            EntityPropertyChangePublisher entityPropertyChangePublisher) {
 
-            private final Email email = new Email();
             /**
              * Mail specific configuration in addition to Spring's <code>spring.mail.*</code>.
              * See also spring.mail.port, spring.mail.properties.mail.smtp.connectiontimeout,
              * spring.mail.properties.mail.smtp.timeout, spring.mail.host, spring.mail.username,
              * spring.mail.password, spring.mail.javamail.properties.mail.smtp.starttls.enable
              */
-            @Data
-            public static class Email {
-
+            public record Email(
                 /**
                  * If an email fails to send, whether to propagate the exception (meaning that potentially the end-user
                  * might see the exception), or whether instead to just indicate failure through the return value of
                  * the method ({@link org.apache.causeway.applib.services.email.EmailService#send(List, List, List, String, String, DataSource...)}
                  * that's being called.
                  */
-                private boolean throwExceptionOnFail = true;
-
-                private final Override override = new Override();
-                @Data
-                public static class Override {
+                @DefaultValue("true")
+                boolean throwExceptionOnFail,
+                @DefaultValue
+                Override override,
+                @DefaultValue
+                Sender sender) {
+
+                public record Override(
                     /**
                      * Intended for testing purposes only, if set then the requested <code>to:</code> of the email will
                      * be ignored, and instead sent to this email address instead.
                      */
                     @jakarta.validation.constraints.Email
-                    private String to;
+                    String to,
                     /**
                      * Intended for testing purposes only, if set then the requested <code>cc:</code> of the email will
                      * be ignored, and instead sent to this email address instead.
                      */
                     @jakarta.validation.constraints.Email
-                    private String cc;
+                    String cc,
                     /**
                      * Intended for testing purposes only, if set then the requested <code>bcc:</code> of the email will
                      * be ignored, and instead sent to this email address instead.
                      */
                     @jakarta.validation.constraints.Email
-                    private String bcc;
+                    String bcc) {
                 }
 
-                private final Sender sender = new Sender();
-                @Data
-                public static class Sender {
-
+                public record Sender(
                     /**
                      * Specifies the email address of the user sending the email.
                      *
-                     * <p>
-                     *     If the {@link #getUsername() username} is not specified, is also used as the username to
-                     *     connect to the SMTP service.
-                     * </p>
+                     * <p>If the {@link #username()} is not specified, is also used as the user name to
+                     * connect to the SMTP service.
                      *
-                     * <p>
-                     *     This configuration property is mandatory (for the default implementation of the
-                     *     {@link org.apache.causeway.applib.services.email.EmailService}, at least).
-                     * </p>
+                     * <p>This configuration property is mandatory (for the default implementation of the
+                     * {@link org.apache.causeway.applib.services.email.EmailService}, at least).
                      */
                     @jakarta.validation.constraints.Email
-                    private String address;
+                    String address) {
                 }
 
             }
 
-            private final ApplicationFeatures applicationFeatures = new ApplicationFeatures();
-            @Data
-            public static class ApplicationFeatures {
+            public record ApplicationFeatures(
                 /**
                  * Whether the {@link org.apache.causeway.applib.services.appfeat.ApplicationFeatureRepository} (or the
                  * default implementation of that service, at least) should compute the set of
                  * <code>ApplicationFeature</code> that describe the metamodel
                  * {@link ApplicationFeaturesInitConfiguration#EAGERLY eagerly}, or lazily.
                  */
-                ApplicationFeaturesInitConfiguration init = ApplicationFeaturesInitConfiguration.NOT_SPECIFIED;
+                @DefaultValue("NOT_SPECIFIED")
+                ApplicationFeaturesInitConfiguration init) {
             }
 
-            private final ExceptionRecognizer exceptionRecognizer = new ExceptionRecognizer();
-            @Data
-            public static class ExceptionRecognizer {
+            public record ExceptionRecognizer(
+                @DefaultValue
+                Dae dae) {
 
-                private final Dae dae = new Dae();
-                @Data
-                public static class Dae {
+                public record Dae(
                     /**
                      * Whether the {@link org.apache.causeway.applib.services.exceprecog.ExceptionRecognizer}
                      * implementation for Spring's DataAccessException - which attempts to sanitize any exceptions
                      * arising from object stores - should be disabled (meaning that exceptions will potentially
                      * propagate as more serious to the end user).
                      */
-                    private boolean disable = false;
+                    @DefaultValue("false")
+                    boolean disable) {
                 }
             }
 
-            private final Translation translation = new Translation();
-            @Data
-            public static class Translation {
-
-                private final Po po = new Po();
-
+            public record Translation(
+                @DefaultValue
+                Po po,
                 /**
                  * Specifies the relative resource path to look for translation files.
                  * <p>
@@ -1951,15 +1932,14 @@ public static class Translation {
                  * <p>
                  * Replaces the former Servlet context parameter 'causeway.config.dir';
                  */
-                private String resourceLocation = null;
+                String resourceLocation
+                ) {
 
-                @Data
-                public static class Po {
+                public record Po(
                     /**
                      * Specifies the initial mode for obtaining/discovering translations.
                      *
-                     * <p>
-                     *     There are three modes:
+                     * <p>There are three modes:
                      *     <ul>
                      *         <li>
                      *              <p>
@@ -1967,63 +1947,59 @@ public static class Po {
                      *                  integration testing or prototyping, meaning that the service records any requests made of it
                      *                  but just returns the string unaltered.  This is a good way to discover new strings that
                      *                  require translation.
-                     *              </p>
+                     *
                      *         </li>
                      *         <li>
                      *              <p>
                      *                  The {@link Mode#READ read} mode is appropriate for production; the
                      *                  service looks up translations that have previously been captured.
-                     *              </p>
+                     *
                      *         </li>
                      *         <li>
                      *             <p>
                      *                 The {@link Mode#DISABLED disabled} performs no translation
                      *                 and simply returns the original string unchanged.  Unlike the write mode, it
                      *                 does <i>not</i> keep track of translation requests.
-                     *             </p>
+                     *
                      *         </li>
                      *     </ul>
-                     * </p>
+                     *
                      */
-                    Mode mode = Mode.WRITE;
+                    @DefaultValue("WRITE")
+                    Mode mode) {
                 }
             }
 
-            private final EntityPropertyChangePublisher entityPropertyChangePublisher = new EntityPropertyChangePublisher();
-
-            @Data
-            public static class EntityPropertyChangePublisher {
-
-                private final Bulk bulk = new Bulk();
-
-                @Data
-                public static class Bulk {
+            public record EntityPropertyChangePublisher(
+                @DefaultValue
+                Bulk bulk) {
 
+                public record Bulk(
                     /**
                      * Determines the threshold as to whether to execute a set of entity changes in blk, in other words without a transaction flush in between.
                      *
-                     * <p>
-                     *     If the threshold is passed (by default, anything more than 1 entity to persist), then the {@link org.apache.causeway.applib.services.repository.RepositoryService#execInBulk(Callable)} API is used.
-                     * </p>
+                     * <p>If the threshold is passed (by default, anything more than 1 entity to persist), then the {@link org.apache.causeway.applib.services.repository.RepositoryService#execInBulk(Callable)} API is used.
                      */
-                    int threshold = 1;
+                    @DefaultValue("1")
+                    int threshold) {
                 }
             }
         }
     }
 
-    private final Persistence persistence = new Persistence();
-    @Data
-    public static class Persistence {
+    public record Persistence(
+        @DefaultValue
+        Commons commons,
+        @DefaultValue
+        Schema schema) {
 
-        private final Commons commons = new Commons();
-        @Data
-        public static class Commons {
-
-            private final RepositoryService repositoryService = new RepositoryService();
-            @Data
-            public static class RepositoryService {
+        public record Commons(
+            @DefaultValue
+            RepositoryService repositoryService,
+            @DefaultValue
+            EntityChangeTracker entityChangeTracker) {
 
+            public record RepositoryService(
                 /**
                  * Normally any queries are automatically preceded by flushing pending executions.
                  *
@@ -2033,14 +2009,13 @@ public static class RepositoryService {
                  * <p>
                  *     NOTE: this key is redundant for JPA/EclipseLink, which supports its own auto-flush using
                  *     <a href="https://www.eclipse.org/eclipselink/documentation/2.7/jpa/extensions/persistenceproperties_ref.htm#BABDHEEB">eclipselink.persistence-context.flush-mode</a>
-                 * </p>
+                 *
                  */
-                private boolean disableAutoFlush = false;
+                @DefaultValue("false")
+                boolean disableAutoFlush) {
             }
 
-            private final EntityChangeTracker entityChangeTracker = new EntityChangeTracker();
-            @Data
-            public static class EntityChangeTracker {
+            public record EntityChangeTracker(
                 /**
                  * Normally any query submitted to {@link org.apache.causeway.applib.services.repository.RepositoryService#allMatches(Query)} will trigger a
                  * flush first, unless auto-flush has been {@link Core.RuntimeServices.RepositoryService#isDisableAutoFlush() disabled}.
@@ -2049,29 +2024,28 @@ public static class EntityChangeTracker {
                  *     However, this auto-flush behaviour can be troublesome if the query occurs as a side-effect of the evaluation of a derived property,
                  *     whose value in turn is enlisted by an implementation of a subscriber (in particular {@link EntityPropertyChangeSubscriber}) which
                  *     captures the value of all properties (both persisted and derived).  However, this behaviour can (at least under JDO), result in a {@link java.util.ConcurrentModificationException}.
-                 * </p>
+                 *
                  *
                  * <p>
                  *     By default, {@link EntityChangeTracker} will therefore temporarily suppress any auto-flushing while this is ongoing.  The purpose
                  *     of this configuration property is to never suppress, ie always autoflush.
-                 * </p>
+                 *
                  */
-                private boolean suppressAutoFlush = true;
+                @DefaultValue("true")
+                boolean suppressAutoFlush,
                 /**
                  * Provides a mechanism to globally enable or disable this service.
                  *
                  * <p>
                  *     By default this service is enabled (if added to the classpath as a module).
-                 * </p>
+                 *
                  */
-                private boolean enabled = true;
+                @DefaultValue("true")
+                boolean enabled) {
             }
         }
 
-        private final Schema schema = new Schema();
-        @Data
-        public static class Schema {
-
+        public record Schema(
             /**
              * List of additional schemas to be auto-created.
              * <p>
@@ -2082,7 +2056,8 @@ public static class Schema {
              * This configuration mechanism does not consider any schema-auto-creation
              * configuration (if any), that independently is provided the standard JPA way.
              */
-            private final List<String> autoCreateSchemas = new ArrayList<>();
+            @DefaultValue
+            List<String> autoCreateSchemas,
 
             /**
              * Does lookup additional "mapping-files" in META-INF/orm-<i>name</i>.xml
@@ -2090,7 +2065,8 @@ public static class Schema {
              * to those that are already configured the <i>Spring Data</i> way (if any).
              * @implNote not implemented for JDO
              */
-            private final List<String> additionalOrmFiles = new ArrayList<>();
+            @DefaultValue
+            List<String> additionalOrmFiles,
 
             /**
              * Vendor specific SQL syntax to create a DB schema.
@@ -2103,38 +2079,59 @@ public static class Schema {
              * <p>
              * For MYSQL/MARIADB use escape like {@code `%S`}
              */
-            private String createSchemaSqlTemplate = "CREATE SCHEMA IF NOT EXISTS %S";
-
+            @DefaultValue("CREATE SCHEMA IF NOT EXISTS %S")
+            String createSchemaSqlTemplate) {
         }
     }
 
-    private final Prototyping prototyping = new Prototyping();
-    @Data
-    public static class Prototyping {
+    public record Prototyping(
+        @DefaultValue
+        H2Console h2Console,
+        /**
+         * Whether and how to display any properties, actions and collections whose visibility has been vetoed.
+         *
+         * <p>By default, such object members are of course hidden.  However, this config property can be used to
+         * instead show these objects as disabled, with the tooltip indicating why the object member has been
+         * vetoed.  Setting the property to
+         * {@link IfHiddenPolicy#SHOW_AS_DISABLED_WITH_DIAGNOSTICS SHOW_AS_DISABLED_WITH_DIAGNOSTICS} shows
+         * additional detail in the tooltip.
+         *
+         * <p>This config property only applies in prototyping mode.
+         */
+        @DefaultValue("HIDE")
+        IfHiddenPolicy ifHiddenPolicy,
+        /**
+         * Whether and how to display any properties, actions and collections whose usability has been vetoed.
+         *
+         * <p>By default, such object members are shown as disabled, with the tooltip indicating why. Setting the
+         * property to {@link IfDisabledPolicy#SHOW_AS_DISABLED_WITH_DIAGNOSTICS SHOW_AS_DISABLED_WITH_DIAGNOSTICS} shows additional detail in the
+         * tooltip.
+         *
+         * <p>This config property only applies in prototyping mode.
+         */
+        @DefaultValue("DISABLE")
+        IfDisabledPolicy ifDisabledPolicy) {
 
-        private final H2Console h2Console = new H2Console();
-        @Data
-        public static class H2Console {
+        public record H2Console(
             /**
              * Whether to allow remote access to the H2 Web-Console,
              * which is a potential security risk when no web-admin password is set.
-             * <p>
-             * Corresponds to Spring Boot's "spring.h2.console.settings.web-allow-others" config property.
+             *
+             * <p>Corresponds to Spring Boot's "spring.h2.console.settings.web-allow-others" config property.
              */
-            private boolean webAllowRemoteAccess = false;
+            @DefaultValue("false")
+            boolean webAllowRemoteAccess,
 
             /**
              * Whether to generate a random password for access to the H2 Web-Console advanced features.
              *
-             * <p>
-             * If a password is generated, it is logged to the logging subsystem (Log4j2).
-             * </p>
+             * <p>If a password is generated, it is logged to the logging subsystem (Log4j2).
              *
-             * <p>
-             * Recommended (<code>true</code>) when {@link #isWebAllowRemoteAccess()} is also <code>true</code>.
-             * </p>
+             * <p>Recommended (<code>true</code>) when {@link #webAllowRemoteAccess()} is also <code>true</code>.
              */
-            private boolean generateRandomWebAdminPassword = true;
+            @DefaultValue("false")
+            boolean generateRandomWebAdminPassword) {
+
         }
 
         public enum IfHiddenPolicy {
@@ -2159,23 +2156,6 @@ public enum IfHiddenPolicy {
             public boolean isShowAsDisabledWithDiagnostics() { return this == SHOW_AS_DISABLED_WITH_DIAGNOSTICS; }
         }
 
-        /**
-         * Whether and how to display any properties, actions and collections whose visibility has been vetoed.
-         *
-         * <p>
-         *     By default, such object members are of course hidden.  However, this config property can be used to
-         *     instead show these objects as disabled, with the tooltip indicating why the object member has been
-         *     vetoed.  Setting the property to
-         *     {@link IfHiddenPolicy#SHOW_AS_DISABLED_WITH_DIAGNOSTICS SHOW_AS_DISABLED_WITH_DIAGNOSTICS} shows
-         *     additional detail in the tooltip.
-         * </p>
-         *
-         * <p>
-         *     This config property only applies in prototyping mode.
-         * </p>
-         */
-        private IfHiddenPolicy ifHiddenPolicy = IfHiddenPolicy.HIDE;
-
         public enum IfDisabledPolicy {
             /**
              * The default  behaviour: any properties, collections or actions whose usability has been vetoed
@@ -2191,84 +2171,79 @@ public enum IfDisabledPolicy {
             public boolean isDisabled() { return this == DISABLE; }
             public boolean isShowAsDisabledWithDiagnostics() { return this == SHOW_AS_DISABLED_WITH_DIAGNOSTICS; }
         }
+    }
 
-        /**
-         * Whether and how to display any properties, actions and collections whose usability has been vetoed.
-         *
-         * <p>
-         *     By default, such object members are shown as disabled, with the tooltip indicating why. Setting the
-         *     property to {@link IfDisabledPolicy#SHOW_AS_DISABLED_WITH_DIAGNOSTICS SHOW_AS_DISABLED_WITH_DIAGNOSTICS} shows additional detail in the
-         *     tooltip.
-         * </p>
-         *
-         * <p>
-         *     This config property only applies in prototyping mode.
-         * </p>
-         */
-        private IfDisabledPolicy ifDisabledPolicy = IfDisabledPolicy.DISABLE;
-    }
-
-    private final Viewer viewer = new Viewer();
-    @Data
-    public static class Viewer {
-
-        private final Common common = new Common();
-        @Data
-        public static class Common {
-
-            private final Application application = new Application();
-            @Data
-            public static class Application {
+    public record Viewer(
+        @DefaultValue
+        Common common,
+        @DefaultValue
+        Graphql graphql,
+        @DefaultValue
+        Restfulobjects restfulobjects,
+        @DefaultValue
+        Wicket wicket) {
+
+        public record Common(
+            @DefaultValue
+            Application application,
+            /**
+             * List of organizations or individuals to give credit to, shown as links and icons in the footer.
+             * A maximum of 3 credits can be specified.
+             *
+             * <p>IntelliJ unfortunately does not provide IDE completion for lists of classes; YMMV.
+             *
+             * @implNote - For further discussion, see for example
+             * <a href="https://stackoverflow.com/questions/41417933/spring-configuration-properties-metadata-json-for-nested-list-of-objects">this stackoverflow question</a>
+             * and <a href="https://github.com/spring-projects/spring-boot/wiki/IDE-binding-features#simple-pojo">this wiki page</a>.
+             */
+            @DefaultValue
+            List<Credit> credit) {
 
+            public record Application(
                 /**
                  * Label used on the about page.
                  */
-                private String about;
+                String about,
 
                 /**
                  * Either the location of the image file (relative to the class-path resource root),
                  * or an absolute URL.
                  *
-                 * <p>
-                 * This is rendered on the header panel. An image with a size of 160x40 works well.
+                 * <p>This is rendered on the header panel. An image with a size of 160x40 works well.
                  * If not specified, the application.name is used instead.
-                 * </p>
                  */
                 @jakarta.validation.constraints.Pattern(regexp="^[^/].*$")
-                private Optional<String> brandLogoHeader = Optional.empty();
+                @DefaultValue
+                Optional<String> brandLogoHeader,
 
                 /**
                  * Either the location of the image file (relative to the class-path resource root),
                  * or an absolute URL.
                  *
-                 * <p>
-                 * This is rendered on the sign-in page. An image with a size of 400x40 works well.
+                 * <p>This is rendered on the sign-in page. An image with a size of 400x40 works well.
                  * If not specified, the {@link Application#getName() application name} is used instead.
-                 * </p>
                  */
                 @jakarta.validation.constraints.Pattern(regexp="^[^/].*$")
-                private Optional<String> brandLogoSignin = Optional.empty();
+                @DefaultValue
+                Optional<String> brandLogoSignin,
 
                 /**
                  * Specifies the URL to use of the favIcon.
                  *
-                 * <p>
-                 *     This is expected to be a local resource.
-                 * </p>
+                 * <p>This is expected to be a local resource.
                  */
                 @jakarta.validation.constraints.Pattern(regexp="^[^/].*$")
-                private Optional<String> faviconUrl = Optional.empty();
+                @DefaultValue
+                Optional<String> faviconUrl,
 
                 /**
                  * Specifies the file name containing the menubars.
                  *
-                 * <p>
-                 *     This is expected to be a local resource.
-                 * </p>
+                 * <p>This is expected to be a local resource.
                  */
                 @NotNull @NotEmpty
-                private String menubarsLayoutFile = "menubars.layout.xml";
-
+                @DefaultValue("menubars.layout.xml")
+                String menubarsLayoutFile,
                 /**
                  * Identifies the application on the sign-in page
                  * (unless a {@link Application#brandLogoSignin sign-in} image is configured) and
@@ -2276,66 +2251,44 @@ public static class Application {
                  * (unless a {@link Application#brandLogoHeader header} image is configured).
                  */
                 @NotNull @NotEmpty
-                private String name = "Apache Causeway ™";
+                @DefaultValue("Apache Causeway ™")
+                String name,
 
                 /**
                  * The version of the application, eg 1.0, 1.1, etc.
                  *
-                 * <p>
-                 * If present, then this will be shown in the footer on every page as well as on the
+                 * <p>If present, then this will be shown in the footer on every page as well as on the
                  * about page.
-                 * </p>
                  */
-                private String version;
-            }
+                String version
+                ) {
 
-            /**
-             * List of organisations or individuals to give credit to, shown as links and icons in the footer.
-             * A maximum of 3 credits can be specified.
-             *
-             * <p>
-             * IntelliJ unfortunately does not provide IDE completion for lists of classes; YMMV.
-             * </p>
-             *
-             * <p>
-             * @implNote - For further discussion, see for example
-             * <a href="https://stackoverflow.com/questions/41417933/spring-configuration-properties-metadata-json-for-nested-list-of-objects">this stackoverflow question</a>
-             * and <a href="https://github.com/spring-projects/spring-boot/wiki/IDE-binding-features#simple-pojo">this wiki page</a>.
-             * </p>
-             */
-            private List<Credit> credit = new ArrayList<>();
+            }
 
-            @Data
-            public static class Credit {
+            public record Credit(
                 /**
-                 * URL of an organisation or individual to give credit to, appearing as a link in the footer.
+                 * URL of an organization or individual to give credit to, appearing as a link in the footer.
                  *
-                 * <p>
-                 *     For the credit to appear, the {@link #getUrl() url} must be provided along with either
-                 *     {@link #getName() name} and/or {@link #getImage() image}.
-                 * </p>
+                 * <p>For the credit to appear, the {@link #getUrl() url} must be provided along with either
+                 * {@link #getName() name} and/or {@link #getImage() image}.
                  */
                 @jakarta.validation.constraints.Pattern(regexp="^http[s]?://[^:]+?(:\\d+)?.*$")
-                private String url;
+                String url,
                 /**
-                 * URL of an organisation or individual to give credit to, appearing as text in the footer.
+                 * URL of an organization or individual to give credit to, appearing as text in the footer.
                  *
-                 * <p>
-                 *     For the credit to appear, the {@link #getUrl() url} must be provided along with either
-                 *     {@link #getName() name} and/or {@link #getImage() image}.
-                 * </p>
+                 * <p>For the credit to appear, the {@link #getUrl() url} must be provided along with either
+                 * {@link #getName() name} and/or {@link #getImage() image}.
                  */
-                private String name;
+                String name,
                 /**
                  * Name of an image resource of an organisation or individual, appearing as an icon in the footer.
                  *
-                 * <p>
-                 *     For the credit to appear, the {@link #getUrl() url} must be provided along with either
-                 *     {@link #getName() name} and/or {@link #getImage() image}.
-                 * </p>
+                 * <p>For the credit to appear, the {@link #getUrl() url} must be provided along with either
+                 * {@link #getName() name} and/or {@link #getImage() image}.
                  */
                 @jakarta.validation.constraints.Pattern(regexp="^[^/].*$")
-                private String image;
+                String image) {
 
                 /**
                  * Whether enough information has been defined for the credit to be appear.
@@ -2344,59 +2297,88 @@ public static class Credit {
             }
         }
 
-        private final Graphql graphql = new Graphql();
-        @Data
-        public static class Graphql {
+        public record Graphql(
+            /**
+             * Which {@link SchemaStyle} to expose, 'simple' or 'rich'.  By default both are
+             * exposed under top-level field names.
+             *
+             * @see Schema.Rich#getTopLevelFieldName()
+             * @see Schema.Simple#getTopLevelFieldName()
+             */
+            @DefaultValue("RICH_AND_SIMPLE")
+            SchemaStyle schemaStyle,
+
+            @DefaultValue
+            Schema schema,
+            /**
+             * Which variant of API to expose: {@link ApiVariant#QUERY_ONLY} (which suppresses any actions that mutate the state of the
+             * system), or as {@link ApiVariant#QUERY_AND_MUTATIONS} (which additionally exposes actions that mutate the system as mutations)
+             * or alternatively as {@link ApiVariant#QUERY_WITH_MUTATIONS_NON_SPEC_COMPLIANT}, a query-only schema that relaxes the read-only rule
+             * by exposing actions that mutate the system; it is therefore not compliant with the GraphQL spec),
+             */
+            @DefaultValue("QUERY_AND_MUTATIONS")
+            ApiVariant apiVariant,
+            /**
+             * Which domain objects to include the GraphQL schema.  By default, all domain objects are exposed
+             * (entities and view models).
+             */
+            @DefaultValue("ALL")
+            ApiScope apiScope,
+
+            @DefaultValue
+            MetaData metaData,
+            @DefaultValue
+            Lookup lookup,
+            @DefaultValue
+            Mutation mutation,
+            @DefaultValue
+            ScalarMarshaller scalarMarshaller,
+            @DefaultValue
+            Resources resources,
+            @DefaultValue
+            Authentication authentication
+            ) {
 
             /**
-             * Which style of schema to expose: &quot;simple&quot;, &quot;rich&quot; or some combination of both.
+             * Which style of schema to expose: 'simple', 'rich' or some combination of both.
              *
              * @since 2.0 {@index}
              */
             public enum SchemaStyle {
                 /**
-                 * Expose only the &quot;simple&quot; schema, defining only fields that return the state of the domain
+                 * Expose only the 'simple' schema, defining only fields that return the state of the domain
                  * objects but with no fields to represent additional facets of state (such as whether
                  * an action is hidden or disabled).
                  *
-                 * <p>
-                 *     Suitable for clients where the application logic and state is the responsibility of the client.
-                 * </p>
+                 * <p>Suitable for clients where the application logic and state is the responsibility of the client.
                  */
                 SIMPLE_ONLY,
                 /**
-                 * Expose only the &quot;rich&quot; schema, exposing not only fields that return the state of the domain
+                 * Expose only the 'rich' schema, exposing not only fields that return the state of the domain
                  * objects but <i>also</i> with fields to represent additional facets of state (such as whether
                  * an action is hidden or disabled).
                  *
-                 * <p>
-                 *     Optionally, fields for Scenario (given/when/then) testing may also be added if the
-                 *     {@link Schema.Rich#isEnableScenarioTesting()} config property is set.
-                 * </p>
-                 * <p>
-                 *     Suitable for clients where the application logic and state remains in the backend, within the
-                 *     domain model hosted by Causeway.
-                 * </p>
+                 * <p> Optionally, fields for Scenario (given/when/then) testing may also be added if the
+                 * {@link Schema.Rich#enableScenarioTesting()} config property is set.
+                 *
+                 * <p>Suitable for clients where the application logic and state remains in the backend, within the
+                 * domain model hosted by Causeway.
                  */
                 RICH_ONLY,
                 /**
                  * Exposes both the simple and rich schemas, for the query have each under a field as defined by
-                 * {@link Schema.Simple#getTopLevelFieldName()} (by default &quot;simple&quot;) and
-                 * {@link Schema.Rich#getTopLevelFieldName()} (by default &quot;rich&quot;).
+                 * {@link Schema.Simple#topLevelFieldName()} (by default 'simple') and
+                 * {@link Schema.Rich#topLevelFieldName()} (by default 'rich').
                  *
-                 * <p>
-                 *     For mutations, use the <i>simple</i> schema types.
-                 * </p>
+                 * <p>For mutations, use the <i>simple</i> schema types.
                  */
                 SIMPLE_AND_RICH,
                 /**
                  * Exposes both the simple and rich schemas, for the query have each under a field as defined by
-                 * {@link Schema.Simple#getTopLevelFieldName()} (by default &quot;simple&quot;) and
-                 * {@link Schema.Rich#getTopLevelFieldName()} (by default &quot;rich&quot;).
+                 * {@link Schema.Simple#topLevelFieldName()} (by default 'simple') and
+                 * {@link Schema.Rich#topLevelFieldName()} (by default 'rich').
                  *
-                 * <p>
-                 *     For mutations, use the <i>rich</i> schema types.
-                 * </p>
+                 * <p>For mutations, use the <i>rich</i> schema types.
                  */
                 RICH_AND_SIMPLE,
                 ;
@@ -2409,58 +2391,43 @@ public boolean isSimple() {
                 }
             }
 
-            /**
-             * Which {@link SchemaStyle} to expose, &quot;simple&quot; or &quot;rich&quot;.  By default both are
-             * exposed under top-level field names.
-             *
-             * @see Schema.Rich#getTopLevelFieldName()
-             * @see Schema.Simple#getTopLevelFieldName()
-             */
-            private SchemaStyle schemaStyle = SchemaStyle.RICH_AND_SIMPLE;
-
-            private final Schema schema = new Schema();
-            @Data
-            public static class Schema {
-
-                private final Simple simple = new Simple();
-                @Data
-                public static class Simple {
+            public record Schema(
+                @DefaultValue
+                Simple simple,
+                @DefaultValue
+                Rich rich) {
 
+                public record Simple(
                     /**
-                     * If the {@link #getSchemaStyle()} is set to {@link SchemaStyle#SIMPLE_AND_RICH}, defines the name of the
-                     * top-level field under which the &quot;simple&quot; schema resides.
+                     * If the {@link #schemaStyle()} is set to {@link SchemaStyle#SIMPLE_AND_RICH}, defines the name of the
+                     * top-level field under which the 'simple' schema resides.
                      *
-                     * <p>
-                     *     Ignored for any other {@link #getSchemaStyle()}.
-                     * </p>
+                     * <p>Ignored for any other {@link #schemaStyle()}.
                      */
-                    private String topLevelFieldName = "simple";
-                }
+                    @DefaultValue("simple")
+                    String topLevelFieldName) {
 
-                private final Rich rich = new Rich();
-                @Data
-                public static class Rich {
+                }
 
+                public record Rich(
                     /**
-                     * If the {@link #getSchemaStyle()} is set to {@link SchemaStyle#SIMPLE_AND_RICH}, defines the name of the
-                     * top-level field under which the &quot;rich&quot; schema resides.
+                     * If the {@link #schemaStyle()} is set to {@link SchemaStyle#SIMPLE_AND_RICH}, defines the name of the
+                     * top-level field under which the 'rich' schema resides.
                      *
-                     * <p>
-                     *     Ignored for any other {@link #getSchemaStyle()}.
-                     * </p>
+                     * <p>Ignored for any other {@link #schemaStyle()}.
                      */
-                    private String topLevelFieldName = "rich";
+                    @DefaultValue("rich")
+                    String topLevelFieldName,
 
                     /**
-                     * If the {@link #getSchemaStyle()} is set to either {@link SchemaStyle#RICH_ONLY} or
-                     * {@link SchemaStyle#SIMPLE_AND_RICH}, then determines whether the &quot;Scenario&quot; field is included
+                     * If the {@link #schemaStyle()} is set to either {@link SchemaStyle#RICH_ONLY} or
+                     * {@link SchemaStyle#SIMPLE_AND_RICH}, then determines whether the 'Scenario' field is included
                      * in order to allow given/when/then tests to be expressed.
                      *
-                     * <p>
-                     *     Ignored if the {@link #getSchemaStyle()} is {@link SchemaStyle#SIMPLE_ONLY}.
-                     * </p>
+                     * <p>Ignored if the {@link #schemaStyle()} is {@link SchemaStyle#SIMPLE_ONLY}.
                      */
-                    private boolean enableScenarioTesting = false;
+                    @DefaultValue("false")
+                    boolean enableScenarioTesting) {
                 }
             }
 
@@ -2481,25 +2448,15 @@ public enum ApiVariant {
                  * Exposes an API with both Query and Mutations, but relaxes the constraints for the Query API by also
                  * including idempotent and non-idempotent actions and property setters.
                  *
-                 * <p>
-                 *     <b>IMPORTANT</b>: be aware that the resultant API is not compliant with the rules of the
-                 *     GraphQL spec; in particular, it violates
-                 *     <a href="https://spec.graphql.org/June2018/#sec-Language.Operations">2.3 Operations</a> which
-                 *     states: &quot;query – [is] a read‐only fetch.&quot;
-                 * </p>
+                 * <p><b>IMPORTANT</b>: be aware that the resultant API is not compliant with the rules of the
+                 * GraphQL spec; in particular, it violates
+                 * <a href="https://spec.graphql.org/June2018/#sec-Language.Operations">2.3 Operations</a> which
+                 * states: 'query – [is] a read‐only fetch.'
                  */
                 QUERY_WITH_MUTATIONS_NON_SPEC_COMPLIANT,
                 ;
             }
 
-            /**
-             * Which variant of API to expose: {@link ApiVariant#QUERY_ONLY} (which suppresses any actions that mutate the state of the
-             * system), or as {@link ApiVariant#QUERY_AND_MUTATIONS} (which additionally exposes actions that mutate the system as mutations)
-             * or alternatively as {@link ApiVariant#QUERY_WITH_MUTATIONS_NON_SPEC_COMPLIANT}, a query-only schema that relaxes the read-only rule
-             * by exposing actions that mutate the system; it is therefore not compliant with the GraphQL spec),
-             */
-            private ApiVariant apiVariant = ApiVariant.QUERY_AND_MUTATIONS;
-
             /**
              * Specifies which elements of the metamodel are included within the generated
              * GraphQL spec.
@@ -2512,11 +2469,9 @@ public enum ApiScope {
                  * The generated GraphQL spec is restricted only to include only
                  * {@link org.apache.causeway.applib.annotation.Nature#VIEW_MODEL view model}s.
                  *
-                 * <p>
-                 * Applicable when the GraphQL API is in use by third-party clients, ie public use and not
+                 * <p>Applicable when the GraphQL API is in use by third-party clients, ie public use and not
                  * under the control of the authors of the backend Apache Causeway application.
                  * Exposing entities also would couple the GraphQL client too deeply to the backend implementation.
-                 * </p>
                  */
                 VIEW_MODELS,
                 /**
@@ -2524,75 +2479,67 @@ public enum ApiScope {
                  * {@link org.apache.causeway.applib.annotation.Nature#ENTITY domain entities} as well as
                  * {@link org.apache.causeway.applib.annotation.Nature#VIEW_MODEL view model}s.
                  *
-                 * <p>
-                 * This is perfectly acceptable where the team developing the GraphQL client is the
+                 * <p>This is perfectly acceptable where the team developing the GraphQL client is the
                  * same as the team developing the backend service ... the use of the Web
-                 * API between the client and server is a private implementation detail of
+                 * API between the client and server is a  implementation detail of
                  * the application.
-                 * </p>
+                 *
                  */
                 ALL,
                 ;
             }
 
-            /**
-             * Which domain objects to include the GraphQL schema.  By default, all domain objects are exposed
-             * (entities and view models).
-             */
-            private ApiScope apiScope = ApiScope.ALL;
-
-            private final MetaData metaData = new MetaData();
-            @Data
-            public static class MetaData {
+            public record MetaData(
                 /**
-                 * Note that field names <i>cannot</i> being with &quot;__&quot;, as that is reserved by the
+                 * Note that field names <i>cannot</i> being with '__', as that is reserved by the
                  * underlying GraphQL implementation.
                  */
-                private String fieldName = "_meta";
+                @DefaultValue("_meta")
+                String fieldName) {
             }
 
-            private final Lookup lookup = new Lookup();
-            @Data
-            public static class Lookup {
+            public record Lookup(
                 /**
                  * Lookup field prefix
                  */
-                private String fieldNamePrefix = "";
+                @DefaultValue("")
+                String fieldNamePrefix,
                 /**
                  * Lookup field suffix
                  */
-                private String fieldNameSuffix = "";
+                @DefaultValue("")
+                String fieldNameSuffix,
                 /**
                  * This is the name of the synthetic first argument used to locate the object to be looked up.
                  */
-                private String argName = "object";
+                @DefaultValue("object")
+                String argName) {
             }
 
-            private final Mutation mutation = new Mutation();
-            @Data
-            public static class Mutation {
+            public record Mutation(
                 /**
                  * The name of the synthetic argument of mutators representing the target domain object.
                  */
-                private String targetArgName = "_target";
+                @DefaultValue("_target")
+                String targetArgName) {
             }
 
-            private final ScalarMarshaller scalarMarshaller = new ScalarMarshaller();
-            @Data
-            public static class ScalarMarshaller {
-
+            public record ScalarMarshaller(
                 /**
                  * For {@link java.time.LocalTime} (since Java 8)
                  */
-                private String localTimeFormat = "HH:mm:ss";
+                @DefaultValue("HH:mm:ss")
+                String localTimeFormat,
                 /**
                  * For {@link java.time.LocalDate} (since Java 8)
                  */
-                private String localDateFormat = "yyyy-MM-dd";
+                @DefaultValue("yyyy-MM-dd")
+                String localDateFormat,
                 /**
                  * for {@link java.time.ZonedDateTime} (since Java 8)
                  */
-                private String zonedDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ssXXX";
+                @DefaultValue("yyyy-MM-dd'T'HH:mm:ssXXX")
+                String zonedDateTimeFormat) {
             }
 
             /**
@@ -2604,208 +2551,177 @@ public enum ResponseType {
                 /**
                  * Do not allow the resources to be downloaded at all.  This is the default.
                  *
-                 * <p>
-                 *     In this case any {@link org.apache.causeway.applib.value.Blob} and
-                 *     {@link org.apache.causeway.applib.value.Clob} properties will <i>not</i> provide a link to
-                 *     the URL.  Attempting to download from the resource controller will result in a 403 (forbidden).
-                 * </p>
+                 * <p>In this case any {@link org.apache.causeway.applib.value.Blob} and
+                 * {@link org.apache.causeway.applib.value.Clob} properties will <i>not</i> provide a link to
+                 * the URL.  Attempting to download from the resource controller will result in a 403 (forbidden).
                  */
                 FORBIDDEN,
                 /**
                  * Allows resources to be downloaded directly.
                  *
-                 * <p>
-                 *     <b>IMPORTANT: </b> if enabling this configuration property, make sure that the <code>ResourcesController</code> endpoints
-                 *     are secured appropriately.
-                 * </p>
+                 * <p><b>IMPORTANT: </b> if enabling this configuration property, make sure that the <code>ResourcesController</code> endpoints
+                 * are secured appropriately.
                  */
                 DIRECT,
                 /**
                  * Allows resources to be downloaded as attachments (using <code>Content-Disposition</code> header).
                  *
-                 * <p>
-                 *     <b>IMPORTANT: </b> if enabling this configuration property, make sure that the <code>ResourcesController</code> endpoints
+                 * <p><b>IMPORTANT: </b> if enabling this configuration property, make sure that the <code>ResourcesController</code> endpoints
                  *     are secured appropriately.
-                 * </p>
                  */
                 ATTACHMENT,
                 ;
             }
 
-            @Getter
-            private final Resources resources = new Resources();
-            @Data
-            public static class Resources {
+            public record Resources(
                 /**
                  * How resources ({@link org.apache.causeway.applib.value.Blob} bytes,
                  * {@link org.apache.causeway.applib.value.Clob} chars, grids and icons) can be downloaded from the
                  * resource controller.
                  *
-                 * <p>
-                 *     By default the download of these resources if {@link ResponseType#FORBIDDEN}, but alternatively
-                 *     they can be enabled to download either {@link ResponseType#DIRECT}ly or as an
-                 *     {@link ResponseType#ATTACHMENT}.
-                 * </p>
+                 * <p>By default the download of these resources if {@link ResponseType#FORBIDDEN}, but alternatively
+                 * they can be enabled to download either {@link ResponseType#DIRECT}ly or as an
+                 * {@link ResponseType#ATTACHMENT}.
                  */
-                private ResponseType responseType = ResponseType.FORBIDDEN;
+                @DefaultValue("FORBIDDEN")
+                ResponseType responseType) {
             }
 
-            @Getter
-            private final Authentication authentication = new Authentication();
-            @Data
-            public static class Authentication {
-
-                @Getter
-                private final Fallback fallback = new Fallback();
-                @Data
-                public static class Fallback {
+            public record Authentication(
+                @DefaultValue
+                Fallback fallback) {
 
+                public record Fallback(
                     /**
-                     * Used as the default username (if not provided by other means).
+                     * Used as the default user name (if not provided by other means).
                      */
-                    private String username;
-
+                    String username,
                     /**
-                     * Used as the set of roles for the default {@link #getUsername() username} (if not provided by other means).
+                     * Used as the set of roles for the default {@link #username()} (if not provided by other means).
                      */
-                    private List<String> roles;
+                    List<String> roles
+                    ) {
                 }
             }
         }
 
-        private final Restfulobjects restfulobjects = new Restfulobjects();
-        @Data
-        public static class Restfulobjects {
-
-            @Getter
-            private final Authentication authentication = new Authentication();
-            @Data
-            public static class Authentication {
-                /**
-                 * Defaults to <code>org.apache.causeway.viewer.restfulobjects.viewer.webmodule.auth.AuthenticationStrategyBasicAuth</code>.
-                 */
-                private String strategyClassName = "org.apache.causeway.viewer.restfulobjects.viewer.webmodule.auth.AuthenticationStrategyBasicAuth";
-            }
-
+        public record Restfulobjects(@DefaultValue
+            Authentication authentication,
             /**
              * Whether to enable the <code>x-ro-follow-links</code> support, to minimize round trips.
              *
-             * <p>
-             *     The RO viewer provides the capability for the client to set the optional
-             *     <code>x-ro-follow-links</code> query parameter, as described in section 34.4 of the RO spec v1.0.
-             *     If used, the resultant representation includes the result of following the associated link, but
-             *     through a server-side "join", somewhat akin to GraphQL.
-             * </p>
+             * <p>The RO viewer provides the capability for the client to set the optional
+             * <code>x-ro-follow-links</code> query parameter, as described in section 34.4 of the RO spec v1.0.
+             * If used, the resultant representation includes the result of following the associated link, but
+             * through a server-side "join", somewhat akin to GraphQL.
              *
-             * <p>
-             *     By default this functionality is disabled, this configuration property enables the feature.
-             *     If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
-             * </p>
+             * <p>By default this functionality is disabled, this configuration property enables the feature.
+             * If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
              */
-            private boolean honorUiHints = false;
+            @DefaultValue("false")
+        boolean honorUiHints,
 
-            /**
-             * When rendering domain objects, if set the representation returned is stripped back to a minimal set,
-             * excluding links to actions and collections and with a simplified representation of an object's
-             * properties.
-             *
-             * <p>
-             *     This is disabled by default.  If enabled, then the representations returned are non-standard with
-             *     respect to the RO Spec v1.0.
-             * </p>
-             */
-            private boolean objectPropertyValuesOnly = false;
+        /**
+         * When rendering domain objects, if set the representation returned is stripped back to a minimal set,
+         * excluding links to actions and collections and with a simplified representation of an object's
+         * properties.
+         *
+         * <p>This is disabled by default.  If enabled, then the representations returned are non-standard with
+         * respect to the RO Spec v1.0.
+         */
+        @DefaultValue("false")
+        boolean objectPropertyValuesOnly,
 
-            /**
-             * If set, then any unrecognised <code>Accept</code> headers will result in an HTTP <i>Not Acceptable</i>
-             * response code (406).
-             */
-            private boolean strictAcceptChecking = false;
+        /**
+         * If set, then any unrecognized <code>Accept</code> headers will result in an HTTP <i>Not Acceptable</i>
+         * response code (406).
+         */
+        @DefaultValue("false")
+        boolean strictAcceptChecking,
 
-            /**
-             * If set, then the representations returned will omit any links to the formal domain-type representations.
-             */
-            private boolean suppressDescribedByLinks = false;
+        /**
+         * If set, then the representations returned will omit any links to the formal domain-type representations.
+         */
+        @DefaultValue("false")
+        boolean suppressDescribedByLinks,
 
-            /**
-             * If set, then - should there be an interaction with an action, property or collection that is disabled -
-             * then this will prevent the <code>disabledReason</code> reason from being added to the returned
-             * representation.
-             *
-             * <p>
-             *     This is disabled by default.  If enabled, then the representations returned are non-standard with
-             *     respect to the RO Spec v1.0.
-             * </p>
-             */
-            private boolean suppressMemberDisabledReason = false;
+        /**
+         * If set, then - should there be an interaction with an action, property or collection that is disabled -
+         * then this will prevent the <code>disabledReason</code> reason from being added to the returned
+         * representation.
+         *
+         * <p>This is disabled by default.  If enabled, then the representations returned are non-standard with
+         * respect to the RO Spec v1.0.
+         */
+        @DefaultValue("false")
+        boolean suppressMemberDisabledReason,
 
-            /**
-             * If set, then the <code>x-causeway-format</code> key (under <code>extensions</code>) for properties will be
-             * suppressed.
-             *
-             * <p>
-             *     This is disabled by default.  If enabled, then the representations returned are non-standard with
-             *     respect to the RO Spec v1.0.
-             * </p>
-             */
-            private boolean suppressMemberExtensions = false;
+        /**
+         * If set, then the <code>x-causeway-format</code> key (under <code>extensions</code>) for properties will be
+         * suppressed.
+         *
+         * <p>This is disabled by default.  If enabled, then the representations returned are non-standard with
+         * respect to the RO Spec v1.0.
+         */
+        @DefaultValue("false")
+        boolean suppressMemberExtensions,
 
-            /**
-             * If set, then the <code>id</code> key for all members will be suppressed.
-             *
-             * <p>
-             *     This is disabled by default.  If enabled, then the representations returned are non-standard with
-             *     respect to the RO Spec v1.0.
-             * </p>
-             */
-            private boolean suppressMemberId = false;
+        /**
+         * If set, then the <code>id</code> key for all members will be suppressed.
+         *
+         * <p>This is disabled by default.  If enabled, then the representations returned are non-standard with
+         * respect to the RO Spec v1.0.
+         */
+        @DefaultValue("false")
+        boolean suppressMemberId,
 
-            /**
-             * If set, then the detail link (in other words <code>links[rel='details' ...]</code>) for all members
-             * will be suppressed.
-             *
-             * <p>
-             *     This is disabled by default.  If enabled, then the representations returned are non-standard with
-             *     respect to the RO Spec v1.0.
-             * </p>
-             */
-            private boolean suppressMemberLinks = false;
+        /**
+         * If set, then the detail link (in other words <code>links[rel='details' ...]</code>) for all members
+         * will be suppressed.
+         *
+         * <p>This is disabled by default.  If enabled, then the representations returned are non-standard with
+         * respect to the RO Spec v1.0.
+         */
+        @DefaultValue("false")
+        boolean suppressMemberLinks,
 
-            /**
-             * If set, then the update link (in other words <code>links[rel='update'... ]</code> to perform a bulk
-             * update of an object) will be suppressed.
-             *
-             * <p>
-             *     This is disabled by default.  If enabled, then the representations returned are non-standard with
-             *     respect to the RO Spec v1.0.
-             * </p>
-             */
-            private boolean suppressUpdateLink = false;
+        /**
+         * If set, then the update link (in other words <code>links[rel='update'... ]</code> to perform a bulk
+         * update of an object) will be suppressed.
+         *
+         * <p> This is disabled by default.  If enabled, then the representations returned are non-standard with
+         * respect to the RO Spec v1.0.
+         */
+        @DefaultValue("false")
+        boolean suppressUpdateLink,
 
-			@jakarta.validation.constraints.Pattern(regexp="^[/].*[^/]$")
-			private String basePath = "/restful";
+        @jakarta.validation.constraints.Pattern(regexp="^[/].*[^/]$")
+        @DefaultValue("/restful")
+        String basePath,
 
-            /**
-             * If left unset (the default), then the RO viewer will use the {@link jakarta.ws.rs.core.UriInfo}
-             * (injected using {@link jakarta.ws.rs.core.Context}) to figure out the base Uri (used to render
-             * <code>href</code>s).
-             *
-             * <p>
-             * This will be correct much of the time, but will almost certainly be wrong if there is a reverse proxy.
-             * </p>
-             *
-             * <p>
-             * If set, eg <code>https://dev.myapp.com/</code>, then this value will be used instead.
-             * </p>
-             */
-            @jakarta.validation.constraints.Pattern(regexp="^http[s]?://[^:]+?(:\\d+)?/([^/]+/)*+$")
-            private Optional<String> baseUri = Optional.empty();
-        }
+        /**
+         * If left unset (the default), then the RO viewer will use the {@link jakarta.ws.rs.core.UriInfo}
+         * (injected using {@link jakarta.ws.rs.core.Context}) to figure out the base Uri (used to render
+         * <code>href</code>s).
+         *
+         * <p>This will be correct much of the time, but will almost certainly be wrong if there is a reverse proxy.
+         *
+         * <p>If set, eg <code>https://dev.myapp.com/</code>, then this value will be used instead.
+         */
+        @SuppressWarnings("javadoc") @jakarta.validation.constraints.Pattern(regexp="^http[s]?://[^:]+?(:\\d+)?/([^/]+/)*+$")
+        @DefaultValue
+        Optional<String> baseUri) {
 
-        private final Wicket wicket = new Wicket();
-        @Data
-        public static class Wicket {
+            public record Authentication(
+                /**
+                 * Defaults to <code>org.apache.causeway.viewer.restfulobjects.viewer.webmodule.auth.AuthenticationStrategyBasicAuth</code>.
+                 */
+                @DefaultValue("org.apache.causeway.viewer.restfulobjects.viewer.webmodule.auth.AuthenticationStrategyBasicAuth")
+                String strategyClassName) {
+            }
+        }
 
+        public record Wicket(
             /**
              * Whether actions, that have explicit <code>hidden = Where</code> semantics
              * to enable them in tables,
@@ -2814,386 +2730,362 @@ public static class Wicket {
              * where corresponding domain object actions are gathered into an additional
              * (typically trailing) column (labeled 'action-column').
              */
-            private boolean actionColumnEnabled = true;
+            @DefaultValue("true")
+            boolean actionColumnEnabled,
 
             /**
              * Whether actions, that on click will show a dialog,
              * should be indicated by a trailing ellipsis on the action's label.
-             * <p>
-             * Applies to both, action buttons and action menu items.
+             *
+             * <p>Applies to both, action buttons and action menu items.
              */
-            private boolean actionIndicationWhenBoundToDialog = true;
+            @DefaultValue("true")
+            boolean actionIndicationWhenBoundToDialog,
 
             /**
              * Specifies the subclass of
              * <code>org.apache.causeway.viewer.wicket.viewer.wicketapp.CausewayWicketApplication</code> that is used to
              * bootstrap Wicket.
              *
-             * <p>
-             *     There is usually very little reason to change this from its default.
-             * </p>
+             * <p>There is usually very little reason to change this from its default.
              */
-            private String app = "org.apache.causeway.viewer.wicket.viewer.wicketapp.CausewayWicketApplication";
+            @DefaultValue("org.apache.causeway.viewer.wicket.viewer.wicketapp.CausewayWicketApplication")
+            String app,
 
             /**
              * Whether the Ajax debug should be shown, by default this is disabled.
              */
-            private boolean ajaxDebugMode = false;
+            @DefaultValue("false")
+            boolean ajaxDebugMode,
 
             /**
              * The base path at which the Wicket viewer is mounted.
              */
             @jakarta.validation.constraints.Pattern(regexp="^[/](.*[/]|)$") @NotNull @NotEmpty
-            private String basePath = "/wicket/";
+            @DefaultValue("/wicket/")
+            String basePath,
 
             /**
              * If the end user uses a deep link to access the Wicket viewer, but is not authenticated, then this
              * configuration property determines whether to continue through to that original destination once
              * authenticated, or simply to go to the home page.
              *
-             * <p>
-             *     The default behaviour is to honour the original destination requested.
-             * </p>
+             * <p>The default behaviour is to honour the original destination requested.
              */
-            private boolean clearOriginalDestination = false;
+            @DefaultValue("false")
+            boolean clearOriginalDestination,
 
             /**
              * Whether the clear-field-button, that allows to clear a null-able (optional) field
              * (a property or a dialog argument) is enabled for rendering or not.
              *
-             * <p>
-             *     The default is to enable (show) the clear-field-button.
-             * </p>
+             * <p>The default is to enable (show) the clear-field-button.
              */
-            private boolean clearFieldButtonEnabled = true;
+            @DefaultValue("true")
+            boolean clearFieldButtonEnabled,
 
             /**
              * In prototyping mode, a text icon is appended to any property that is disabled, with its tool-tip explaining why the property is disabled.
              * This configuration property can be used to suppress the icon, even in prototyping mode, if desired.
              *
-             * <p>
-             *     The default is to enable (show) the text icon (if in prototyping mode).
-             * </p>
+             * <p>The default is to enable (show) the text icon (if in prototyping mode).
              */
-            private boolean disableReasonExplanationInPrototypingModeEnabled = true;
+            @DefaultValue("true")
+            boolean disableReasonExplanationInPrototypingModeEnabled,
 
             /**
              * URL of file to read any custom CSS, relative to <code>static</code> package on the class path.
              *
-             * <p>
-             *     A typical value is <code>css/application.css</code>.  This will result in this file being read
-             *     from the <code>static/css</code> directory (because static resources such as CSS are mounted by
-             *     Spring by default under <code>static</code> package).
-             * </p>
+             * <p>A typical value is <code>css/application.css</code>.  This will result in this file being read
+             * from the <code>static/css</code> directory (because static resources such as CSS are mounted by
+             * Spring by default under <code>static</code> package).
              */
             @jakarta.validation.constraints.Pattern(regexp="^[^/].*$")
-            private Optional<String> css = Optional.empty();
+            @DefaultValue
+            Optional<String> css,
 
             /**
              * Whether the dialog mode rendered when invoking actions on domain objects should be to use
              * the sidebar (the default) or to use a modal dialog.
              *
-             * <p>
-             *     This can be overridden on a case-by-case basis using {@link ActionLayout#promptStyle()}.
-             * </p>
+             * <p>This can be overridden on a case-by-case basis using {@link ActionLayout#promptStyle()}.
              */
-            private DialogMode dialogMode = DialogMode.SIDEBAR;
+            @DefaultValue("SIDEBAR")
+            DialogMode dialogMode,
 
             /**
              * Whether the dialog mode rendered when invoking actions on domain services (that is, menus) should be to
              * use a modal dialog (the default) or to use the sidebar panel.
              *
-             * <p>
-             *     This can be overridden on a case-by-case basis using {@link ActionLayout#promptStyle()}.
-             * </p>
+             * <p>This can be overridden on a case-by-case basis using {@link ActionLayout#promptStyle()}.
              */
-            private DialogMode dialogModeForMenu = DialogMode.MODAL;
+            @DefaultValue("MODAL")
+            DialogMode dialogModeForMenu,
 
             /**
              * URL of file to read any custom JavaScript, relative to <code>static</code> package on the class path.
              *
-             * <p>
-             *     A typical value is <code>js/application.js</code>.  This will result in this file being read
-             *     from the <code>static/js</code> directory (because static resources such as CSS are mounted by
-             *     Spring by default under <code>static</code> package).
-             * </p>
+             * <p>A typical value is <code>js/application.js</code>.  This will result in this file being read
+             * from the <code>static/js</code> directory (because static resources such as CSS are mounted by
+             * Spring by default under <code>static</code> package).
              */
             @jakarta.validation.constraints.Pattern(regexp="^[^/].*$")
-            private Optional<String> js = Optional.empty();
+            @DefaultValue
+            Optional<String> js,
 
             /**
              * If specified, then is rendered on each page to enable live reload.
              *
-             * <p>
-             *     Configuring live reload also requires an appropriate plugin to the web browser (eg see
-             *     <a href="http://livereload.com/">livereload.com</a> and a mechanism to trigger changes, eg by
-             *     watching <code>Xxx.layout.xml</code> files.
-             * </p>
+             * <p>Configuring live reload also requires an appropriate plugin to the web browser (eg see
+             * <a href="http://livereload.com/">livereload.com</a> and a mechanism to trigger changes, eg by
+             * watching <code>Xxx.layout.xml</code> files.
              */
-            private Optional<String> liveReloadUrl = Optional.empty();
+            @DefaultValue
+            Optional<String> liveReloadUrl,
 
             /**
              * The maximum number of characters to use to render the title of a domain object (alongside the icon)
-             * in any table, if not otherwise overridden by either {@link #getMaxTitleLengthInParentedTables()}
-             * or {@link #getMaxTitleLengthInStandaloneTables()}.
+             * in any table, if not otherwise overridden by either {@link #maxTitleLengthInParentedTables()}
+             * or {@link #maxTitleLengthInStandaloneTables()}.
              *
-             * <p>
-             *     If truncated, then the remainder of the title will be replaced with ellipses (...).
-             * </p>
+             * <p>If truncated, then the remainder of the title will be replaced with ellipses (...).
              */
-            private int maxTitleLengthInTables = 12;
+            @DefaultValue("12")
+            int maxTitleLengthInTables,
 
-            private static boolean isMaxTitleLenghtValid(final int len) {
-                return len>=0;
-            }
-            private int asTitleLenght(final int len) {
-                return isMaxTitleLenghtValid(len)
-                        ? len
-                        : getMaxTitleLengthInTables();
-            }
-
-            private int maxTitleLengthInParentedTables = -1;
-
-            /**
-             * The maximum number of characters to use to render the title of a domain object (alongside the icon) in a
-             * parented table.
-             *
-             * <p>
-             *     If truncated, then the remainder of the title will be replaced with ellipses (...).
-             * </p>
-             *
-             * <p>
-             *     If invalid or not specified, then the value of {@link #getMaxTitleLengthInTables()} is used.
-             * </p>
-             */
-            public int getMaxTitleLengthInParentedTables() {
-                return asTitleLenght(maxTitleLengthInParentedTables);
-            }
-
-            public void setMaxTitleLengthInParentedTables(final int val) {
-                maxTitleLengthInParentedTables = val;
-            }
-
-            private int maxTitleLengthInStandaloneTables = -1;
-
-            /**
-             * The maximum number of characters to use to render the title of a domain object (alongside the icon)
-             * in a standalone table, ie the result of invoking an action.
-             *
-             * <p>
-             *     If truncated, then the remainder of the title will be replaced with ellipses (...).
-             * </p>
-             *
-             * <p>
-             *     If invalid or not specified, then the value of {@link #getMaxTitleLengthInTables()} is used.
-             * </p>
-             */
-            public int getMaxTitleLengthInStandaloneTables() {
-                return asTitleLenght(maxTitleLengthInStandaloneTables);
-            }
-            /**
-             * The maximum length that a title of an object will be shown when rendered in a standalone table;
-             * will be truncated beyond this (with ellipses to indicate the truncation).
-             * <p>
-             * If set to negative or zero, sets defaults instead.
-             */
-            public void setMaxTitleLengthInStandaloneTables(final int val) {
-                maxTitleLengthInStandaloneTables = val;
-            }
+            @DefaultValue("-1")
+            int maxTitleLengthInParentedTables,
+            @DefaultValue("-1")
+            int maxTitleLengthInStandaloneTables,
 
             /**
              * If a table has no property columns,
              * for the title column this value is used,
              * to determine how many characters to render for the table element titles.
-             * <p>
-             * Introduced for the case when max-title-length is set to zero for tables in general,
+             *
+             * <p>Introduced for the case when max-title-length is set to zero for tables in general,
              * that if a table has no property columns an exception to that title suppression can be made.
              */
-            private int maxTitleLengthInTablesNotHavingAnyPropertyColumn = 80;
+            @DefaultValue("80")
+            int maxTitleLengthInTablesNotHavingAnyPropertyColumn,
 
             /**
              * Whether to use a modal dialog for property edits and for actions associated with properties.
              *
-             * <p>
-             * This can be overridden on a case-by-case basis using <code>@PropertyLayout#promptStyle</code> and
+             * <p>This can be overridden on a case-by-case basis using <code>@PropertyLayout#promptStyle</code> and
              * <code>@ActionLayout#promptStyle</code>.
-             * </p>
              *
-             * <p>
-             * This behaviour is disabled by default; the viewer will use an inline prompt in these cases, making for a smoother
+             * <p>This behaviour is disabled by default; the viewer will use an inline prompt in these cases, making for a smoother
              * user experience.
-             * </p>
              */
-            private PromptStyle promptStyle = PromptStyle.INLINE;
+            @DefaultValue("INLINE")
+            PromptStyle promptStyle,
 
             /**
              * Whether to redirect to a new page, even if the object being shown (after an action invocation or a property edit)
              * is the same as the previous page.
              *
-             * <p>
-             * This behaviour is disabled by default; the viewer will update the existing page if it can, making for a
+             * <p>This behaviour is disabled by default; the viewer will update the existing page if it can, making for a
              * smoother user experience. If enabled then this reinstates the pre-1.15.0 behaviour of redirecting in all cases.
-             * </p>
              */
-            private boolean redirectEvenIfSameObject = false;
+            @DefaultValue("false")
+            boolean redirectEvenIfSameObject,
 
             /**
              * In Firefox and more recent versions of Chrome 54+, cannot copy out of disabled fields; instead we use the
              * readonly attribute (https://www.w3.org/TR/2014/REC-html5-20141028/forms.html#the-readonly-attribute)
              *
-             * <p>
-             * This behaviour is enabled by default but can be disabled using this flag
-             * </p>
+             * <p>This behaviour is enabled by default but can be disabled using this flag
              */
-            private boolean replaceDisabledTagWithReadonlyTag = true;
+            @DefaultValue("true")
+            boolean replaceDisabledTagWithReadonlyTag,
 
             /**
              * Whether to disable a form submit button after it has been clicked, to prevent users causing an error if they
              * do a double click.
              *
-             * This behaviour is enabled by default, but can be disabled using this flag.
+             * <p>This behaviour is enabled by default, but can be disabled using this flag.
              */
-            private boolean preventDoubleClickForFormSubmit = true;
+            @DefaultValue("true")
+            boolean preventDoubleClickForFormSubmit,
 
             /**
              * Whether to disable a no-arg action button after it has been clicked, to prevent users causing an error if they
              * do a double click.
              *
-             * <p>
-             * This behaviour is enabled by default, but can be disabled using this flag.
-             * </p>
+             * <p>This behaviour is enabled by default, but can be disabled using this flag.
              */
-            private boolean preventDoubleClickForNoArgAction = true;
+            @DefaultValue("true")
+            boolean preventDoubleClickForNoArgAction,
 
             /**
              * Whether to show the footer menu bar.
              *
-             * <p>
-             *     This is enabled by default.
-             * </p>
+             * <p>This is enabled by default.
              */
-            private boolean showFooter = true;
+            @DefaultValue("true")
+            boolean showFooter,
 
             /**
              * Whether Wicket tags should be stripped from the markup.
              *
-             * <p>
-             *      By default this is enabled, in other words Wicket tags are stripped.  Please be aware that if
-             *      tags are <i>not</i> stripped, then this may break CSS rules on some browsers.
-             * </p>
+             * <p>By default this is enabled, in other words Wicket tags are stripped.  Please be aware that if
+             * tags are <i>not</i> stripped, then this may break CSS rules on some browsers.
              */
-            private boolean stripWicketTags = true;
+            @DefaultValue("true")
+            boolean stripWicketTags,
 
             /**
              * Whether to suppress the sign-up link on the sign-in page.
              *
-             * <p>
-             *     Although this is disabled by default (in other words the sign-up link is not suppressed), note that
-             *     in addition the application must provide an implementation of the
-             *     {@link UserRegistrationService} as well as a
-             *     configured {@link EmailNotificationService} (same conditions
-             *     as for the {@link #isSuppressPasswordReset()} password reset link).
-             * </p>
+             * <p>Although this is disabled by default (in other words the sign-up link is not suppressed), note that
+             * in addition the application must provide an implementation of the
+             * {@link UserRegistrationService} as well as a
+             * configured {@link EmailNotificationService} (same conditions
+             * as for the {@link #isSuppressPasswordReset()} password reset link).
              */
-            private boolean suppressSignUp = false;
+            @DefaultValue("false")
+            boolean suppressSignUp,
 
             /**
              * Whether to suppress the password reset link on the sign-in page.
              *
-             * <p>
-             *     Although this is disabled by default (in other words the 'reset password' link is not suppressed),
-             *     note that in addition the application must provide an implementation of the
-             *     {@link UserRegistrationService} as well as a
-             *     configured {@link EmailNotificationService} (same conditions
-             *     as for the {@link #isSuppressSignUp()} sign-up link).
-             * </p>
+             * <p>Although this is disabled by default (in other words the 'reset password' link is not suppressed),
+             * note that in addition the application must provide an implementation of the
+             * {@link UserRegistrationService} as well as a
+             * configured {@link EmailNotificationService} (same conditions
+             * as for the {@link #isSuppressSignUp()} sign-up link).
              */
-            private boolean suppressPasswordReset = false;
+            @DefaultValue("false")
+            boolean suppressPasswordReset,
 
             /**
              * How to interpret tooltip content, e.g. as HTML or TEXT. default = TEXT
              */
-            private TextMode tooltipTextMode = TextMode.defaults();
+            @DefaultValue("TEXT")
+            TextMode tooltipTextMode,
 
             /**
              * Whether to show an indicator for a form submit button that it has been clicked.
              *
-             * <p>
-             * This behaviour is enabled by default.
-             * </p>
+             * <p>This behaviour is enabled by default.
              */
-            private boolean useIndicatorForFormSubmit = true;
+            @DefaultValue("true")
+            boolean useIndicatorForFormSubmit,
 
             /**
              * Whether to show an indicator for a no-arg action button that it has been clicked.
              *
-             * <p>
-             * This behaviour is enabled by default.
-             * </p>
+             * <p>This behaviour is enabled by default.
              */
-            private boolean useIndicatorForNoArgAction = true;
+            @DefaultValue("true")
+            boolean useIndicatorForNoArgAction,
 
             /**
              * Whether to show an indicator for a sortable column (that is an up-down arrow icon).
              *
-             * <p>
-             * This behaviour is disabled by default.
-             * </p>
+             * <p>This behaviour is disabled by default.
              */
-            private boolean useIndicatorForSortableColumn = false;
+            @DefaultValue("false")
+            boolean useIndicatorForSortableColumn,
 
             /**
              * Whether the Wicket source plugin should be enabled; if so, the markup includes links to the Wicket source.
              *
-             * <p>
-             *     This behaviour is disabled by default.  Please be aware that enabling it can substantially impact
-             *     performance.
-             * </p>
+             * <p>This behaviour is disabled by default.  Please be aware that enabling it can substantially impact
+             * performance.
              */
-            private boolean wicketSourcePlugin = false;
+            @DefaultValue("false")
+            boolean wicketSourcePlugin,
+
+            @DefaultValue
+            BookmarkedPages bookmarkedPages,
+            @DefaultValue
+            Breadcrumbs breadcrumbs,
+            @DefaultValue
+            DatePicker datePicker,
+            @DefaultValue
+            DevelopmentUtilities developmentUtilities,
+            @DefaultValue
+            RememberMe rememberMe,
+            @DefaultValue
+            Themes themes,
+            @DefaultValue
+            Welcome welcome,
+            @DefaultValue
+            MessagePopups messagePopups) {
 
-            private final BookmarkedPages bookmarkedPages = new BookmarkedPages();
-            @Data
-            public static class BookmarkedPages {
+            private static boolean isMaxTitleLenghtValid(final int len) {
+                return len>=0;
+            }
+            private int asTitleLenght(final int len) {
+                return isMaxTitleLenghtValid(len)
+                    ? len
+                        : maxTitleLengthInTables();
+            }
+
+            /**
+             * The maximum number of characters to use to render the title of a domain object (alongside the icon) in a
+             * parented table.
+             *
+             * <p>If truncated, then the remainder of the title will be replaced with ellipses (...).
+             *
+             * <p>If invalid or not specified, then the value of {@link #maxTitleLengthInTables()} is used.
+             */
+            public int maxTitleLengthInParentedTables() {
+                return asTitleLenght(maxTitleLengthInParentedTables);
+            }
 
+            /**
+             * The maximum number of characters to use to render the title of a domain object (alongside the icon)
+             * in a standalone table, ie the result of invoking an action.
+             *
+             * <p>If truncated, then the remainder of the title will be replaced with ellipses (...).
+             *
+             * <p>If invalid or not specified, then the value of {@link #maxTitleLengthInTables()} is used.
+             */
+            public int maxTitleLengthInStandaloneTables() {
+                return asTitleLenght(maxTitleLengthInStandaloneTables);
+            }
+
+            public record BookmarkedPages(
                 /**
-                 * Whether the panel providing linsk to previously visited object should be accessible from the top-left of the header.
+                 * Whether the panel providing links to previously visited object should be accessible from the top-left of the header.
                  */
-                private boolean showChooser = true;
+                @DefaultValue("true")
+                boolean showChooser,
 
                 /**
                  * Specifies the maximum number of bookmarks to show.
                  *
                  * <p>
                  *     These are aged out on an MRU-LRU basis.
-                 * </p>
+                 *
                  */
-                private int maxSize = 15;
+                @DefaultValue("15")
+                int maxSize,
 
                 /**
                  * Whether the drop-down list of previously visited objects should be shown in the footer.
                  */
-                private boolean showDropDownOnFooter = true;
-
+                @DefaultValue("true")
+                boolean showDropDownOnFooter) {
             }
 
-            private final Breadcrumbs breadcrumbs = new Breadcrumbs();
-            @Data
-            public static class Breadcrumbs {
+            public record Breadcrumbs(
                 /**
                  * Whether to enable the 'where am i' feature, in other words the breadcrumbs.
                  */
-                private boolean enabled = true;
+                @DefaultValue("true")
+                boolean enabled,
                 /**
                  *
                  */
-                private int maxParentChainLength = 64;
+                @DefaultValue("64")
+                int maxParentChainLength) {
             }
 
-            private final DatePicker datePicker = new DatePicker();
-            @Data
-            public static class DatePicker {
-
+            public record DatePicker(
                 /**
                  * Defines the first date available in the date picker.
                  * <p>
@@ -3202,7 +3094,8 @@ public static class DatePicker {
                  * Use time zone 'Z', as the date/time picker UI component is not wired up to support time-zones.
                  */
                 @NotEmpty @NotNull
-                private String minDate = "1900-01-01T00:00:00.000Z";
+                @DefaultValue("1900-01-01T00:00:00.000Z")
+                String minDate,
 
                 /**
                  * Defines the first date available in the date picker.
@@ -3212,370 +3105,394 @@ public static class DatePicker {
                  * Use time zone 'Z', as the date/time picker UI component is not wired up to support time-zones.
                  */
                 @NotEmpty @NotNull
-                private String maxDate = "2100-01-01T00:00:00.000Z";
-
-//XXX probably needed by TempusDominus 5+
-//                public final java.util.Date minDateAsJavaUtilDate() {
-//                    return asJavaUtilDate(getMinDate());
-//                }
-//
-//                public final java.util.Date maxDateAsJavaUtilDate() {
-//                    return asJavaUtilDate(getMaxDate());
-//                }
-//
-//                private static java.util.Date asJavaUtilDate(final String input) {
-//                    return new Date(
-//                            DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(input, OffsetDateTime::from)
-//                            .toEpochSecond());
-//                }
+                @DefaultValue("2100-01-01T00:00:00.000Z")
+                String maxDate,
+
+                //probably needed by TempusDominus 5+
+                //                public final java.util.Date minDateAsJavaUtilDate() {
+                //                    return asJavaUtilDate(getMinDate());
+                //                }
+                //
+                //                public final java.util.Date maxDateAsJavaUtilDate() {
+                //                    return asJavaUtilDate(getMaxDate());
+                //                }
+                //
+                //                 static java.util.Date asJavaUtilDate(final String input) {
+                //                    return new Date(
+                //                            DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(input, OffsetDateTime::from)
+                //                            .toEpochSecond());
+                //                }
 
                 /**
                  * Whether the date picker should automatically be shown when the field gains focus.
                  *
                  * <p>
                  *     The default is to show the picker when the user clicks into the field.
-                 * </p>
+                 *
                  *
                  * <p>
                  *     Corresponds to the <code>allowInputToggle</code> config property of the underlying library,
                  *     (TempusDominus).
-                 * </p>
+                 *
                  */
-                private boolean popupOnFocus = false;
-
+                @DefaultValue("false")
+                boolean popupOnFocus) {
             }
 
-            private final DevelopmentUtilities developmentUtilities = new DevelopmentUtilities();
-            @Data
-            public static class DevelopmentUtilities {
-
+            public record DevelopmentUtilities(
                 /**
                  * Determines whether debug bar and other stuff influenced by
                  * <code>DebugSettings#isDevelopmentUtilitiesEnabled()</code> is enabled or not.
                  *
                  * <p>
                  *     By default, depends on the mode (prototyping = enabled, server = disabled).  This property acts as an override.
-                 * </p>
+                 *
                  */
-                private boolean enable = false;
+                @DefaultValue("false")
+                boolean enable) {
             }
 
-            private final RememberMe rememberMe = new RememberMe();
-            @Data
-            public static class RememberMe {
+            public record RememberMe(
                 /**
-                 * Whether the sign-in page should have a &quot;remember me&quot; link (the default), or if it should
+                 * Whether the sign-in page should have a 'remember me' link (the default), or if it should
                  * be suppressed.
                  *
                  * <p>
-                 *     If &quot;remember me&quot; is available and checked, then the viewer will allow users to login
+                 *     If 'remember me' is available and checked, then the viewer will allow users to login
                  *     based on encrypted credentials stored in a cookie.  An {@link #getEncryptionKey() encryption key}
                  *     can optionally be specified.
-                 * </p>
+                 *
                  */
-                private boolean suppress = false;
+                @DefaultValue("false")
+                boolean suppress,
 
                 /**
-                 * If the &quot;remember me&quot; feature is available, specifies the key to hold the encrypted
+                 * If the 'remember me' feature is available, specifies the key to hold the encrypted
                  * credentials in the cookie.
                  */
-                private String cookieKey = "causewayWicketRememberMe";
+                @DefaultValue("causewayWicketRememberMe")
+                String cookieKey,
                 /**
-                 * If the &quot;remember me&quot; feature is available, optionally specifies an encryption key
+                 * If the 'remember me' feature is available, optionally specifies an encryption key
                  * (a complex string acting as salt to the encryption algorithm) for computing the encrypted
                  * credentials.
                  *
                  * <p>
                  *     If not set, then (in production mode) the Wicket viewer will compute a random key each time it
                  *     is started.  This will mean that any credentials stored between sessions will become invalid.
-                 * </p>
+                 *
                  *
                  * <p>
                  *     Conversely, if set then (in production mode) then the same salt will be used each time the app
                  *     is started, meaning that cached credentials can continue to be used across restarts.
-                 * </p>
+                 *
                  *
                  * <p>
                  *     In prototype mode this setting is effectively ignored, because the same key will always be
                  *     provided (either as set, or a fixed literal otherwise).
-                 * </p>
+                 *
                  */
-                private Optional<String> encryptionKey = Optional.empty();
-            }
+                @DefaultValue
+                Optional<String> encryptionKey) {
 
-            private final Themes themes = new Themes();
-            @Data
-            public static class Themes {
+            }
 
+            public record Themes(
                 /**
                  * A comma separated list of enabled theme names, as defined by https://bootswatch.com.
                  */
-                private List<String> enabled = listOf("Cosmo","Flatly","Darkly","Sandstone","United");
+                @DefaultValue({"Cosmo","Flatly","Darkly","Sandstone","United"})
+                List<String> enabled,
 
                 /**
                  * The initial theme to use.
                  *
                  * <p>
                  *     Expected to be in the list of {@link #getEnabled()} themes.
-                 * </p>
+                 *
                  */
                 @NotEmpty @NotNull
-                private String initial = "Flatly";
+                @DefaultValue("Flatly")
+                String initial,
 
                 /**
                  * Whether the theme chooser widget should be available in the footer.
                  */
-                private boolean showChooser = false;
+                @DefaultValue("false")
+                boolean showChooser) {
             }
 
-            private final Welcome welcome = new Welcome();
-            @Data
-            public static class Welcome {
-
+            public record Welcome(
                 /**
                  * Text to be displayed on the application’s home page, used as a fallback if
                  * welcome.file is not specified. If a @HomePage action exists, then that will take
                  * precedence.
                  */
-                private String text;
+                String text) {
             }
 
-            private final MessagePopups messagePopups = new MessagePopups();
-            @Data
-            public static class MessagePopups {
-
+            public record MessagePopups(
                 /**
                  * How long the info popup should display before disappearing.
                  *
                  * <p>
                  *     A value of 0 means do not disappear automatically.
-                 * </p>
+                 *
                  */
-                Duration infoDelay = Duration.ofMillis(3500);
+                @DurationUnit(ChronoUnit.MILLIS)
+                @DefaultValue("3500")
+                Duration infoDelay,
 
                 /**
                  * How long the warning popup should display before disappearing.
                  *
-                 * <p>
-                 *     A value of 0 (the default) means do not disappear automatically.
-                 * </p>
+                 * <p>A value of 0 (the default) means do not disappear automatically.
                  */
-                Duration warningDelay = Duration.ofMillis(0);
+                @DurationUnit(ChronoUnit.MILLIS)
+                @DefaultValue("0")
+                Duration warningDelay,
 
                 /**
                  * How long the error popup should display before disappearing.
                  *
-                 * <p>
-                 *     A value of 0 (the default) means do not disappear automatically.
-                 * </p>
+                 * <p>A value of 0 (the default) means do not disappear automatically.
                  */
-                Duration errorDelay = Duration.ofMillis(0);
+                @DurationUnit(ChronoUnit.MILLIS)
+                @DefaultValue("0")
+                Duration errorDelay,
 
                 /**
                  * How far in from the edge the popup should display
                  */
-                int offset = 100;
-
-                private final Placement placement = new Placement();
-                @Data
-                public static class Placement {
+                @DefaultValue("100")
+                int offset,
 
-                    public static enum Vertical {
-                        TOP, BOTTOM
-                    }
-
-                    public static enum Horizontal {
-                        LEFT, RIGHT
-                    }
+                @DefaultValue
+                Placement placement) {
 
+                public record Placement(
                     /**
                      * Whether to display popups at the top or the bottom of the page.
                      *
                      * <p>
                      * The default is to show them at the top.
-                     * </p>
+                     *
                      */
-                    Vertical vertical = Vertical.TOP;
-
+                    @DefaultValue("TOP")
+                    Vertical vertical,
                     /**
                      * Whether to display popups aligned ot the left or right of the page.
                      *
                      * <p>
                      * The default is to show them aligned to the right
-                     * </p>
+                     *
                      */
-                    Horizontal horizontal = Horizontal.RIGHT;
+                    @DefaultValue("RIGHT")
+                    Horizontal horizontal) {
+
+                    public enum Vertical {
+                        TOP, BOTTOM
+                    }
+
+                    public enum Horizontal {
+                        LEFT, RIGHT
+                    }
+
                 }
             }
+
+            // TODO remove
+            @Deprecated
+            public static Wicket defaults() {
+                throw _Exceptions.unsupportedOperation();
+            }
+
         }
     }
 
-    private final ValueTypes valueTypes = new ValueTypes();
-    @Data
-    public static class ValueTypes {
+    public record ValueTypes(
+        @DefaultValue @NonNull
+        Temporal temporal,
+        @DefaultValue
+        BigDecimal bigDecimal,
+        @DefaultValue
+        Kroki kroki) {
+
+        public record Temporal(
+            @DefaultValue
+            TemporalEditingPattern editing,
+            @DefaultValue
+            TemporalDisplayPattern display
+            ) {
+
+            //TODO can this be removed ?
+            @Deprecated
+            public static Temporal defaults() {
+                return new Temporal(new TemporalEditingPattern(), new TemporalDisplayPattern(null, null));
+            }
 
-        private final Temporal temporal = new Temporal();
-        @Data
-        public static class Temporal {
-            private final TemporalEditingPattern editing = new TemporalEditingPattern();
-            private final TemporalDisplayPattern display = new TemporalDisplayPattern();
         }
 
-        private final BigDecimal bigDecimal = new BigDecimal();
-        @Data
-        public static class BigDecimal {
-
+        public record BigDecimal(
             /**
              * Indicates how to derive the min fractional facet (the minimum number of digits after the decimal point).
              *
-             * <p>
-             * If this flag is set, then the {@link Digits#fraction()} annotation or ORM equivalent (the JDO
+             * <p>If this flag is set, then the {@link Digits#fraction()} annotation or ORM equivalent (the JDO
              * <code>@Column#scale</code> or the JPA {@link Column#scale()}) should be used for the
              * <code>MinFractionalFacet</code> as well as the <code>MaxFractionalFacet</code>.
-             * </p>
              *
-             * <p>
-             * What this means in practice is that a numeric values will be rendered to the same number of fractional
+             * <p>What this means in practice is that a numeric values will be rendered to the same number of fractional
              * digits, irrespective of whether they are whole numbers or fractional.  For example, with a scale of 2,
-             * then &quot;123.4532&quot; will be rendered as &quot;123.45&quot;, while &quot;123&quot; will be rendered
-             * as &quot;123.00&quot;.
-             * </p>
+             * then '123.4532' will be rendered as '123.45', while '123' will be rendered
+             * as '123.00'.
              *
-             * <p>
-             * If this flag is NOT set, or if it is set but there is no annotation, then the {@link #minScale} config
+             * <p>If this flag is NOT set, or if it is set but there is no annotation, then the {@link Display#minScale} config
              * property is used as a fallback.
-             * </p>
              *
-             * <p>
-             * If there is no fallback, then it means that a big decimal such as &quot;123.00&quot; will be presented as
-             * just &quot;123&quot; (that is, the shortest possible textual representation).
-             * </p>
+             * <p>If there is no fallback, then it means that a big decimal such as '123.00' will be presented as
+             * just '123' (that is, the shortest possible textual representation).
              */
-            private boolean useScaleForMinFractionalFacet = true;
+            @DefaultValue("true")
+            boolean useScaleForMinFractionalFacet,
+
+            @DefaultValue
+            Editing editing,
+            @DefaultValue
+            Display display
+            ) {
 
-            private final Editing editing = new Editing();
-            @Data
-            public static class Editing {
+            public record Editing(
                 /**
                  * A common use of {@link java.math.BigDecimal} is as a money value.  In some locales (eg English), the
-                 * &quot;,&quot; (comma) is the grouping (thousands) separator wihle the &quot;.&quot; (period) acts as a
+                 * ',' (comma) is the grouping (thousands) separator while the '.' (period) acts as a
                  * decimal point, but in others (eg France, Italy) it is the other way around.
                  *
-                 * <p>
-                 *     Surprisingly perhaps, a string such as "123,99", when parsed ((by {@link java.text.DecimalFormat})
-                 *     in an English locale, is not rejected but instead is evaluated as the value 12399L.  That's almost
-                 *     certainly not what the end-user would have expected, and results in a money value 100x too large.
-                 * </p>
+                 * <p>Surprisingly perhaps, a string such as "123,99", when parsed ((by {@link java.text.DecimalFormat})
+                 * in an English locale, is not rejected but instead is evaluated as the value 12399L.  That's almost
+                 * certainly not what the end-user would have expected, and results in a money value 100x too large.
                  *
-                 * <p>
-                 *     The purpose of this configuration property is to remove the confusion by simply disallowing the
-                 *     thousands separator from being part of the input string.
-                 * </p>
+                 * <p>The purpose of this configuration property is to remove the confusion by simply disallowing the
+                 * thousands separator from being part of the input string.
                  *
-                 * <p>
-                 *     For maximum safety, allowing the grouping separator is disallowed, but the alternate (original)
-                 *     behaviour can be reinstated by setting this config property back to <code>true</code>.
-                 * </p>
+                 * <p>For maximum safety, allowing the grouping separator is disallowed, but the alternate (original)
+                 * behaviour can be reinstated by setting this config property back to <code>true</code>.
                  *
-                 * <p>
-                 *     The same configuration property is also used for rendering the value.
-                 * </p>
+                 * <p>The same configuration property is also used for rendering the value.
                  *
                  * @see Display#isUseGroupingSeparator()
                  */
-                private boolean useGroupingSeparator = false;
+                @DefaultValue("false")
+                boolean useGroupingSeparator,
                 /**
                  * When a BigDecimal is presented for editing, whether it should enforce the scale, possibly meaning
                  * trailing '0's to pad).  This is probably appropriate for BigDecimals that represent a money amount.
                  */
-                private boolean preserveScale = false;
+                @DefaultValue("false")
+                boolean preserveScale) {
             }
 
-            private final Display display = new Display();
-            @Data
-            public static class Display {
-
+            public record Display(
                 /**
                  * The minimum scale to use for all {@link java.math.BigDecimal}s.
                  *
-                 * <p>
-                 * Is only used if the minimum scale has not been specified explicitly by some other means, typically
+                 * <p> Is only used if the minimum scale has not been specified explicitly by some other means, typically
                  * either {@link Digits#fraction()} or an ORM semantic such as the (JPA) {@link Column#scale()}.
                  */
-                private Integer minScale = null;
+                Integer minScale,
 
                 /**
-                 * Whether to use a grouping (thousands) separator (eg the &quot;,&quot; (comma) in the English locale)
+                 * Whether to use a grouping (thousands) separator (eg the ',' (comma) in the English locale)
                  * when rendering a big decimal.
                  *
-                 * @see Editing#isUseGroupingSeparator()
+                 * @see org.apache.causeway.core.config.CausewayConfiguration.ValueTypes.BigDecimal.Editing#useGroupingSeparator()
                  */
-                private boolean useGroupingSeparator = true;
+                @DefaultValue("true")
+                boolean useGroupingSeparator) {
             }
 
         }
 
-        private final Kroki kroki = new Kroki();
-        @Data
-        public static class Kroki {
+        public record Kroki(
             /**
              * If set, adds plantuml support to the AsciiDoc value type.
-             * <p>
-             * Eg. via docker instance like {@literal yuzutech/kroki}.
+             *
+             * <p>Eg. via docker instance like {@literal yuzutech/kroki}.
              */
-            private URL backendUrl = null;
+            URL backendUrl,
 
             /**
              * Max time for requests to the {@link #getBackendUrl()},
              * when waiting for a response. (default: 5 seconds)
              */
-            private Duration requestTimeout = Duration.ofMillis(5000);
+            @DurationUnit(ChronoUnit.MILLIS)
+            @DefaultValue("5000")
+            Duration requestTimeout) {
+
         }
 
     }
 
-    private final Testing testing = new Testing();
-    @Data
-    public static class Testing {
-        private final Fixtures fixtures = new Fixtures();
-        @Data
-        public static class Fixtures {
+    public record Testing(
+        @DefaultValue
+        Fixtures fixtures) {
+
+        public record Fixtures(
             /**
              * Indicates the fixture script class to run initially.
              *
-             * <p>
-             *     Intended for use when prototyping against an in-memory database (but will run in production mode
-             *     as well if required).
-             * </p>
+             * <p>Intended for use when prototyping against an in-memory database (but will run in production mode
+             * as well if required).
              */
             @AssignableFrom("org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript")
-            private Class<?> initialScript = null;
+            Class<?> initialScript,
+
+            @DefaultValue
+            FixtureScriptsSpecification fixtureScriptsSpecification) {
 
-            private final FixtureScriptsSpecification fixtureScriptsSpecification = new FixtureScriptsSpecification();
-            @Data
-            public static class FixtureScriptsSpecification {
+            public record FixtureScriptsSpecification(
                 /**
                  * Specifies the base package from which to search for fixture scripts.
                  *
-                 * <p>
-                 *     Either this or {@link #getPackagePrefix() packagePrefix} must be specified.  This property is
-                 *     used by preference.
-                 * </p>
+                 * <p>Either this or {@link #getPackagePrefix() packagePrefix} must be specified.  This property is
+                 * used by preference.
                  *
                  * @see #getPackagePrefix()
                  */
-                private Class<?> contextClass = null;
+                Class<?> contextClass,
                 /**
                  * Specifies the base package from which to search for fixture scripts.
                  *
-                 * <p>
-                 *     Either this or {@link #getContextClass()} must be specified; {@link #getContextClass()} is
-                 *     used by preference.
-                 * </p>
+                 * <p>Either this or {@link #getContextClass()} must be specified; {@link #getContextClass()} is
+                 * used by preference.
                  *
                  * @see #getContextClass()
                  */
-                private String packagePrefix = null;
+                String packagePrefix,
+                /**
+                 * Indicates whether, if a fixture script (or more precisely any other fixture scripts of the same
+                 * class) is encountered more than once in a graph of dependencies, it should be executed again or
+                 * skipped.
+                 *
+                 * <p>The default is to fixture scripts are executed only once per class.
+                 *
+                 * <p>Note that this policy can be overridden on a fixture-by-fixture basis if the fixture implements
+                 * <code>FixtureScriptWithExecutionStrategy</code>.
+                 */
+                @DefaultValue("EXECUTE_ONCE_BY_CLASS")
+                MultipleExecutionStrategy multipleExecutionStrategy,
+
+                /**
+                 * Indicates whether objects that are returned as a fixture result should be automatically persisted
+                 * if required (the default) or not.
+                 */
+                @DefaultValue("PERSIST")
+                NonPersistedObjectsStrategy nonPersistedObjectsStrategy,
+
+                @AssignableFrom("org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript")
+                Class<?> recreate,
+
+                @AssignableFrom("org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript")
+                Class<?> runScriptDefault) {
 
                 /**
                  * How to handle objects that are to be added into a <code>FixtureResult</code> but which are not yet
@@ -3594,117 +3511,106 @@ public enum NonPersistedObjectsStrategy {
                  *     (currently) any way to mix-and-match fixture scripts that are written with differing semantics
                  *     in mind.  Ideally it should be the responsibility of the fixture script itself to determine
                  *     whether it should be run.
-                 * </p>
+                 *
                  */
                 public enum MultipleExecutionStrategy {
                     /**
                      * Any given fixture script (or more precisely, any fixture script instance for a particular fixture script
                      * class) can only be run once.
                      *
-                     * <p>
-                     *     This strategy represents the original design of fixture scripts service.  Specifically, it allows an
-                     *     arbitrary graph of fixture scripts (eg A -> B -> C, A -> B -> D, A -> C -> D) to be created each
-                     *     specifying its dependencies, and without having to worry or co-ordinate whether those prerequisite
-                     *     fixture scripts have already been run.
-                     * </p>
-                     * <p>
-                     *     The most obvious example is a global teardown script; every fixture script can require this to be
-                     *     called, but it will only be run once.
-                     * </p>
-                     * <p>
-                     *     Note that this strategy treats fixture scripts as combining both the 'how' (which business action(s) to
-                     *     call) and the also the 'what' (what the arguments are to those actions).
-                     * </p>
+                     * <p>This strategy represents the original design of fixture scripts service.  Specifically, it allows an
+                     * arbitrary graph of fixture scripts (eg A -> B -> C, A -> B -> D, A -> C -> D) to be created each
+                     * specifying its dependencies, and without having to worry or co-ordinate whether those prerequisite
+                     * fixture scripts have already been run.
+                     *
+                     * <p>The most obvious example is a global teardown script; every fixture script can require this to be
+                     * called, but it will only be run once.
+                     *
+                     * <p>Note that this strategy treats fixture scripts as combining both the 'how' (which business action(s) to
+                     * call) and the also the 'what' (what the arguments are to those actions).
+                     *
                      */
                     EXECUTE_ONCE_BY_CLASS,
                     /**
                      * Any given fixture script can only be run once, where the check to determine if a fixture script has already
                      * been run is performed using value semantics.
                      *
-                     * <p>
-                     *     This strategy is a half-way house between the {@link #EXECUTE_ONCE_BY_VALUE} and {@link #EXECUTE}
-                     *     strategies, where we want to prevent a fixture from running more than once, where by "fixture" we mean
-                     *     the 'what' - the data to be loaded up; the 'how' is unimportant.
-                     * </p>
+                     * <p>This strategy is a half-way house between the {@link #EXECUTE_ONCE_BY_VALUE} and {@link #EXECUTE}
+                     * strategies, where we want to prevent a fixture from running more than once, where by "fixture" we mean
+                     * the 'what' - the data to be loaded up; the 'how' is unimportant.
                      *
-                     * <p>
-                     *     This strategy was introduced in order to better support the <tt>ExcelFixture</tt> fixture script
-                     *     (provided by the (non-ASF) Causeway Addons'
-                     *     <a href="https://github.com/causewayaddons/causeway-module-excel">Excel module</a>.  The <tt>ExcelFixture</tt>
-                     *     takes an Excel spreadsheet as the 'what' and loads up each row.  So the 'how' is re-usable (therefore
-                     *     the {@link #EXECUTE_ONCE_BY_CLASS} doesn't apply) on the other hand we don't want the 'what' to be
-                     *     loaded more than once (so the {@link #EXECUTE} strategy doesn't apply either).  The solution is for
-                     *     <tt>ExcelFixture</tt> to have value semantics (a digest of the spreadsheet argument).
-                     * </p>
+                     * <p>This strategy was introduced in order to better support the <tt>ExcelFixture</tt> fixture script
+                     * (provided by the (non-ASF) Causeway Addons'
+                     * <a href="https://github.com/causewayaddons/causeway-module-excel">Excel module</a>.  The <tt>ExcelFixture</tt>
+                     * takes an Excel spreadsheet as the 'what' and loads up each row.  So the 'how' is re-usable (therefore
+                     * the {@link #EXECUTE_ONCE_BY_CLASS} doesn't apply) on the other hand we don't want the 'what' to be
+                     * loaded more than once (so the {@link #EXECUTE} strategy doesn't apply either).  The solution is for
+                     * <tt>ExcelFixture</tt> to have value semantics (a digest of the spreadsheet argument).
                      */
                     EXECUTE_ONCE_BY_VALUE,
                     /**
                      * Allow fixture scripts to run as requested.
                      *
-                     * <p>
-                     *     This strategy is conceptually the simplest; all fixtures are run as requested.  However, it is then
-                     *     the responsibility of the programmer to ensure that fixtures do not interfere with each other.  For
-                     *     example, if fixture A calls fixture B which calls teardown, and fixture A also calls fixture C that
-                     *     itself calls teardown, then fixture B's setup will get removed.
-                     * </p>
-                     * <p>
-                     *     The workaround to the teardown issue is of course to call the teardown fixture only once in the test
-                     *     itself; however even then this strategy cannot cope with arbitrary graphs of fixtures.  The solution
-                     *     is for the fixture list to be flat, one level high.
-                     * </p>
+                     * <p>This strategy is conceptually the simplest; all fixtures are run as requested.  However, it is then
+                     * the responsibility of the programmer to ensure that fixtures do not interfere with each other.  For
+                     * example, if fixture A calls fixture B which calls teardown, and fixture A also calls fixture C that
+                     * itself calls teardown, then fixture B's setup will get removed.
+                     *
+                     * <p>The workaround to the teardown issue is of course to call the teardown fixture only once in the test
+                     * itself; however even then this strategy cannot cope with arbitrary graphs of fixtures.  The solution
+                     * is for the fixture list to be flat, one level high.
                      */
                     EXECUTE;
                 }
-
-                /**
-                 * Indicates whether, if a fixture script (or more precisely any other fixture scripts of the same
-                 * class) is encountered more than once in a graph of dependencies, it should be executed again or
-                 * skipped.
-                 *
-                 * <p>
-                 *     The default is to fixture scripts are executed only once per class.
-                 * </p>
-                 *
-                 * <p>
-                 * Note that this policy can be overridden on a fixture-by-fixture basis if the fixture implements
-                 * <code>FixtureScriptWithExecutionStrategy</code>.
-                 * </p>
-                 */
-                private MultipleExecutionStrategy multipleExecutionStrategy = MultipleExecutionStrategy.EXECUTE_ONCE_BY_CLASS;
-
-                /**
-                 * Indicates whether objects that are returned as a fixture result should be automatically persisted
-                 * if required (the default) or not.
-                 */
-                private NonPersistedObjectsStrategy nonPersistedObjectsStrategy = NonPersistedObjectsStrategy.PERSIST;
-
-                @AssignableFrom("org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript")
-                private Class<?> recreate = null;
-
-                @AssignableFrom("org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript")
-                private Class<?> runScriptDefault = null;
-
             }
-
         }
     }
 
-    private final Extensions extensions = new Extensions();
-    @Valid
-    @Data
-    public static class Extensions {
 
-        private final AuditTrail auditTrail = new AuditTrail();
-        @Data
-        public static class AuditTrail {
+    @Valid
+    public record Extensions(
+        @DefaultValue
+        AuditTrail auditTrail,
+        @DefaultValue
+        CommandLog commandLog,
+        @DefaultValue
+        CommandReplay commandReplay,
+        @DefaultValue
+        Cors cors,
+        @DefaultValue
+        ExecutionLog executionLog,
+        @DefaultValue
+        ExecutionOutbox executionOutbox,
+        @DefaultValue
+        LayoutLoaders layoutLoaders,
+        @DefaultValue
+        Secman secman,
+        @DefaultValue
+        SessionLog sessionLog,
+        @DefaultValue
+        Titlecache titlecache
+        ) {
+
+        public record AuditTrail(
+            /**
+             * Whether the {@link EntityPropertyChangeSubscriber} implementation provided by this extension (which
+             * persists property changes to the audit trail) is enabled or not.
+             *
+             * <p>One reason to use this option is if you wish to provide your own implementation that wraps
+             * or delegates to the default implementation of {@link EntityPropertyChangeSubscriber} that is
+             * provided by the <i>audittrail</i> extension.  Because entity property changes are published to
+             * <i>all</i> subscribers on the class path, you can disable the default implementation from
+             * doing anything using this setting.
+             */
+            @DefaultValue("ENABLED")
+            PersistPolicy persist
+            ) {
 
             /**
-             * As per {@link AuditTrail#getPersist()}.
+             * As per {@link AuditTrail#persist()}.
              *
-             * <p>
-             *     Implementation note: we use an enum here (rather than a simple boolean) to allow for future
-             *     enhancements.
-             * </p>
+             * <p>Implementation note: we use an enum here (rather than a simple boolean) to allow for future
+             * enhancements.
              */
             public enum PersistPolicy {
                 /**
@@ -3719,33 +3625,29 @@ public enum PersistPolicy {
                 public boolean isEnabled() { return this == ENABLED; }
                 public boolean isDisabled() { return this == DISABLED; }
             }
+        }
 
+        public record CommandLog(
             /**
-             * Whether the {@link EntityPropertyChangeSubscriber} implementation provided by this extension (which
-             * persists property changes to the audit trail) is enabled or not.
+             * Whether the {@link CommandSubscriber} implementation
+             * provided by this extension (which persists commands to the command log) is enabled or not.
              *
-             * <p>
-             *     One reason to use this option is if you wish to provide your own implementation that wraps
-             *     or delegates to the default implementation of {@link EntityPropertyChangeSubscriber} that is
-             *     provided by the <i>audittrail</i> extension.  Because entity property changes are published to
-             *     <i>all</i> subscribers on the class path, you can disable the default implementation from
-             *     doing anything using this setting.
-             * </p>
+             * <p>One reason to use this option is if you wish to provide your own implementation that wraps
+             * or delegates to the default implementation of {@link CommandSubscriber} that is
+             * provided by the <i>commandlog</i> extension.  Because commands are published to
+             * <i>all</i> subscribers on the class path, you can disable the default implementation from
+             * doing anything using this setting.
              */
-            private PersistPolicy persist = PersistPolicy.ENABLED;
-        }
-
-        private final CommandLog commandLog = new CommandLog();
-        @Data
-        public static class CommandLog {
+            @DefaultValue("ENABLED")
+            PersistPolicy persist,
+            @DefaultValue
+            RunBackgroundCommands runBackgroundCommands) {
 
             /**
-             * As per {@link CommandLog#getPersist()}.
+             * As per {@link CommandLog#persist()}.
              *
-             * <p>
-             *     Implementation note: we use an enum here (rather than a simple boolean) to allow for future
-             *     enhancements.
-             * </p>
+             * <p>Implementation note: we use an enum here (rather than a simple boolean) to allow for future
+             * enhancements.
              */
             public enum PersistPolicy {
                 /**
@@ -3761,23 +3663,21 @@ public enum PersistPolicy {
                 public boolean isDisabled() { return this == DISABLED; }
             }
 
-            /**
-             * Whether the {@link CommandSubscriber} implementation
-             * provided by this extension (which persists commands to the command log) is enabled or not.
-             *
-             * <p>
-             *     One reason to use this option is if you wish to provide your own implementation that wraps
-             *     or delegates to the default implementation of {@link CommandSubscriber} that is
-             *     provided by the <i>commandlog</i> extension.  Because commands are published to
-             *     <i>all</i> subscribers on the class path, you can disable the default implementation from
-             *     doing anything using this setting.
-             * </p>
-             */
-            private PersistPolicy persist = PersistPolicy.ENABLED;
-
-            private final RunBackgroundCommands runBackgroundCommands = new RunBackgroundCommands();
-            @Data
-            public static class RunBackgroundCommands {
+            public record RunBackgroundCommands(
+                /**
+                 * Limits the number of pending commands that the <code>RunBackgroundCommandsJob</code>
+                 * will execute.  After these have been executed, any <code>RunBackgroundCommandsJobListener</code>s are called.
+                 *
+                 * <p>By default, quartz runs this command every 10 seconds, so the size should be proportion to that.
+                 */
+                @DefaultValue("25")
+                int batchSize,
+                /**
+                 * If there is an exception executing one of the commands, what should be done?
+                 */
+                @DefaultValue("STOP_THE_LINE")
+                OnFailurePolicy onFailurePolicy
+                ) {
 
                 /**
                  * If the attempt to execute the command in the background results in failure,
@@ -3788,225 +3688,195 @@ public enum OnFailurePolicy {
                      * If a failure has occurred, then capture the exception on the <code>CommandLogEntry</code> and
                      * then move onto the next command
                      *
-                     * <p>
-                     *     This policy allows further processing to continue, but runs the risk that the failed command
-                     *     is not noticed by anyone, producing perhaps erroneous results later if it is run
-                     *     out-of-order.
-                     * </p>
+                     * <p>This policy allows further processing to continue, but runs the risk that the failed command
+                     * is not noticed by anyone, producing perhaps erroneous results later if it is run
+                     * out-of-order.
                      */
                     CONTINUE_WITH_NEXT,
                     /**
                      * If a failure has occurred, then leave the <code>CommandLogEntry</code> unchanged, so that it
                      * is picked up again to be retried.
                      *
-                     * <p>
-                     *     This policy is in effect a 'stop the line' or 'fail fast' approach.  Because the background
-                     *     command only runs in serial, an exception will result in no other commands being executed.
-                     * </p>
+                     * <p>This policy is in effect a 'stop the line' or 'fail fast' approach.  Because the background
+                     * command only runs in serial, an exception will result in no other commands being executed.
                      */
                     STOP_THE_LINE;
                 }
-
-                /**
-                 * Limits the number of pending commands that the <code>RunBackgroundCommandsJob</code>
-                 * will execute.  After these have been executed, any <code>RunBackgroundCommandsJobListener</code>s are called.
-                 *
-                 * <p>
-                 *     By default, quartz runs this command every 10 seconds, so the size should be proportion to that.
-                 * </p>
-                 */
-                private int batchSize = 25;
-
-                /**
-                 * If there is an exception executing one of the commands, what should be done?
-                 */
-                private OnFailurePolicy onFailurePolicy = OnFailurePolicy.STOP_THE_LINE;
             }
         }
 
-        private final CommandReplay commandReplay = new CommandReplay();
-        @Data
-        public static class CommandReplay {
-
-            private final PrimaryAccess primaryAccess = new PrimaryAccess();
-            @Data
-            public static class PrimaryAccess {
+        public record CommandReplay(
+            @DefaultValue
+            PrimaryAccess primaryAccess,
+            @DefaultValue
+            SecondaryAccess secondaryAccess,
+            @DefaultValue("10")
+            Integer batchSize,
+            @DefaultValue
+            QuartzSession quartzSession,
+            @DefaultValue
+            QuartzReplicateAndReplayJob quartzReplicateAndReplayJob,
+            @DefaultValue
+            Analyser analyser) {
+
+            public record PrimaryAccess(
                 @jakarta.validation.constraints.Pattern(regexp="^http[s]?://[^:]+?(:\\d+)?.*([^/]+/)$")
-                private Optional<String> baseUrlRestful = Optional.empty();
-                private Optional<String> user = Optional.empty();
-                private Optional<String> password = Optional.empty();
+                Optional<String> baseUrlRestful,
+                Optional<String> user,
+                Optional<String> password,
                 @jakarta.validation.constraints.Pattern(regexp="^http[s]?://[^:]+?(:\\d+)?.*([^/]+/)$")
-                private Optional<String> baseUrlWicket = Optional.empty();
+                Optional<String> baseUrlWicket) {
             }
 
-            private final SecondaryAccess secondaryAccess = new SecondaryAccess();
-            @Data
-            public static class SecondaryAccess {
+            public record SecondaryAccess(
                 @jakarta.validation.constraints.Pattern(regexp="^http[s]?://[^:]+?(:\\d+)?.*([^/]+/)$")
-                private Optional<String> baseUrlWicket = Optional.empty();
+                Optional<String> baseUrlWicket) {
             }
 
-            private Integer batchSize = 10;
-
-            private final QuartzSession quartzSession = new QuartzSession();
-            @Data
-            public static class QuartzSession {
+            public record QuartzSession(
                 /**
                  * The user that runs the replay session secondary.
                  */
-                private String user = "causewayModuleExtCommandReplaySecondaryUser";
-                private List<String> roles = listOf("causewayModuleExtCommandReplaySecondaryRole");
+                @DefaultValue("causewayModuleExtCommandReplaySecondaryUser")
+                String user,
+                @DefaultValue({"causewayModuleExtCommandReplaySecondaryRole"})
+                List<String> roles) {
             }
 
-            private final QuartzReplicateAndReplayJob quartzReplicateAndReplayJob = new QuartzReplicateAndReplayJob();
-            @Data
-            public static class QuartzReplicateAndReplayJob {
+            public record QuartzReplicateAndReplayJob(
                 /**
                  * Number of milliseconds before starting the job.
                  */
-                private long startDelay = 15000;
+                @DefaultValue("15000")
+                long startDelay,
                 /**
                  * Number of milliseconds before running again.
                  */
-                private long repeatInterval = 10000;
+                @DefaultValue("10000")
+                long repeatInterval) {
             }
 
-            private final Analyser analyser = new Analyser();
-            @Data
-            public static class Analyser {
-                private final Result result = new Result();
-                @Data
-                public static class Result {
-                    private boolean enabled = true;
-                }
-                private final Exception exception = new Exception();
-                @Data
-                public static class Exception {
-                    private boolean enabled = true;
+            public record Analyser(
+                @DefaultValue
+                Result result,
+                @DefaultValue
+                Exception exception) {
+
+                public record Result(
+                    @DefaultValue("true")
+                    boolean enabled) {
                 }
 
+                public record Exception(
+                    @DefaultValue("true")
+                    boolean enabled) {
+                }
             }
         }
 
-        private final Cors cors = new Cors();
-        @Data
-        public static class Cors {
-
+        public record Cors(
             /**
              * Whether the resource supports user credentials.
              *
-             * <p>
-             *     This flag is exposed as part of 'Access-Control-Allow-Credentials' header in a pre-flight response.
-             *     It helps browser determine whether or not an actual request can be made using credentials.
-             * </p>
+             * <p>This flag is exposed as part of 'Access-Control-Allow-Credentials' header in a pre-flight response.
+             * It helps browser determine whether or not an actual request can be made using credentials.
              *
-             * <p>
-             *     By default this is not set (i.e. user credentials are not supported).
-             * </p>
+             * <p>By default this is not set (i.e. user credentials are not supported).
              *
-             * <p>
-             *     For more information, check the usage of the <code>cors.support.credentials</code> init parameter
-             *     for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
-             * </p>
+             * <p>For more information, check the usage of the <code>cors.support.credentials</code> init parameter
+             * for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
              */
-            private boolean allowCredentials = false;
+            @DefaultValue("false")
+            boolean allowCredentials,
 
             /**
              * Which origins are allowed to make CORS requests.
              *
-             * <p>
-             *     The default is the wildcard (&quot;*&quot;), meaning any origin is allowed to access the resource,
-             *     but this can be made more restrictive if necessary using a whitelist of comma separated origins
-             *     eg:
-             * </p>
-             * <p>
-             *     <code>http://www.w3.org, https://www.apache.org</code>
-             * </p>
+             * <p>The default is the wildcard ('*'), meaning any origin is allowed to access the resource,
+             * but this can be made more restrictive if necessary using a whitelist of comma separated origins
+             * eg:
+             *
+             * <p><code>http://www.w3.org, https://www.apache.org</code>
+             *
+             * <p>For more information, check the usage of the <code>cors.allowed.origins</code> init parameter
+             * for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
              *
-             * <p>
-             *     For more information, check the usage of the <code>cors.allowed.origins</code> init parameter
-             *     for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
-             * </p>
              */
-            private List<String> allowedOrigins = listOf("*");
+            @DefaultValue({"*"})
+            List<String> allowedOrigins,
 
             /**
              * Which HTTP headers can be allowed in a CORS request.
              *
-             * <p>
-             *     These header will also be returned as part of 'Access-Control-Allow-Headers' header in a
-             *     pre-flight response.
-             * </p>
+             * <p>These header will also be returned as part of 'Access-Control-Allow-Headers' header in a
+             * pre-flight response.
              *
-             * <p>
-             *     For more information, check the usage of the <code>cors.allowed.headers</code> init parameter
-             *     for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
-             * </p>
+             * <p>For more information, check the usage of the <code>cors.allowed.headers</code> init parameter
+             * for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
              */
-            private List<String> allowedHeaders = listOf(
-                    "Content-Type",
-                    "Accept",
-                    "Origin",
-                    "Access-Control-Request-Method",
-                    "Access-Control-Request-Headers",
-                    "Authorization",
-                    "Cache-Control",
-                    "If-Modified-Since",
-                    "Pragma");
+            @DefaultValue({
+                "Content-Type",
+                "Accept",
+                "Origin",
+                "Access-Control-Request-Method",
+                "Access-Control-Request-Headers",
+                "Authorization",
+                "Cache-Control",
+                "If-Modified-Since",
+                "Pragma"
+            })
+            List<String> allowedHeaders,
 
             /**
              * Which HTTP methods are permitted in a CORS request.
              *
-             * <p>
-             *     A comma separated list of HTTP methods that can be used to access the resource, using cross-origin
-             *     requests. These are the methods which will also be included as part of 'Access-Control-Allow-Methods'
-             *     header in a pre-flight response.
-             * </p>
+             * <p>A comma separated list of HTTP methods that can be used to access the resource, using cross-origin
+             * requests. These are the methods which will also be included as part of 'Access-Control-Allow-Methods'
+             * header in a pre-flight response.
              *
-             * <p>
-             *     Default is <code>GET</code>, <code>POST</code>, <code>HEAD</code>, <code>OPTIONS</code>.
-             * </p>
+             * <p>Default is <code>GET</code>, <code>POST</code>, <code>HEAD</code>, <code>OPTIONS</code>.
              *
-             * <p>
-             *     For more information, check the usage of the <code>cors.allowed.methods</code> init parameter
-             *     for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
-             * </p>
+             * <p>For more information, check the usage of the <code>cors.allowed.methods</code> init parameter
+             * for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
              */
-            private List<String> allowedMethods = listOf("GET","PUT","DELETE","POST","OPTIONS");
+            @DefaultValue({"GET","PUT","DELETE","POST","OPTIONS"})
+            List<String> allowedMethods,
 
             /**
              * Which HTTP headers are exposed in a CORS request.
              *
-             * <p>
-             *     A comma separated list of headers other than the simple response headers that browsers are allowed
-             *     to access. These are the headers which will also be included as part of
-             *     'Access-Control-Expose-Headers' header in the pre-flight response.
-             * </p>
+             * <p>A comma separated list of headers other than the simple response headers that browsers are allowed
+             * to access. These are the headers which will also be included as part of
+             * 'Access-Control-Expose-Headers' header in the pre-flight response.
              *
-             * <p>
-             *     Default is none.
-             * </p>
+             * <p>Default is none.
              *
-             * <p>
-             *     For more information, check the usage of the <code>cors.exposed.headers</code> init parameter
-             *     for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
-             * </p>
+             * <p>For more information, check the usage of the <code>cors.exposed.headers</code> init parameter
+             * for <a href="https://github.com/eBay/cors-filter">EBay CORSFilter</a>.
              */
-            private List<String> exposedHeaders = listOf("Authorization");
-
+            @DefaultValue({"Authorization"})
+            List<String> exposedHeaders) {
         }
 
-        private final ExecutionLog executionLog = new ExecutionLog();
-        @Data
-        public static class ExecutionLog {
+        public record ExecutionLog(
+            /**
+             * Whether the {@link ExecutionSubscriber} implementation
+             * provided by this extension (which persists executions to the execution log) is enabled or not.
+             *
+             * <p>One reason to use this option is if you wish to provide your own implementation that wraps
+             * or delegates to the default implementation of {@link ExecutionSubscriber} that is
+             * provided by the <i>executionLog</i> extension.  Because executions are published to
+             * <i>all</i> subscribers on the class path, you can disable the default implementation from
+             * doing anything using this setting.
+             */
+            @DefaultValue("ENABLED")
+            PersistPolicy persist) {
 
             /**
-             * As per {@link ExecutionLog#getPersist()}.
+             * As per {@link ExecutionLog#persist()}.
              *
-             * <p>
-             *     Implementation note: we use an enum here (rather than a simple boolean) to allow for future
-             *     enhancements.
-             * </p>
+             * <p>Implementation note: we use an enum here (rather than a simple boolean) to allow for future
+             * enhancements.
              */
             public enum PersistPolicy {
                 /**
@@ -4022,45 +3892,42 @@ public enum PersistPolicy {
                 public boolean isDisabled() { return this == DISABLED; }
             }
 
-            /**
-             * Whether the {@link ExecutionSubscriber} implementation
-             * provided by this extension (which persists executions to the execution log) is enabled or not.
-             *
-             * <p>
-             *     One reason to use this option is if you wish to provide your own implementation that wraps
-             *     or delegates to the default implementation of {@link ExecutionSubscriber} that is
-             *     provided by the <i>executionLog</i> extension.  Because executions are published to
-             *     <i>all</i> subscribers on the class path, you can disable the default implementation from
-             *     doing anything using this setting.
-             * </p>
-             */
-            private PersistPolicy persist = PersistPolicy.ENABLED;
+
         }
 
-        private final ExecutionOutbox executionOutbox = new ExecutionOutbox();
         @Valid
-        @Data
-        public static class ExecutionOutbox {
+        public record ExecutionOutbox(
+            @DefaultValue
+            RestApi restApi,
+            /**
+             * Whether the {@link ExecutionSubscriber}
+             * implementation provided by this extension (which persists executions to the outbox) is enabled or not.
+             *
+             * <p>One reason to use this option is if you wish to provide your own implementation that wraps
+             * or delegates to the outbox implementation of {@link ExecutionSubscriber} that is
+             * provided by the <i>executionOutbox</i> extension.  Because executions are published to
+             * <i>all</i> subscribers on the class path, you can disable the outbox implementation from
+             * doing anything using this setting.
+             */
+            @DefaultValue("ENABLED")
+            ExecutionOutbox.PersistPolicy persist) {
 
-            private final RestApi restApi = new RestApi();
             @Valid
-            @Data
-            public static class RestApi {
+            public record RestApi(
                 /**
                  * The maximum number of interactions that will be returned when the REST API is polled.
                  */
                 @Min(value = 1)
                 @Max(value = 1000)
-                private int maxPending = 100;
+                @DefaultValue("100")
+                int maxPending) {
             }
 
             /**
-             * As per {@link ExecutionLog#getPersist()}.
+             * As per {@link ExecutionLog#persist()}.
              *
-             * <p>
-             *     Implementation note: we use an enum here (rather than a simple boolean) to allow for future
-             *     enhancements.
-             * </p>
+             * <p>Implementation note: we use an enum here (rather than a simple boolean) to allow for future
+             * enhancements.
              */
             public enum PersistPolicy {
                 /**
@@ -4075,101 +3942,90 @@ public enum PersistPolicy {
                 public boolean isEnabled() { return this == ENABLED; }
                 public boolean isDisabled() { return this == DISABLED; }
             }
-
-            /**
-             * Whether the {@link ExecutionSubscriber}
-             * implementation provided by this extension (which persists executions to the outbox) is enabled or not.
-             *
-             * <p>
-             *     One reason to use this option is if you wish to provide your own implementation that wraps
-             *     or delegates to the outbox implementation of {@link ExecutionSubscriber} that is
-             *     provided by the <i>executionOutbox</i> extension.  Because executions are published to
-             *     <i>all</i> subscribers on the class path, you can disable the outbox implementation from
-             *     doing anything using this setting.
-             * </p>
-             */
-            private ExecutionOutbox.PersistPolicy persist = ExecutionOutbox.PersistPolicy.ENABLED;
-
         }
 
-        private final LayoutLoaders layoutLoaders = new LayoutLoaders();
-        @Data
-        public static class LayoutLoaders {
-
-            private final Github github = new Github();
-            @Data
-            public static class Github {
+        public record LayoutLoaders(
+            @DefaultValue
+            Github github) {
 
+            public record Github(
                 /**
                  * eg <code>apache/causeway-app-simpleapp</code>
                  */
-                private String repository;
-
+                String repository,
                 /**
                  * As per <a href="https://github.com/settings/tokens">https://github.com/settings/tokens</a>,
                  * must have permissions to the <code>/search</code> and <code>/contents</code> APIs for the specified
-                 * {@link #getRepository() repository}.
+                 * {@link #repository()}.
                  */
-                private String apiKey;
+                String apiKey) {
             }
         }
 
-        private final Secman secman = new Secman();
-        @Data
-        public static class Secman {
+        public record Secman(
+            @DefaultValue
+            Seed seed,
+            @DefaultValue
+            FixtureScripts fixtureScripts,
+            @DefaultValue
+            DelegatedUsers delegatedUsers,
+            /**
+             * Whether the presence of SecMan should result in the automatic suppression of the
+             * {@link org.apache.causeway.applib.services.userui.UserMenu}'s {@link UserMenu.me#act() me()} action.
+             *
+             * <p>This is normally what is required as SecMan's <code>ApplicationUser</code> is a more comprehensive
+             * representation of the current user.  If the default {@link UserMenu.me#act() me()} action is not
+             * suppressed, then the end-user will see two actions with the name 'me' in the tertiary menu.
+             */
+            @DefaultValue("HIDE")
+            UserMenuMeActionPolicy userMenuMeActionPolicy,
+            /**
+             * If there are conflicting (allow vs veto) permissions at the same scope, then this policy determines
+             * whether to prefer to allow the permission or to veto it.
+             *
+             * <p>This is only used an implementation of secman's <code>PermissionsEvaluationService</code> SPI has
+             * not been provided explicitly.
+             *
+             */
+            @DefaultValue("ALLOW_BEATS_VETO")
+            PermissionsEvaluationPolicy permissionsEvaluationPolicy,
+            @DefaultValue
+            UserRegistration userRegistration) {
 
-            private final Seed seed = new Seed();
-            @Data
-            public static class Seed {
+            public record Seed(
+                /**
+                 * Path to local YAML file, if present, to use as an alternative seeding strategy.
+                 *
+                 * <p>Eg. seed from a YAML file, that was previously exported by SecMan's
+                 * ApplicationRoleManager_exportAsYaml mixin.
+                 */
+                String yamlFile,
+                @DefaultValue
+                Admin admin,
+                @DefaultValue
+                RegularUser regularUser) {
 
                 public static final String ADMIN_USER_NAME_DEFAULT = "secman-admin";
                 public static final String ADMIN_PASSWORD_DEFAULT = "pass";
                 public static final String ADMIN_ROLE_NAME_DEFAULT = "causeway-ext-secman-admin";
-                public static final List<String> ADMIN_STICKY_NAMESPACE_PERMISSIONS_DEFAULT =
-                        Collections.unmodifiableList(listOf(
-                                CausewayModuleApplib.NAMESPACE,
-                                CausewayModuleApplib.NAMESPACE_SUDO,
-                                CausewayModuleApplib.NAMESPACE_CONF,
-                                CausewayModuleApplib.NAMESPACE_FEAT,
-                                "causeway.security",
-                                "causeway.ext.h2Console",
-                                "causeway.ext.secman",
-                                "causeway.ext.layoutLoaders"
-                        ));
-                public static final List<String> ADMIN_ADDITIONAL_NAMESPACE_PERMISSIONS =
-                        Collections.unmodifiableList(listOf());
                 public static final String REGULAR_USER_ROLE_NAME_DEFAULT = "causeway-ext-secman-user";
                 public static final boolean AUTO_UNLOCK_IF_DELEGATED_AND_AUTHENTICATED_DEFAULT = false;
 
-                /**
-                 * Path to local YAML file, if present, to use as an alternative seeding strategy.
-                 * <p>
-                 * Eg. seed from a YAML file, that was previously exported by SecMan's
-                 * ApplicationRoleManager_exportAsYaml mixin.
-                 */
-                private String yamlFile = null;
-
-                private final Admin admin = new Admin();
-                @Data
-                public static class Admin {
-
+                public record Admin(
                     /**
                      * The name of the security super user.
                      *
-                     * <p>
-                     * This user is automatically made a member of the
+                     * <p>This user is automatically made a member of the
                      * {@link #getRoleName() admin role}, from which it is granted
                      * permissions to administer other users.
-                     * </p>
                      *
-                     * <p>
-                     * The password for this user is set in {@link Admin#getPassword()}.
-                     * </p>
+                     * <p>The password for this user is set in {@link Admin#getPassword()}.
                      *
                      * @see #getPassword()
                      * @see #getRoleName()
                      */
-                    private String userName = ADMIN_USER_NAME_DEFAULT;
+                    @DefaultValue(ADMIN_USER_NAME_DEFAULT)
+                    String userName,
 
                     // sonar-ignore-on (detects potential security risk, which we are aware of)
                     /**
@@ -4177,101 +4033,103 @@ public static class Admin {
                      *
                      * @see #getUserName()
                      */
-                    private String password = ADMIN_PASSWORD_DEFAULT;
+                    @DefaultValue(ADMIN_PASSWORD_DEFAULT)
+                    String password,
                     // sonar-ignore-off
 
                     /**
                      * The name of security admin role.
                      *
-                     * <p>
-                     * Users with this role (in particular, the default
-                     * {@link Admin#getUserName() admin user} are granted access to a set of
-                     * namespaces ({@link NamespacePermissions#getSticky()} and
-                     * {@link NamespacePermissions#getAdditional()}) which are intended to
+                     * <p>Users with this role (in particular, the default
+                     * {@link Admin#userName() admin user} are granted access to a set of
+                     * namespaces ({@link NamespacePermissions#sticky()} and
+                     * {@link NamespacePermissions#additional()}) which are intended to
                      * be sufficient to allow users with this admin role to be able to
                      * administer the security module itself, for example to manage users and
                      * roles.
-                     * </p>
                      *
-                     * @see Admin#getUserName()
-                     * @see NamespacePermissions#getSticky()
-                     * @see NamespacePermissions#getAdditional()
+                     * @see Admin#userName()
+                     * @see NamespacePermissions#sticky()
+                     * @see NamespacePermissions#additional()
                      */
-                    private String roleName = ADMIN_ROLE_NAME_DEFAULT;
-
-                    private final NamespacePermissions namespacePermissions = new NamespacePermissions();
-                    @Data
-                    public static class NamespacePermissions {
+                    @DefaultValue(ADMIN_ROLE_NAME_DEFAULT)
+                    String roleName,
+                    @DefaultValue
+                    NamespacePermissions namespacePermissions
+                    ) {
 
+                    public record NamespacePermissions(
                         /**
                          * The set of namespaces to which the {@link Admin#getRoleName() admin role}
                          * is granted.
                          *
-                         * <p>
-                         * These namespaces are intended to be sufficient to allow users with
+                         * <p>These namespaces are intended to be sufficient to allow users with
                          * this admin role to be able to administer the security module itself,
                          * for example to manage users and roles.  The security user is not
                          * necessarily able to use the main business logic within the domain
                          * application itself, though.
-                         * </p>
                          *
-                         * <p>
-                         * These roles cannot be removed via user interface
-                         * </p>
+                         * <p>These roles cannot be removed via user interface
                          *
-                         * <p>
-                         * WARNING: normally these should not be overridden.  Instead, specify
+                         * <p>WARNING: normally these should not be overridden.  Instead, specify
                          * additional namespaces using
                          * {@link NamespacePermissions#getAdditional()}.
-                         * </p>
                          *
                          * @see NamespacePermissions#getAdditional()
                          */
-                        private List<String> sticky = ADMIN_STICKY_NAMESPACE_PERMISSIONS_DEFAULT;
+                        @DefaultValue({
+                            CausewayModuleApplib.NAMESPACE,
+                            CausewayModuleApplib.NAMESPACE_SUDO,
+                            CausewayModuleApplib.NAMESPACE_CONF,
+                            CausewayModuleApplib.NAMESPACE_FEAT,
+                            "causeway.security",
+                            "causeway.ext.h2Console",
+                            "causeway.ext.secman",
+                        "causeway.ext.layoutLoaders" })
+                        List<String> sticky,
 
                         /**
                          * An (optional) additional set of namespaces that the
-                         * {@link Admin#getRoleName() admin role} is granted.
+                         * {@link Admin#roleName() admin role} is granted.
                          *
-                         * <p>
-                         * These are in addition to the main
-                         * {@link NamespacePermissions#getSticky() namespaces} granted.
-                         * </p>
+                         * <p>These are in addition to the main
+                         * {@link #sticky() namespaces} granted.
                          *
-                         * @see NamespacePermissions#getSticky()
+                         * @see #sticky()
                          */
-                        private List<String> additional = ADMIN_ADDITIONAL_NAMESPACE_PERMISSIONS;
-
+                        @DefaultValue({})
+                        List<String> additional
+                        ) {
                     }
 
                 }
 
-                private final RegularUser regularUser = new RegularUser();
-                @Data
-                public static class RegularUser {
-
+                public record RegularUser(
                     /**
                      * The role name for regular users of the application, granting them access
                      * to basic security features.
                      *
-                     * <p>
-                     *     The exact set of permissions is hard-wired in the
-                     *     <code>CausewayExtSecmanRegularUserRoleAndPermissions</code> fixture.
-                     * </p>
+                     * <p>The exact set of permissions is hard-wired in the
+                     * <code>CausewayExtSecmanRegularUserRoleAndPermissions</code> fixture.
                      */
-                    private String roleName = REGULAR_USER_ROLE_NAME_DEFAULT;
-
+                    @DefaultValue(REGULAR_USER_ROLE_NAME_DEFAULT)
+                    String roleName) {
                 }
 
             }
 
-            private final FixtureScripts fixtureScripts = new FixtureScripts();
-            @Data
-            public static class FixtureScripts {
+            public record FixtureScripts(
+                @DefaultValue
+                AbstractRoleAndPermissionsFixtureScript abstractRoleAndPermissionsFixtureScript
+                ) {
 
-                private final AbstractRoleAndPermissionsFixtureScript abstractRoleAndPermissionsFixtureScript = new AbstractRoleAndPermissionsFixtureScript();
-                @Data
-                public static class AbstractRoleAndPermissionsFixtureScript {
+                public record AbstractRoleAndPermissionsFixtureScript(
+                    /**
+                     * Whether to check if every featureId passed in exists or not.
+                     */
+                    @DefaultValue("IGNORE")
+                    UnknownFeatureIdCheckingPolicy unknownFeatureIdCheckingPolicy
+                    ) {
 
                     public enum UnknownFeatureIdCheckingPolicy {
                         /**
@@ -4281,58 +4139,43 @@ public enum UnknownFeatureIdCheckingPolicy {
                         /**
                          * Check that the featureIds passed in actually exist, and fail immediately if not.
                          */
-                        FAIL_FAST,
-                        ;
-
+                        FAIL_FAST;
                         public boolean isIgnore() { return this == IGNORE; }
                         public boolean isFailFast() { return this == FAIL_FAST; }
                     }
-
-                    /**
-                     * Whether to check if every featureId passed in exists or not.
-                     */
-                    private UnknownFeatureIdCheckingPolicy unknownFeatureIdCheckingPolicy = UnknownFeatureIdCheckingPolicy.IGNORE;
                 }
 
             }
 
-            private final DelegatedUsers delegatedUsers = new DelegatedUsers();
-
-            @Data
-            public static class DelegatedUsers {
-
-                public enum AutoCreatePolicy {
-                    AUTO_CREATE_AS_LOCKED,
-                    AUTO_CREATE_AS_UNLOCKED,
-                    DO_NOT_AUTO_CREATE,
-                }
-
+            public record DelegatedUsers(
                 /**
                  * Whether delegated users should be autocreated as locked (the default) or unlocked.
                  *
-                 * <p>
-                 * BE AWARE THAT if any users are auto-created as unlocked, then the set of roles that
+                 * <p>BE AWARE THAT if any users are auto-created as unlocked, then the set of roles that
                  * they are given should be highly restricted !!!
-                 * </p>
                  *
-                 * <p>
-                 *     NOTE also that this configuration policy is ignored if running secman with Spring OAuth2
-                 *     or Keycloak as the authenticator; users are always auto-created.
-                 * </p>
+                 * <p>NOTE also that this configuration policy is ignored if running secman with Spring OAuth2
+                 * or Keycloak as the authenticator; users are always auto-created.
                  */
-                private AutoCreatePolicy autoCreatePolicy = AutoCreatePolicy.AUTO_CREATE_AS_LOCKED;
+                @DefaultValue("AUTO_CREATE_AS_LOCKED")
+                AutoCreatePolicy autoCreatePolicy,
 
                 /**
                  * The set of roles that users that have been automatically created are granted automatically.
                  *
-                 * <p>
-                 *     Typically the regular user role (as per <code>causeway.secman.seed.regular-user.role-name</code>,
-                 *     default value of <code>causeway-ext-secman-user</code>) will be one of the roles listed here, to
-                 *     provide the ability for the end-user to logout, among other things (!).
-                 * </p>
+                 * <p>Typically the regular user role (as per <code>causeway.secman.seed.regular-user.role-name</code>,
+                 * default value of <code>causeway-ext-secman-user</code>) will be one of the roles listed here, to
+                 * provide the ability for the end-user to logout, among other things (!).
                  */
-                private List<String> initialRoleNames = new ArrayList<>();
+                @DefaultValue
+                List<String> initialRoleNames
+                ) {
 
+                public enum AutoCreatePolicy {
+                    AUTO_CREATE_AS_LOCKED,
+                    AUTO_CREATE_AS_UNLOCKED,
+                    DO_NOT_AUTO_CREATE,
+                }
             }
 
             public enum PermissionsEvaluationPolicy {
@@ -4340,31 +4183,18 @@ public enum PermissionsEvaluationPolicy {
                 VETO_BEATS_ALLOW
             }
 
-            /**
-             * If there are conflicting (allow vs veto) permissions at the same scope, then this policy determines
-             * whether to prefer to allow the permission or to veto it.
-             *
-             * <p>
-             *     This is only used an implementation of secman's <code>PermissionsEvaluationService</code> SPI has
-             *     not been provided explicitly.
-             * </p>
-             */
-            private PermissionsEvaluationPolicy permissionsEvaluationPolicy = PermissionsEvaluationPolicy.ALLOW_BEATS_VETO;
-
-            private final UserRegistration userRegistration = new UserRegistration();
-            @Data
-            public static class UserRegistration {
+            public record UserRegistration(
                 /**
                  * The set of roles that users registering with the app are granted
                  * automatically.
                  *
-                 * <p>
-                 *     If using the wicket viewer, also requires
-                 *     {@link Viewer.Wicket#isSuppressSignUp() causeway.viewer.wicket.suppress-signup} to be set
-                 *     <code>false</code>, along with any other of its other prereqs.
-                 * </p>
+                 * <p>If using the wicket viewer, also requires
+                 * {@link Viewer.Wicket#suppressSignUp() causeway.viewer.wicket.suppress-signup} to be set
+                 * <code>false</code>, along with any other of its other pre requisites.
                  */
-                private final List<String> initialRoleNames = new ArrayList<>();
+                @DefaultValue
+                List<String> initialRoleNames) {
+
             }
 
             public enum UserMenuMeActionPolicy {
@@ -4373,74 +4203,58 @@ public enum UserMenuMeActionPolicy {
                 ENABLE
             }
 
-            /**
-             * Whether the presence of SecMan should result in the automatic suppression of the
-             * {@link org.apache.causeway.applib.services.userui.UserMenu}'s {@link UserMenu.me#act() me()} action.
-             *
-             * <p>
-             *     This is normally what is required as SecMan's <code>ApplicationUser</code> is a more comprehensive
-             *     representation of the current user.  If the default {@link UserMenu.me#act() me()} action is not
-             *     suppressed, then the end-user will see two actions with the name &quot;me&quot; in the tertiary menu.
-             * </p>
-             */
-            private UserMenuMeActionPolicy userMenuMeActionPolicy = UserMenuMeActionPolicy.HIDE;
+            //TODO remove
+            @Deprecated
+            public static Secman defaults() {
+                throw _Exceptions.unsupportedOperation();
+            }
         }
 
-        private final SessionLog sessionLog = new SessionLog();
-        @Data
-        public static class SessionLog {
-            boolean autoLogoutOnRestart = true;
+        public record SessionLog(
+            @DefaultValue("true")
+            boolean autoLogoutOnRestart) {
         }
 
-        private final Titlecache titlecache = new Titlecache();
-        @Data
-        public static class Titlecache {
+        public record Titlecache(
+            @DefaultValue
+            Caffeine caffeine) {
 
-            private final Caffeine caffeine = new Caffeine();
-            @Data
-            public static class Caffeine {
+            public record Caffeine(
                 /**
                  * Default duration that entries remain in the cache (for a given logical type name),
                  * in minutes.
                  *
-                 * <p>
-                 *     Default is 20 mins.
-                 * </p>
+                 * <p>Default is 20 minutes.
                  */
-                private int expiryDurationInMinutes = 20;
-
+                @DefaultValue("20")
+                int expiryDurationInMinutes,
                 /**
                  * Default maximum number of entries in the cache (for a given logical type name)
                  *
-                 * <p>
-                 *     Default is 1000
-                 * </p>
+                 * <p>Default is 1000
                  */
-                private int maxSizeInEntries = 1000;
+                @DefaultValue("1000")
+                int maxSizeInEntries) {
             }
         }
     }
 
-    private static List<String> listOf(final String ...values) {
-        return new ArrayList<>(Arrays.asList(values));
-    }
-
     record PatternToString(
-            Pattern pattern,
-            String string) {
+        Pattern pattern,
+        String string) {
     }
-    private static Map<Pattern, String> asMap(final String... mappings) {
+    static Map<Pattern, String> asMap(final String... mappings) {
         return new LinkedHashMap<>(_NullSafe.stream(mappings).map(mapping -> {
-                    final String[] parts = mapping.split(":");
-                    if (parts.length != 2) return null;
-                    
-                    try {
-                        return new PatternToString(Pattern.compile(parts[0], Pattern.CASE_INSENSITIVE), parts[1]);
-                    } catch(Exception ex) {
-                        return null;
-                    }
-                }).filter(Objects::nonNull)
-                .collect(Collectors.toMap(PatternToString::pattern, PatternToString::string)));
+            final String[] parts = mapping.split(":");
+            if (parts.length != 2) return null;
+
+            try {
+                return new PatternToString(Pattern.compile(parts[0], Pattern.CASE_INSENSITIVE), parts[1]);
+            } catch(Exception ex) {
+                return null;
+            }
+        }).filter(Objects::nonNull)
+            .collect(Collectors.toMap(PatternToString::pattern, PatternToString::string)));
     }
 
     @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@@ -4452,35 +4266,39 @@ private static Map<Pattern, String> asMap(final String... mappings) {
         String value();
 
         String message()
-                default "{org.apache.causeway.core.config.CausewayConfiguration.AssignableFrom.message}";
+        default "{org.apache.causeway.core.config.CausewayConfiguration.AssignableFrom.message}";
 
         Class<?>[] groups() default { };
 
         Class<? extends Payload>[] payload() default { };
     }
 
-    public static class AssignableFromValidator implements ConstraintValidator<AssignableFrom, Class<?>> {
+    public record AssignableFromValidator(
+        //XXX StableValue candidate JDK25++
+        AtomicReference<Class<?>> superType) implements ConstraintValidator<AssignableFrom, Class<?>> {
 
-        private Class<?> superType;
+        public AssignableFromValidator() {
+            this(new AtomicReference<>());
+        }
 
         @Override
         public void initialize(final AssignableFrom assignableFrom) {
             var className = assignableFrom.value();
             try {
-                superType = _Context.loadClass(className);
+                superType.set(_Context.loadClass(className));
             } catch (ClassNotFoundException e) {
-                superType = null;
+                superType.set(null);
             }
         }
 
         @Override
         public boolean isValid(
-                final Class<?> candidateClass,
-                final ConstraintValidatorContext constraintContext) {
-            if (superType == null || candidateClass == null) {
+            final Class<?> candidateClass,
+            final ConstraintValidatorContext constraintContext) {
+            if (superType.get() == null || candidateClass == null) {
                 return true;
             }
-            return superType.isAssignableFrom(candidateClass);
+            return superType.get().isAssignableFrom(candidateClass);
         }
     }
 
@@ -4493,28 +4311,33 @@ public boolean isValid(
         String[] value();
 
         String message()
-                default "{org.apache.causeway.core.config.CausewayConfiguration.OneOf.message}";
+        default "{org.apache.causeway.core.config.CausewayConfiguration.OneOf.message}";
 
         Class<?>[] groups() default { };
 
         Class<? extends Payload>[] payload() default { };
     }
 
-    public static class OneOfValidator implements ConstraintValidator<OneOf, String> {
+    public record OneOfValidator(
+        //XXX StableValue candidate JDK25++
+        AtomicReference<List<String>> allowed
+        ) implements ConstraintValidator<OneOf, String> {
 
-        private List<String> allowed;
+        public OneOfValidator() {
+            this(new AtomicReference<>());
+        }
 
         @Override
         public void initialize(final OneOf assignableFrom) {
             var value = assignableFrom.value();
-            allowed = value != null? Collections.unmodifiableList(Arrays.asList(value)): Collections.emptyList();
+            allowed.set(value != null? List.of(value): Collections.emptyList());
         }
 
         @Override
         public boolean isValid(
-                final String candidateValue,
-                final ConstraintValidatorContext constraintContext) {
-            return allowed.contains(candidateValue);
+            final String candidateValue,
+            final ConstraintValidatorContext constraintContext) {
+            return allowed.get().contains(candidateValue);
         }
     }
 
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/CausewayModuleCoreConfig.java b/core/config/src/main/java/org/apache/causeway/core/config/CausewayModuleCoreConfig.java
index bb1e81ce8a6..5e89e825129 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/CausewayModuleCoreConfig.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/CausewayModuleCoreConfig.java
@@ -23,7 +23,6 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
-
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.core.config.applib.RestfulPathProvider;
 import org.apache.causeway.core.config.beans.CausewayBeanFactoryPostProcessor;
@@ -35,10 +34,11 @@
 import org.apache.causeway.core.config.validators.PatternOptionalStringConstraintValidator;
 import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
 
-@Configuration
+@Configuration(proxyBeanMethods = false)
 @Import({
 
     // @Component
+    CausewayConfiguration.class,
     PatternsConverter.class,
     CausewayBeanFactoryPostProcessor.class,
     CausewayLocaleInitializer.class,
@@ -52,7 +52,7 @@
     WebAppContextPath.class,
 })
 @EnableConfigurationProperties({
-        CausewayConfiguration.class,
+        CausewayConfiguration.Causeway.class,
         EclipselinkConfiguration.class,
         EclipselinkConfiguration.Weaving.class,
         EclipselinkConfiguration.DdlGeneration.class,
@@ -74,24 +74,21 @@ public EmailConfiguration emailConfiguration(
         @Value("#{systemProperties['spring.mail.properties.mail.smtp.timeout']}") Integer smtpTimeout,
         @Value("#{systemProperties['spring.mail.properties.mail.smtp.connectiontimeout']}") Integer smtpConnectionTimeout) {
 
-        var emailConfiguration = conf.getCore().getRuntimeServices().getEmail();
-
-        String senderUsername = _Strings.emptyToNull(senderEmailUsername);
-        String senderPassword = _Strings.emptyToNull(senderEmailPassword);
-        String senderHostName = _Strings.emptyToNull(senderEmailHostName);
-        int senderPort = senderEmailPort!=null ? senderEmailPort : 587;
-        boolean isSenderTlsEnabled = senderEmailTlsEnabled!=null ? senderEmailTlsEnabled : true;
-        int socketTimeout = smtpTimeout!=null ? smtpTimeout : 2000;
-        int socketConnectionTimeout = smtpConnectionTimeout!=null ? smtpConnectionTimeout : 2000;
-        boolean isThrowExceptionOnFail = emailConfiguration.isThrowExceptionOnFail();
-        String senderAddress = _Strings.emptyToNull(emailConfiguration.getSender().getAddress());
-        String overrideTo = _Strings.emptyToNull(emailConfiguration.getOverride().getTo());
-        String overrideCc = _Strings.emptyToNull(emailConfiguration.getOverride().getCc());
-        String overrideBcc = _Strings.emptyToNull(emailConfiguration.getOverride().getBcc());
+        var emailConfiguration = conf.core().runtimeServices().email();
 
-        return new EmailConfiguration(senderUsername, senderPassword, senderHostName, senderPort,
-            isSenderTlsEnabled, socketTimeout, socketConnectionTimeout,
-            isThrowExceptionOnFail, senderAddress, overrideTo, overrideCc, overrideBcc);
+        return new EmailConfiguration(
+            _Strings.emptyToNull(senderEmailUsername),
+            _Strings.emptyToNull(senderEmailPassword),
+            _Strings.emptyToNull(senderEmailHostName),
+            senderEmailPort!=null ? senderEmailPort : 587,
+            senderEmailTlsEnabled!=null ? senderEmailTlsEnabled : true,
+            smtpTimeout!=null ? smtpTimeout : 2000,
+            smtpConnectionTimeout!=null ? smtpConnectionTimeout : 2000,
+            emailConfiguration.throwExceptionOnFail(),
+            _Strings.emptyToNull(emailConfiguration.sender().address()),
+            _Strings.emptyToNull(emailConfiguration.override().to()),
+            _Strings.emptyToNull(emailConfiguration.override().cc()),
+            _Strings.emptyToNull(emailConfiguration.override().bcc()));
     }
 
 }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/applib/RestfulPathProvider.java b/core/config/src/main/java/org/apache/causeway/core/config/applib/RestfulPathProvider.java
index 3c0263e3eb0..e9e5b68c59b 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/applib/RestfulPathProvider.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/applib/RestfulPathProvider.java
@@ -36,7 +36,7 @@ public record RestfulPathProvider(CausewayConfiguration configuration)
 
     @Override
     public Optional<String> getRestfulPath() {
-        return _Strings.nonEmpty(configuration.getViewer().getRestfulobjects().getBasePath());
+        return _Strings.nonEmpty(configuration.viewer().restfulobjects().basePath());
     }
 
 }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayLocaleInitializer.java b/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayLocaleInitializer.java
index 38feda73268..b9d027be811 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayLocaleInitializer.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayLocaleInitializer.java
@@ -38,7 +38,7 @@ public class CausewayLocaleInitializer {
 
     @PostConstruct
     public void initLocale() {
-        final Optional<String> localeSpecOpt = configuration.getCore().getRuntime().getLocale();
+        final Optional<String> localeSpecOpt = configuration.core().runtime().locale();
         localeSpecOpt.map(CausewayLocaleInitializer::toLocale).ifPresent(CausewayLocaleInitializer::setLocaleDefault);
         log.debug("locale is {}", Locale.getDefault());
     }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewaySystemEnvironment.java b/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewaySystemEnvironment.java
index 1f6ed9ca452..760b77eac07 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewaySystemEnvironment.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewaySystemEnvironment.java
@@ -35,10 +35,11 @@
 
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.context._Context;
-import org.apache.causeway.commons.internal.ioc._IocContainer;
+import org.apache.causeway.commons.internal.ioc.SpringContextHolder;
 import org.apache.causeway.core.config.CausewayModuleCoreConfig;
 
 import lombok.Getter;
+import lombok.experimental.Accessors;
 import lombok.extern.slf4j.Slf4j;
 
 /**
@@ -58,14 +59,15 @@ public class CausewaySystemEnvironment {
 
     @Inject private ApplicationContext springContext;
 
-    @Getter private _IocContainer iocContainer;
+    @Getter @Accessors(fluent=true)
+    private SpringContextHolder springContextHolder;
 
     // -- LIFE-CYCLE
 
     @PostConstruct
     public void postConstruct() {
 
-        this.iocContainer = _IocContainer.spring(springContext);
+        this.springContextHolder = new SpringContextHolder(springContext);
 
         log.info("postConstruct (hashCode = {})", this.hashCode());
 
@@ -95,26 +97,20 @@ public void onContextRefreshed(final ContextRefreshedEvent event) {
 
     @EventListener(ContextClosedEvent.class)
     public void onContextAboutToClose(final ContextClosedEvent event) {
-        // happens before any @PostConstruct
-        // as a consequence, no managed bean should touch the _Context during its post-construct phase
+        // happens before any @PreDestroy
+        // as a consequence, no managed bean should touch the _Context during its pre-detroy phase
         // as it has already been cleared here
         log.info("Context about to close.");
-        this.iocContainer = null;
+        this.springContextHolder = null;
         _Context.clear();
     }
 
     @EventListener(ApplicationFailedEvent.class)
-    public void onContextRefreshed(final ApplicationFailedEvent event) {
+    public void onApplicationFailed(final ApplicationFailedEvent event) {
         // happens eg. when DN finds non enhanced entity classes
         log.error("Application failed to start", event.getException());
     }
 
-    // -- SHORTCUTS
-
-    public _IocContainer ioc() {
-        return getIocContainer();
-    }
-
     // -- SETUP
 
     /**
@@ -123,6 +119,7 @@ public _IocContainer ioc() {
      * Must be set prior to configuration bootstrapping.
      * @param isUnitTesting
      */
+    @Deprecated //TODO use autodetect instead (class-path analysis)
     public static void setUnitTesting(final boolean isUnitTesting) {
         System.setProperty("UNITTESTING", ""+isUnitTesting);
     }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayTimeZoneInitializer.java b/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayTimeZoneInitializer.java
index bd12735b32a..f41f359070b 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayTimeZoneInitializer.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/environment/CausewayTimeZoneInitializer.java
@@ -37,7 +37,7 @@ public class CausewayTimeZoneInitializer {
 
     @PostConstruct
     public void initTimeZone() {
-        final String timeZoneSpec = configuration.getCore().getRuntime().getTimezone();
+        final String timeZoneSpec = configuration.core().runtime().timezone();
         if (timeZoneSpec != null) {
             TimeZone timeZone;
             timeZone = TimeZone.getTimeZone(timeZoneSpec);
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ActionConfigOptions.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ActionConfigOptions.java
index cf9fac15a18..787bfdf0235 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ActionConfigOptions.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ActionConfigOptions.java
@@ -26,12 +26,12 @@ public final class ActionConfigOptions {
 
     public static PublishingPolicy actionCommandPublishingPolicy(
             final @NonNull CausewayConfiguration configuration) {
-        return configuration.getApplib().getAnnotation().getAction().getCommandPublishing();
+        return configuration.applib().annotation().action().commandPublishing();
     }
 
     public static PublishingPolicy actionExecutionPublishingPolicy(
             final @NonNull CausewayConfiguration configuration) {
-        return configuration.getApplib().getAnnotation().getAction().getExecutionPublishing();
+        return configuration.applib().annotation().action().executionPublishing();
     }
 
     public static enum PublishingPolicy {
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/AssociationLayoutConfigOptions.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/AssociationLayoutConfigOptions.java
index 3406de19c6b..6a9a662f978 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/AssociationLayoutConfigOptions.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/AssociationLayoutConfigOptions.java
@@ -36,13 +36,13 @@ public String toNameLower() {
         }
 
         public static SequencePolicy forProperty(final CausewayConfiguration configuration) {
-            return configuration.getApplib().getAnnotation().getPropertyLayout()
-                    .getSequencePolicyIfUnreferenced();
+            return configuration.applib().annotation().propertyLayout()
+                    .sequencePolicyIfUnreferenced();
         }
 
         public static SequencePolicy forCollection(final CausewayConfiguration configuration) {
-            return configuration.getApplib().getAnnotation().getCollectionLayout()
-                    .getSequencePolicyIfUnreferenced();
+            return configuration.applib().annotation().collectionLayout()
+                    .sequencePolicyIfUnreferenced();
         }
     }
 
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/CollectionLayoutConfigOptions.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/CollectionLayoutConfigOptions.java
index e0b390ab4d7..f1f1515b43a 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/CollectionLayoutConfigOptions.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/CollectionLayoutConfigOptions.java
@@ -31,7 +31,7 @@ public String toNameLower() {
         }
 
         public static DefaultView from(final CausewayConfiguration configuration) {
-            return configuration.getApplib().getAnnotation().getCollectionLayout().getDefaultView();
+            return configuration.applib().annotation().collectionLayout().defaultView();
         }
 
     }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/DomainObjectConfigOptions.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/DomainObjectConfigOptions.java
index 11db3971bce..9653015c813 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/DomainObjectConfigOptions.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/DomainObjectConfigOptions.java
@@ -33,10 +33,10 @@ public enum EditingObjectsConfiguration {
         TRUE,
         FALSE;
     }
-    
+
     public static EntityChangePublishingPolicy entityChangePublishingPolicy(
             final @NonNull CausewayConfiguration configuration) {
-        return configuration.getApplib().getAnnotation().getDomainObject().getEntityChangePublishing();
+        return configuration.applib().annotation().domainObject().entityChangePublishing();
     }
 
 }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ParameterConfigOptions.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ParameterConfigOptions.java
index 36432da2b7f..624d2dcf161 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ParameterConfigOptions.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/ParameterConfigOptions.java
@@ -51,7 +51,7 @@ public static PrecedingParametersPolicy defaultsIfNotSpecifiedOtherwise() {
 
     public static PrecedingParametersPolicy precedingParametersPolicy(
             final @NonNull CausewayConfiguration configuration) {
-        return configuration.getApplib().getAnnotation().getParameter().getPrecedingParametersPolicy();
+        return configuration.applib().annotation().parameter().precedingParametersPolicy();
     }
 
 }
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/PropertyConfigOptions.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/PropertyConfigOptions.java
index c41d00e2301..873a4a73477 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/PropertyConfigOptions.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/facets/PropertyConfigOptions.java
@@ -26,12 +26,12 @@ public final class PropertyConfigOptions {
 
     public static PublishingPolicy propertyCommandPublishingPolicy(
             final @NonNull CausewayConfiguration configuration) {
-        return configuration.getApplib().getAnnotation().getProperty().getCommandPublishing();
+        return configuration.applib().annotation().property().commandPublishing();
     }
 
     public static PublishingPolicy propertyExecutionPublishingPolicy(
             final @NonNull CausewayConfiguration configuration) {
-        return configuration.getApplib().getAnnotation().getProperty().getExecutionPublishing();
+        return configuration.applib().annotation().property().executionPublishing();
     }
 
     public enum PublishingPolicy {
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/specloader/IntrospectionMode.java b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/specloader/IntrospectionMode.java
index 08fd9230250..18e7d931777 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/metamodel/specloader/IntrospectionMode.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/metamodel/specloader/IntrospectionMode.java
@@ -58,7 +58,7 @@ protected boolean isFullIntrospect(final DeploymentType deploymentType) {
     protected abstract boolean isFullIntrospect(final DeploymentType deploymentType);
 
     public static boolean isFullIntrospect(CausewayConfiguration configuration, CausewaySystemEnvironment causewaySystemEnvironment) {
-        var introspectionMode = configuration.getCore().getMetaModel().getIntrospector().getMode();
+        var introspectionMode = configuration.core().metaModel().introspector().mode();
         return introspectionMode.isFullIntrospect(causewaySystemEnvironment.getDeploymentType());
     }
 
diff --git a/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInParentedTables_Test.java b/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInParentedTables_Test.java
index d86b3d14a11..5e7d3a1d79b 100644
--- a/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInParentedTables_Test.java
+++ b/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInParentedTables_Test.java
@@ -19,61 +19,69 @@
 package org.apache.causeway.core.config;
 
 import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-class CausewayConfiguration_getMaxTitleLengthInParentedTables_Test {
+import org.springframework.boot.test.util.TestPropertyValues;
 
-    private CausewayConfiguration configuration;
+//--add-exports org.hibernate.validator/org.hibernate.validator.internal.engine=spring.core
+//--add-exports org.hibernate.validator/org.hibernate.validator.internal.constraintvalidators.bv=org.apache.causeway.core.config
+class CausewayConfiguration_getMaxTitleLengthInParentedTables_Test {
 
-    @BeforeEach
-    void setUp() throws Exception {
-        configuration = CausewayConfiguration.builder().build();
-    }
+    private ConfigurationFactory configurationFactory = new ConfigurationFactory();
 
     @Test
     void when_not_set() throws Exception {
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInParentedTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(12);
+        configurationFactory.test(
+            TestPropertyValues.empty(),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInParentedTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(12);
+            });
     }
 
     @Test
-    void when_not_set_explicitly_but_fallback_has_been() throws Exception {
-        // given
-        configuration.getViewer().getWicket().setMaxTitleLengthInTables(20);
-
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInParentedTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(20);
+    void when_not_set_explicitly_but_fallback_has_been() {
+        configurationFactory.test(
+            TestPropertyValues.of(
+                // given
+                "causeway.viewer.wicket.maxTitleLengthInTables=20"),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInParentedTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(20);
+            });
     }
 
     @Test
-    void when_set_explicitly() throws Exception {
-        // given
-        configuration.getViewer().getWicket().setMaxTitleLengthInParentedTables(25);
-
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInParentedTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(25);
+    void when_set_explicitly() {
+        configurationFactory.test(
+            TestPropertyValues.of(
+                // given
+                "causeway.viewer.wicket.maxTitleLengthInParentedTables=25"),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInParentedTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(25);
+            });
     }
 
     @Test
-    void when_set_explicitly_ignores_fallback_has_been() throws Exception {
-        // given
-        configuration.getViewer().getWicket().setMaxTitleLengthInTables(20);
-        configuration.getViewer().getWicket().setMaxTitleLengthInParentedTables(25);
-
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInParentedTables();
+    void when_set_explicitly_ignores_fallback_has_been() {
+        configurationFactory.test(
+            TestPropertyValues.of(
+                // given
+                "causeway.viewer.wicket.maxTitleLengthInTables=20",
+                "causeway.viewer.wicket.maxTitleLengthInParentedTables=25"),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInParentedTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(25);
+            });
 
-        // then
-        Assertions.assertThat(val).isEqualTo(25);
     }
 }
\ No newline at end of file
diff --git a/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInStandaloneTables_Test.java b/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInStandaloneTables_Test.java
index 014cc452e8d..b0ac18b96ea 100644
--- a/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInStandaloneTables_Test.java
+++ b/core/config/src/test/java/org/apache/causeway/core/config/CausewayConfiguration_getMaxTitleLengthInStandaloneTables_Test.java
@@ -19,61 +19,68 @@
 package org.apache.causeway.core.config;
 
 import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-class CausewayConfiguration_getMaxTitleLengthInStandaloneTables_Test {
+import org.springframework.boot.test.util.TestPropertyValues;
 
-    private CausewayConfiguration configuration;
+class CausewayConfiguration_getMaxTitleLengthInStandaloneTables_Test {
 
-    @BeforeEach
-    public void setUp() throws Exception {
-        configuration = CausewayConfiguration.builder().build();
-    }
+    private ConfigurationFactory configurationFactory = new ConfigurationFactory();
 
     @Test
     void when_not_set() throws Exception {
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInStandaloneTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(12);
+        configurationFactory.test(
+            TestPropertyValues.empty(),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInStandaloneTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(12);
+            });
     }
 
     @Test
-    void when_not_set_explicitly_but_fallback_has_been() throws Exception {
-        // given
-        configuration.getViewer().getWicket().setMaxTitleLengthInTables(20);
-
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInStandaloneTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(20);
+    void when_not_set_explicitly_but_fallback_has_been() {
+        configurationFactory.test(
+            TestPropertyValues.of(
+                // given
+                "causeway.viewer.wicket.maxTitleLengthInTables=20"),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInStandaloneTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(20);
+            });
     }
 
     @Test
-    void when_set_explicitly() throws Exception {
-        // given
-        configuration.getViewer().getWicket().setMaxTitleLengthInStandaloneTables(25);
-
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInStandaloneTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(25);
+    void when_set_explicitly() {
+        configurationFactory.test(
+            TestPropertyValues.of(
+                // given
+                "causeway.viewer.wicket.maxTitleLengthInStandaloneTables=25"),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInStandaloneTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(25);
+            });
     }
 
     @Test
-    void when_set_explicitly_ignores_fallback_has_been() throws Exception {
-        // given
-        configuration.getViewer().getWicket().setMaxTitleLengthInTables(20);
-        configuration.getViewer().getWicket().setMaxTitleLengthInStandaloneTables(25);
+    void when_set_explicitly_ignores_fallback_has_been() {
+        configurationFactory.test(
+            TestPropertyValues.of(
+                // given
+                "causeway.viewer.wicket.maxTitleLengthInTables=20",
+                "causeway.viewer.wicket.maxTitleLengthInStandaloneTables=25"),
+            causeway->{
+                // when
+                int val = causeway.viewer().wicket().maxTitleLengthInStandaloneTables();
+                // then
+                Assertions.assertThat(val).isEqualTo(25);
+            });
 
-        // when
-        int val = configuration.getViewer().getWicket().getMaxTitleLengthInStandaloneTables();
-
-        // then
-        Assertions.assertThat(val).isEqualTo(25);
     }
+
 }
\ No newline at end of file
diff --git a/core/config/src/test/java/org/apache/causeway/core/config/ConfigurationFactory.java b/core/config/src/test/java/org/apache/causeway/core/config/ConfigurationFactory.java
new file mode 100644
index 00000000000..382a25d3ee3
--- /dev/null
+++ b/core/config/src/test/java/org/apache/causeway/core/config/ConfigurationFactory.java
@@ -0,0 +1,43 @@
+/*
+ *  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.causeway.core.config;
+
+import java.util.function.Consumer;
+
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.util.TestPropertyValues;
+
+record ConfigurationFactory() {
+
+    /**
+     * @param testPropertyValues {@code TestPropertyValues.of("causeway.property=value")}
+     * @param configConsumer
+     */
+    public void test(final TestPropertyValues testPropertyValues, Consumer<CausewayConfiguration.Causeway> configConsumer) {
+        testPropertyValues.applyToSystemProperties(()->{
+            new ApplicationContextRunner()
+                .withUserConfiguration(CausewayModuleCoreConfig.class)
+                .run(ctx -> {
+                    var config = ctx.getBean(CausewayConfiguration.Causeway.class);
+                    configConsumer.accept(config);
+                });
+        });
+    }
+
+}
diff --git a/core/configtest/pom.xml b/core/configtest/pom.xml
new file mode 100644
index 00000000000..c2f46f8aa26
--- /dev/null
+++ b/core/configtest/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.causeway.core</groupId>
+		<artifactId>causeway-core</artifactId>
+		<version>${revision}</version>
+	</parent>
+
+	<artifactId>causeway-core-configtest</artifactId>
+	<name>Apache Causeway Core - Config Test</name>
+
+	<properties>
+		<jar-plugin.automaticModuleName>org.apache.causeway.core.internaltestsupport</jar-plugin.automaticModuleName>
+		<git-plugin.propertiesDir>org/apache/causeway/core/internaltestsupport</git-plugin.propertiesDir>
+	</properties>
+
+	<description>
+        Supports configuration testing.
+    </description>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>org.apache.causeway.core</groupId>
+			<artifactId>causeway-core-config</artifactId>
+		</dependency>
+
+		<!-- SPRING -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.ow2.asm</groupId>
+					<artifactId>asm</artifactId>
+				</exclusion>
+				<exclusion>
+					<!-- when spring-boot release lags behind spring-core release explicitly 
+						spring-test added below -->
+					<groupId>org.springframework</groupId>
+					<artifactId>spring-core</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+	</dependencies>
+
+</project>
diff --git a/core/configtest/src/main/java/org/apache/causeway/core/configtest/ConfigurationTester.java b/core/configtest/src/main/java/org/apache/causeway/core/configtest/ConfigurationTester.java
new file mode 100644
index 00000000000..3c12892ff86
--- /dev/null
+++ b/core/configtest/src/main/java/org/apache/causeway/core/configtest/ConfigurationTester.java
@@ -0,0 +1,66 @@
+/*
+ *  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.causeway.core.configtest;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.util.TestPropertyValues;
+
+import org.apache.causeway.core.config.CausewayConfiguration;
+import org.apache.causeway.core.config.CausewayModuleCoreConfig;
+
+/**
+ * <pre>
+ * --add-exports org.hibernate.validator/org.hibernate.validator.internal.engine=spring.core
+ * --add-exports org.hibernate.validator/org.hibernate.validator.internal.constraintvalidators.bv=org.apache.causeway.core.config
+ * </pre>
+ */
+public record ConfigurationTester(
+    /**
+     * {@code TestPropertyValues.of("causeway.property=value")}
+     */
+    TestPropertyValues testPropertyValues) {
+
+    public void test(final Consumer<CausewayConfiguration.Causeway> configConsumer) {
+        testPropertyValues.applyToSystemProperties(()->{
+            new ApplicationContextRunner()
+                .withUserConfiguration(CausewayModuleCoreConfig.class)
+                .run(ctx -> {
+                    var config = ctx.getBean(CausewayConfiguration.Causeway.class);
+                    configConsumer.accept(config);
+                });
+        });
+    }
+
+    public CausewayConfiguration.Causeway causeway() {
+        final var causewayRef = new AtomicReference<CausewayConfiguration.Causeway>();
+        testPropertyValues.applyToSystemProperties(()->{
+            new ApplicationContextRunner()
+                .withUserConfiguration(CausewayModuleCoreConfig.class)
+                .run(ctx -> {
+                    var causeway = ctx.getBean(CausewayConfiguration.Causeway.class);
+                    causewayRef.set(causeway);
+                });
+        });
+        return causewayRef.get();
+    }
+
+}
diff --git a/core/metamodel/pom.xml b/core/metamodel/pom.xml
index 1cd29571c59..e89907887e1 100644
--- a/core/metamodel/pom.xml
+++ b/core/metamodel/pom.xml
@@ -88,6 +88,12 @@
             <artifactId>causeway-core-internaltestsupport</artifactId>
             <scope>test</scope>
         </dependency>
+        
+        <dependency>
+            <groupId>org.apache.causeway.core</groupId>
+            <artifactId>causeway-core-configtest</artifactId>
+            <scope>test</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
index b47c6cd6ba2..6f2b4587790 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
@@ -112,9 +112,9 @@
         CausewayModuleCoreSecurity.class,
 
         // @Configuration's
-        MetaModelContextFactory.class,
         CausewayModuleCoreMetamodelMixins.class,
         CausewayModuleCoreMetamodelConfigurationDefault.class,
+        MetaModelContextFactory.class,
 
         // @Component's
         ProgrammingModelInitFilterDefault.class,
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
index 98d96ba2063..48cbac97d41 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
@@ -24,7 +24,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiFunction;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
@@ -62,7 +61,7 @@
 import org.apache.causeway.commons.internal.collections._Maps;
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.commons.internal.collections._Streams;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.config.beans.CausewayBeanFactoryPostProcessor;
 import org.apache.causeway.core.config.beans.CausewayBeanTypeClassifier;
@@ -217,7 +216,7 @@ public static MetaModelContext buildDefault() {
     private List<ValueSemanticsProvider<?>> valueSemantics;
 
     @Singular
-    private List<_SingletonBeanProvider> singletonProviders;
+    private List<SingletonBeanProvider> singletonProviders;
 
     // -- SERVICE SUPPORT
 
@@ -271,19 +270,19 @@ private Stream<Object> streamSingletons() {
                 .filter(_NullSafe::isPresent);
     }
 
-    Stream<_SingletonBeanProvider> streamBeanAdapters() {
+    Stream<SingletonBeanProvider> streamBeanAdapters() {
         return _Streams.concat(
-                streamSingletons().map(_SingletonBeanProvider::forTesting),
+                streamSingletons().map(SingletonBeanProvider::forTesting),
                 singletonProviders.stream(),
                 discoveredServices.stream(),
                 Stream.of(
                     // support for lazy bean providers,
-                    _SingletonBeanProvider.forTestingLazy(GridLoaderService.class, this::getGridLoaderService),
-                    _SingletonBeanProvider.forTestingLazy(GridService.class, this::getGridService),
-                    _SingletonBeanProvider.forTestingLazy(JaxbService.class, this::getJaxbService),
-                    _SingletonBeanProvider.forTestingLazy(MenuBarsService.class, this::getMenuBarsService),
-                    _SingletonBeanProvider.forTestingLazy(LayoutService.class, this::getLayoutService),
-                    _SingletonBeanProvider.forTestingLazy(SpecificationLoader.class, this::getSpecificationLoader)
+                    SingletonBeanProvider.forTestingLazy(GridLoaderService.class, this::getGridLoaderService),
+                    SingletonBeanProvider.forTestingLazy(GridService.class, this::getGridService),
+                    SingletonBeanProvider.forTestingLazy(JaxbService.class, this::getJaxbService),
+                    SingletonBeanProvider.forTestingLazy(MenuBarsService.class, this::getMenuBarsService),
+                    SingletonBeanProvider.forTestingLazy(LayoutService.class, this::getLayoutService),
+                    SingletonBeanProvider.forTestingLazy(SpecificationLoader.class, this::getSpecificationLoader)
                 )
                 );
     }
@@ -294,15 +293,16 @@ private static CausewaySystemEnvironment newCausewaySystemEnvironment() {
         return env;
     }
 
-    private static CausewayConfiguration newCausewayConfiguration() {
-        var properties = _Maps.<String, String>newHashMap();
-        var config = CausewayConfiguration.builder().environment(new AbstractEnvironment() {
-            @Override
-            public String getProperty(final String key) {
-                return properties.get(key);
+    private static AbstractEnvironment emptyEnvironment() {
+        return new AbstractEnvironment() {
+            @Override public String getProperty(final String key) {
+                return null;
             }
-        }).build();
-        return config;
+        };
+    }
+
+    private static CausewayConfiguration newCausewayConfiguration() {
+        return new CausewayConfiguration(emptyEnvironment(), Optional.empty(), null); //FIXME
     }
 
     @Override
@@ -365,6 +365,7 @@ public CausewayBeanTypeRegistry getCausewayBeanTypeRegistry() {
 
     private final _Lazy<ProgrammingModel> programmingModelRef =
             _Lazy.threadSafe(()->initProgrammingModel());
+    @Override
     public ProgrammingModel getProgrammingModel() {
         return programmingModelRef.get();
     }
@@ -434,27 +435,6 @@ public WebAppContextPath getWebAppContextPath() {
         return new WebAppContextPath();
     }
 
-    public void runWithConfigProperties(final Consumer<Map<String, String>> setup, final Runnable runnable) {
-        var properties = _Maps.<String, String>newHashMap();
-        setup.accept(properties);
-
-        var currentConfigBackup = this.configuration;
-        try {
-
-            this.configuration = CausewayConfiguration.builder().environment(new AbstractEnvironment() {
-                @Override
-                public String getProperty(final String key) {
-                    return properties.get(key);
-                }
-            }).build();
-
-            runnable.run();
-        } finally {
-            this.configuration = currentConfigBackup;
-        }
-
-    }
-
     // -- LAYOUT TESTING SUPPORT
 
     @Getter(lazy = true)
@@ -464,7 +444,7 @@ public String getProperty(final String key) {
     private final MenuBarsService menuBarsService = createMenuBarsService();
     private final MenuBarsService createMenuBarsService() {
         return getSingletonProviders().stream()
-                .filter(_SingletonBeanProvider.satisfying(MenuBarsService.class))
+                .filter(SingletonBeanProvider.satisfying(MenuBarsService.class))
                 .findFirst()
                 .map(provider->(MenuBarsService)provider.getInstanceElseFail())
                 .orElseGet(MenuBarsService::forTesting);
@@ -529,12 +509,12 @@ record ServiceInstance(
     }
 
     @Builder.Default
-    private final Set<_SingletonBeanProvider> discoveredServices = _Sets.newHashSet();
+    private final Set<SingletonBeanProvider> discoveredServices = _Sets.newHashSet();
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     private void registerAsService(final ServiceInstance serviceInstance) {
         var spec = serviceInstance.specification();
-        discoveredServices.add(_SingletonBeanProvider.forTestingLazy(
+        discoveredServices.add(SingletonBeanProvider.forTestingLazy(
                 spec.logicalTypeName(),
                 (Class)spec.getCorrespondingClass(),
                 serviceInstance::pojo));
@@ -563,7 +543,7 @@ private final Map<String, ManagedObject> collectBeansOfKnownSort() {
         return map;
     }
 
-    private Optional<ServiceInstance> toServiceInstance(final _SingletonBeanProvider managedBeanAdapter) {
+    private Optional<ServiceInstance> toServiceInstance(final SingletonBeanProvider managedBeanAdapter) {
         var servicePojo = managedBeanAdapter.getInstanceElseFail();
 
         if(ProgrammingModelConstants.TypeVetoMarker.anyMatchOn(managedBeanAdapter.beanClass())) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/ServiceRegistry_forTesting.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/ServiceRegistry_forTesting.java
index 8b1616bcb5c..bbb968c8c7f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/ServiceRegistry_forTesting.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/ServiceRegistry_forTesting.java
@@ -36,8 +36,8 @@
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.commons.internal.context._Context;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.commons.internal.ioc._IocContainer;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SpringContextHolder;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 
 import lombok.Getter;
@@ -50,8 +50,8 @@ class ServiceRegistry_forTesting implements ServiceRegistry {
 
     @NonNull private final MetaModelContext metaModelContext;
 
-    @Getter @Setter private _IocContainer iocContainer;
-    private final Set<_SingletonBeanProvider> registeredBeans = _Sets.newHashSet();
+    @Getter @Setter private SpringContextHolder iocContainer;
+    private final Set<SingletonBeanProvider> registeredBeans = _Sets.newHashSet();
 
     @Override
     public <T> Can<T> select(final Class<T> type, final Annotation[] qualifiers) {
@@ -66,8 +66,8 @@ public <T> Can<T> select(final Class<T> type, final Annotation[] qualifiers) {
 //        }
 
         Optional<T> match = streamBeans()
-                .filter(_SingletonBeanProvider.satisfying(type))
-                .map(_SingletonBeanProvider::getInstanceElseFail)
+                .filter(SingletonBeanProvider.satisfying(type))
+                .map(SingletonBeanProvider::getInstanceElseFail)
                 .map(_Casts::<T>uncheckedCast)
                 .findFirst();
 
@@ -85,15 +85,15 @@ public <T> Can<T> select(final Class<T> type, final Annotation[] qualifiers) {
         return Can.empty();
     }
 
-    private final Map<String, _SingletonBeanProvider> registeredBeanById = _Maps.newHashMap();
+    private final Map<String, SingletonBeanProvider> registeredBeanById = _Maps.newHashMap();
 
     @Override
-    public Stream<_SingletonBeanProvider> streamRegisteredBeans() {
+    public Stream<SingletonBeanProvider> streamRegisteredBeans() {
         return registeredBeans().stream();
     }
 
     @Override
-    public Optional<_SingletonBeanProvider> lookupRegisteredBeanById(final LogicalType id) {
+    public Optional<SingletonBeanProvider> lookupRegisteredBeanById(final LogicalType id) {
         return Optional.ofNullable(registeredBeanById.get(id.logicalName()));
     }
 
@@ -111,7 +111,7 @@ void invalidateRegisteredBeans() {
 
     // -- HELPER
 
-    private Set<_SingletonBeanProvider> registeredBeans() {
+    private Set<SingletonBeanProvider> registeredBeans() {
 
         AtomicBoolean triggerPostInit = new AtomicBoolean(false);
 
@@ -135,7 +135,7 @@ private Set<_SingletonBeanProvider> registeredBeans() {
         return registeredBeans;
     }
 
-    private Stream<_SingletonBeanProvider> streamBeans() {
+    private Stream<SingletonBeanProvider> streamBeans() {
         // lookup the MetaModelContextBean's list of singletons
         var mmc = metaModelContext;
         if(mmc instanceof MetaModelContext_forTesting) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/HasMetaModelContext.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/HasMetaModelContext.java
index 799a72ff0f8..bb1437ca932 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/HasMetaModelContext.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/HasMetaModelContext.java
@@ -75,7 +75,7 @@ default CausewaySystemEnvironment getSystemEnvironment() {
     default CausewayConfiguration getConfiguration() {
         return getMetaModelContext().getConfiguration();
     }
-    
+
     default ProgrammingModel getProgrammingModel() {
         return getMetaModelContext().getProgrammingModel();
     }
@@ -159,7 +159,7 @@ default MenuBarsService getMenuBarsService() {
     default InteractionService getInteractionService() {
         return getMetaModelContext().getInteractionService();
     }
-    
+
     default CommandDtoFactory getCommandDtoFactory() {
         return getMetaModelContext().getCommandDtoFactory();
     }
@@ -226,15 +226,15 @@ default Stream<ManagedObject> streamServiceAdapters() {
     // -- VIEWER SHORTCUTS
 
     default Common getCommonViewerSettings() {
-        return getConfiguration().getViewer().getCommon();
+        return getConfiguration().viewer().common();
     }
 
     default Wicket getWicketViewerSettings() {
-        return getConfiguration().getViewer().getWicket();
+        return getConfiguration().viewer().wicket();
     }
 
     default Application getApplicationSettings() {
-        return getCommonViewerSettings().getApplication();
+        return getCommonViewerSettings().application();
     }
 
     /**
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContextFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContextFactory.java
index d031de3c42d..4ce0391c72f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContextFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContextFactory.java
@@ -41,8 +41,9 @@ public class MetaModelContextFactory {
     @Bean(destroyMethod = "onDestroy")
     public MetaModelContext metaModelContext(final CausewaySystemEnvironment systemEnvironment) {
 
-        var ioc = systemEnvironment.getIocContainer();
+        var ioc = systemEnvironment.springContextHolder();
         var mmc = new MetaModelContext_usingSpring(ioc);
+        var mmcRecord = new MmcRecord(ioc);
 
         if(isIntegrationTesting()) {
             MetaModelContext.setOrReplace(mmc);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext_usingSpring.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext_usingSpring.java
index 55fd93555c0..d281534e25d 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext_usingSpring.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext_usingSpring.java
@@ -39,8 +39,8 @@
 import org.apache.causeway.applib.services.xactn.TransactionService;
 import org.apache.causeway.commons.internal.base._Lazy;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.commons.internal.ioc._IocContainer;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SpringContextHolder;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
@@ -58,8 +58,8 @@
 
 class MetaModelContext_usingSpring extends MetaModelContext {
 
-    private final _IocContainer iocContainer;
-    public MetaModelContext_usingSpring(final _IocContainer iocContainer) {
+    private final SpringContextHolder iocContainer;
+    public MetaModelContext_usingSpring(final SpringContextHolder iocContainer) {
         this.iocContainer = iocContainer;
     }
 
@@ -211,7 +211,7 @@ private Map<String, ManagedObject> collectBeansOfKnownSort() {
                         LinkedHashMap::new));
     }
 
-    private ManagedObject toManagedObject(final _SingletonBeanProvider managedBeanAdapter) {
+    private ManagedObject toManagedObject(final SingletonBeanProvider managedBeanAdapter) {
         var servicePojo = managedBeanAdapter.getInstanceElseFail();
         return getSpecificationLoader()
                 .specForType(servicePojo.getClass())
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MmcRecord.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MmcRecord.java
new file mode 100644
index 00000000000..22212b3984f
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MmcRecord.java
@@ -0,0 +1,85 @@
+/*
+ *  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.causeway.core.metamodel.context;
+
+import org.apache.causeway.applib.services.factory.FactoryService;
+import org.apache.causeway.applib.services.homepage.HomePageResolverService;
+import org.apache.causeway.applib.services.i18n.TranslationService;
+import org.apache.causeway.applib.services.iactnlayer.InteractionService;
+import org.apache.causeway.applib.services.inject.ServiceInjector;
+import org.apache.causeway.applib.services.menu.MenuBarsService;
+import org.apache.causeway.applib.services.message.MessageService;
+import org.apache.causeway.applib.services.placeholder.PlaceholderRenderService;
+import org.apache.causeway.applib.services.registry.ServiceRegistry;
+import org.apache.causeway.applib.services.repository.RepositoryService;
+import org.apache.causeway.applib.services.title.TitleService;
+import org.apache.causeway.applib.services.wrapper.WrapperFactory;
+import org.apache.causeway.applib.services.xactn.TransactionService;
+import org.apache.causeway.commons.internal.ioc.SpringContextHolder;
+import org.apache.causeway.core.config.CausewayConfiguration;
+import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
+import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
+import org.apache.causeway.core.metamodel.execution.MemberExecutorService;
+import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconService;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
+import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.causeway.core.metamodel.services.command.CommandDtoFactory;
+import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
+import org.apache.causeway.core.security.authentication.manager.AuthenticationManager;
+import org.apache.causeway.core.security.authorization.manager.AuthorizationManager;
+
+public record MmcRecord(
+    CausewaySystemEnvironment systemEnvironment,
+    CausewayConfiguration configuration,
+    ProgrammingModel programmingModel,
+    ServiceInjector serviceInjector,
+    FactoryService factoryService,
+    ServiceRegistry serviceRegistry,
+    SpecificationLoader specificationLoader,
+    TranslationService translationService,
+    AuthorizationManager authorizationManager,
+    AuthenticationManager authenticationManager,
+    ObjectIconService objectIconService,
+    PlaceholderRenderService placeholderRenderService,
+    TitleService titleService,
+    RepositoryService repositoryService,
+    TransactionService transactionService,
+    HomePageResolverService homePageResolverService,
+    ObjectManager objectManager,
+    WrapperFactory wrapperFactory,
+    MemberExecutorService memberExecutor,
+    WebAppContextPath webAppContextPath,
+    MenuBarsService menuBarsService,
+    MessageService messageService,
+    InteractionService interactionService,
+    CommandDtoFactory commandDtoFactory,
+    ManagedObject homePageAdapter) {
+
+    MmcRecord(SpringContextHolder iocContainer) {
+        this(
+            iocContainer.getSingletonElseFail(CausewaySystemEnvironment.class),
+            iocContainer.getSingletonElseFail(CausewayConfiguration.class),
+            null,//iocContainer.getSingletonElseFail(ProgrammingModel.class),
+            null,//iocContainer.getSingletonElseFail(ServiceInjector.class),
+            null,//iocContainer.getSingletonElseFail(FactoryService.class),
+            null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/DefaultViewFacetAsConfigured.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/DefaultViewFacetAsConfigured.java
index 4042dc972f6..ba7c6fa0867 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/DefaultViewFacetAsConfigured.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/DefaultViewFacetAsConfigured.java
@@ -28,8 +28,8 @@ public class DefaultViewFacetAsConfigured
 
     public static DefaultViewFacet create(
             final FacetHolder holder) {
-        final String defaultView = holder.getConfiguration().getApplib().getAnnotation()
-                .getCollectionLayout().getDefaultView().toNameLower();
+        final String defaultView = holder.getConfiguration().applib().annotation()
+                .collectionLayout().defaultView().toNameLower();
         return new DefaultViewFacetAsConfigured(_Strings.nullToEmpty(defaultView), holder);
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/tabledec/TableDecoratorFacetForCollectionLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/tabledec/TableDecoratorFacetForCollectionLayoutAnnotation.java
index 6aff4c62452..736f670bcca 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/tabledec/TableDecoratorFacetForCollectionLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/collections/layout/tabledec/TableDecoratorFacetForCollectionLayoutAnnotation.java
@@ -50,7 +50,7 @@ public static Optional<TableDecoratorFacet> create(
         })
         ,
         () -> new TableDecoratorFacetFromConfiguration(
-                holder.getConfiguration().getApplib().getAnnotation().getCollectionLayout().getTableDecorator(), holder));
+                holder.getConfiguration().applib().annotation().collectionLayout().tableDecorator(), holder));
 
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/fallback/FallbackFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/fallback/FallbackFacetFactory.java
index 65cc945d098..743758bb4ac 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/fallback/FallbackFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/fallback/FallbackFacetFactory.java
@@ -69,7 +69,7 @@ public void process(final ProcessClassContext processClassContext) {
 
         addFacet(new TitleFacetNone(facetHolder));
         addFacet(new PagedFacetFromConfiguration(
-                        getConfiguration().getApplib().getAnnotation().getDomainObjectLayout().getPaged(),
+                        getConfiguration().applib().annotation().domainObjectLayout().paged(),
                         facetHolder));
     }
 
@@ -85,7 +85,7 @@ public void process(final ProcessMethodContext processMethodContext) {
             addFacet(new MaxLengthFacetUnlimited(facetedMethod));
             addFacet(new MultiLineFacetNone(facetedMethod));
             addFacet(new LabelAtFacetFromLayoutConfiguration(
-                    getConfiguration().getApplib().getAnnotation().getPropertyLayout().getLabelPosition(),
+                    getConfiguration().applib().annotation().propertyLayout().labelPosition(),
                     facetedMethod));
         }
         if (featureType.isAction()) {
@@ -94,7 +94,7 @@ public void process(final ProcessMethodContext processMethodContext) {
         if (featureType.isCollection()) {
             addFacet(
                     new PagedFacetFromConfiguration(
-                            getConfiguration().getApplib().getAnnotation().getCollectionLayout().getPaged(),
+                            getConfiguration().applib().annotation().collectionLayout().paged(),
                             facetedMethod));
         }
 
@@ -107,7 +107,7 @@ public void processParams(final ProcessParameterContext processParameterContext)
             addFacet(new MultiLineFacetNone(typedHolder));
             addFacet(new MaxLengthFacetUnlimited(typedHolder));
             addFacet(new LabelAtFacetFromLayoutConfiguration(
-                    getConfiguration().getApplib().getAnnotation().getParameterLayout().getLabelPosition(),
+                    getConfiguration().applib().annotation().parameterLayout().labelPosition(),
                     typedHolder));
         }
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/jaxb/JaxbFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/jaxb/JaxbFacetFactory.java
index 7498d31a65e..9e0e1577f62 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/jaxb/JaxbFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/jaxb/JaxbFacetFactory.java
@@ -186,13 +186,13 @@ public void refineProgrammingModel(final ProgrammingModel programmingModel) {
     private List<TypeValidator> getTypeValidators(final CausewayConfiguration configuration) {
 
         final List<TypeValidator> typeValidators = _Lists.newArrayList();
-        if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isNotAbstract()) {
+        if(configuration.core().metaModel().validator().jaxbViewModel().notAbstract()) {
             typeValidators.add(new JaxbViewModelNotAbstractValidator());
         }
-        if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isNotInnerClass()) {
+        if(configuration.core().metaModel().validator().jaxbViewModel().notInnerClass()) {
             typeValidators.add(new JaxbViewModelNotInnerClassValidator());
         }
-        if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isNoArgConstructor()) {
+        if(configuration.core().metaModel().validator().jaxbViewModel().noArgConstructor()) {
             typeValidators.add(new JaxbViewModelPublicNoArgConstructorValidator());
         }
         return typeValidators;
@@ -200,10 +200,10 @@ private List<TypeValidator> getTypeValidators(final CausewayConfiguration config
 
     private List<AssociationValidator> getAssociationValidators(final CausewayConfiguration configuration) {
         final List<AssociationValidator> associationValidators = _Lists.newArrayList();
-        if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isReferenceTypeAdapter()) {
+        if(configuration.core().metaModel().validator().jaxbViewModel().referenceTypeAdapter()) {
             associationValidators.add(new PropertyValidatorForReferenceTypes());
         }
-        if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isDateTimeTypeAdapter()) {
+        if(configuration.core().metaModel().validator().jaxbViewModel().dateTimeTypeAdapter()) {
             associationValidators.add(new PropertyValidatorForDateTypes(java.sql.Timestamp.class));
             associationValidators.add(new PropertyValidatorForDateTypes(ZonedDateTime.class));
             associationValidators.add(new PropertyValidatorForDateTypes(OffsetDateTime.class));
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/cssclass/annotprop/CssClassFacetOnActionFromConfiguredRegex.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/cssclass/annotprop/CssClassFacetOnActionFromConfiguredRegex.java
index 5aeb285d092..2a54de63e57 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/cssclass/annotprop/CssClassFacetOnActionFromConfiguredRegex.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/cssclass/annotprop/CssClassFacetOnActionFromConfiguredRegex.java
@@ -43,8 +43,8 @@ private CssClassFacetOnActionFromConfiguredRegex(final String css, final FacetHo
     private static Optional<String> cssIfAnyFor(
             final String name, final CausewayConfiguration causewayConfiguration) {
 
-        var cssClassByPattern = causewayConfiguration.getApplib().getAnnotation().getActionLayout()
-                .getCssClass().getPatternsAsMap();
+        var cssClassByPattern = causewayConfiguration.applib().annotation().actionLayout()
+                .cssClass().patternsAsMap();
 
         for (Map.Entry<Pattern, String> entry : cssClassByPattern.entrySet()) {
             final Pattern pattern = entry.getKey();
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/annotprop/FaFacetOnMemberFromConfiguredRegex.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/annotprop/FaFacetOnMemberFromConfiguredRegex.java
index 2304595ff8c..a279a8a7251 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/annotprop/FaFacetOnMemberFromConfiguredRegex.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/annotprop/FaFacetOnMemberFromConfiguredRegex.java
@@ -76,7 +76,7 @@ private FaFacetOnMemberFromConfiguredRegex(
         super(facetHolder);
         this.objectSpecification = objectSpecification;
         this.faIconByPattern = getConfiguration()
-                .getApplib().getAnnotation().getActionLayout().getCssClassFa().getPatternsAsMap();
+                .applib().annotation().actionLayout().cssClassFa().patternsAsMap();
         this.memberNamedFacet = memberNamedFacet;
 
         // an optimization, not strictly required
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java
index 6c42a4f5b73..b0dcfde8972 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactory.java
@@ -46,7 +46,7 @@ public void process(final ProcessClassContext processClassContext) {
             return;
         }
 
-        if(getConfiguration().getApplib().getAnnotation().getViewModel().getValidation().getSemanticChecking().isEnable()) {
+        if(getConfiguration().applib().annotation().viewModel().validation().semanticChecking().enable()) {
             checkViewModelSemantics(processClassContext);
         }
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
index e41198bea92..ca87146bac1 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
@@ -399,7 +399,7 @@ private void processLifecycleEventCreated(
                 lifecycleEvent,
                 ObjectCreatedEvent.Noop.class,
                 ObjectCreatedEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getCreatedLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().createdLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new CreatedLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -417,7 +417,7 @@ private void processLifecycleEventLoaded(
                 lifecycleEvent,
                 ObjectLoadedEvent.Noop.class,
                 ObjectLoadedEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getLoadedLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().loadedLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new LoadedLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -435,7 +435,7 @@ private void processLifecycleEventPersisting(
                 lifecycleEvent,
                 ObjectPersistingEvent.Noop.class,
                 ObjectPersistingEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getPersistingLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().persistingLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new PersistingLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -453,7 +453,7 @@ private void processLifecycleEventPersisted(
                 lifecycleEvent,
                 ObjectPersistedEvent.Noop.class,
                 ObjectPersistedEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getPersistedLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().persistedLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new PersistedLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -471,7 +471,7 @@ private void processLifecycleEventRemoving(
                 lifecycleEvent,
                 ObjectRemovingEvent.Noop.class,
                 ObjectRemovingEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getRemovingLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().removingLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new RemovingLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -489,7 +489,7 @@ private void processLifecycleEventUpdated(
                 lifecycleEvent,
                 ObjectUpdatedEvent.Noop.class,
                 ObjectUpdatedEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getUpdatedLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().updatedLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new UpdatedLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -507,7 +507,7 @@ private void processLifecycleEventUpdating(
                 lifecycleEvent,
                 ObjectUpdatingEvent.Noop.class,
                 ObjectUpdatingEvent.Default.class,
-                getConfiguration().getApplib().getAnnotation().getDomainObject().getUpdatingLifecycleEvent().isPostForDefault())
+                getConfiguration().applib().annotation().domainObject().updatingLifecycleEvent().postForDefault())
                 )
         .map(lifecycleEvent -> new UpdatingLifecycleEventFacetForDomainObjectAnnotation(
                 holder, lifecycleEvent))
@@ -632,7 +632,7 @@ private Can<ObjectSpecification> proxiesIn(final @Nullable List<ObjectSpecificat
                 }
 
                 private boolean logicalTypeNameIsNotIncludedInAliased(final ObjectSpecification objectSpecification) {
-                    if (getConfiguration().getCore().getMetaModel().getValidator().isAllowLogicalTypeNameAsAlias()) {
+                    if (getConfiguration().core().metaModel().validator().allowLogicalTypeNameAsAlias()) {
                         return objectSpecification.getAliases()
                                 .map(LogicalType::logicalName).stream()
                                 .noneMatch(name -> objectSpecification.logicalTypeName().equals(name));
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/editing/ImmutableFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/editing/ImmutableFacetForDomainObjectAnnotation.java
index 4077eb56ee6..8dae55c278a 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/editing/ImmutableFacetForDomainObjectAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/editing/ImmutableFacetForDomainObjectAnnotation.java
@@ -41,7 +41,7 @@ public static Optional<ImmutableFacet> create(
             final FacetHolder holder) {
 
         final boolean editingDisabledByDefault =
-                configuration.getApplib().getAnnotation().getDomainObject().getEditing()
+                configuration.applib().annotation().domainObject().editing()
                 == DomainObjectConfigOptions.EditingObjectsConfiguration.FALSE;
 
         if(domainObjectIfAny.isPresent()) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClassUiEvent.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClassUiEvent.java
index 7d5e34814f2..d773b10a178 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClassUiEvent.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClassUiEvent.java
@@ -47,8 +47,8 @@ public static Optional<CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClass
                 cssClassUiEventClass,
                 CssClassUiEvent.Noop.class,
                 CssClassUiEvent.Default.class,
-                facetHolder.getConfiguration().getApplib().getAnnotation()
-                    .getDomainObjectLayout().getCssClassUiEvent().isPostForDefault()))
+                facetHolder.getConfiguration().applib().annotation()
+                    .domainObjectLayout().cssClassUiEvent().postForDefault()))
         .map(cssClassUiEventClass -> {
             return new CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClassUiEvent(
                     cssClassUiEventClass, metamodelEventService, facetHolder);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
index ad6e1d18499..4eb73f94e2e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
@@ -47,8 +47,8 @@ public static Optional<IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent>
                         iconUiEvent,
                         IconUiEvent.Noop.class,
                         IconUiEvent.Default.class,
-                        facetHolder.getConfiguration().getApplib().getAnnotation()
-                            .getDomainObjectLayout().getIconUiEvent().isPostForDefault()))
+                        facetHolder.getConfiguration().applib().annotation()
+                            .domainObjectLayout().iconUiEvent().postForDefault()))
                 .map(iconUiEvent -> {
                     return new IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent(
                             iconUiEvent, metamodelEventService, facetHolder);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/TitleFacetViaDomainObjectLayoutAnnotationUsingTitleUiEvent.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/TitleFacetViaDomainObjectLayoutAnnotationUsingTitleUiEvent.java
index 3ce94788718..1ffa0c9df44 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/TitleFacetViaDomainObjectLayoutAnnotationUsingTitleUiEvent.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/TitleFacetViaDomainObjectLayoutAnnotationUsingTitleUiEvent.java
@@ -47,11 +47,11 @@ public static Optional<TitleFacetViaDomainObjectLayoutAnnotationUsingTitleUiEven
             final FacetHolder facetHolder) {
 
         var isPostForDefault = facetHolder.getConfiguration()
-                .getApplib()
-                .getAnnotation()
-                .getDomainObjectLayout()
-                .getTitleUiEvent()
-                .isPostForDefault();
+                .applib()
+                .annotation()
+                .domainObjectLayout()
+                .titleUiEvent()
+                .postForDefault();
 
         return domainObjectLayoutIfAny
                 .map(DomainObjectLayout::titleUiEvent)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/tabledec/TableDecoratorFacetForDomainObjectLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/tabledec/TableDecoratorFacetForDomainObjectLayoutAnnotation.java
index d06c78e9ac1..748c0b4cdad 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/tabledec/TableDecoratorFacetForDomainObjectLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/tabledec/TableDecoratorFacetForDomainObjectLayoutAnnotation.java
@@ -49,7 +49,7 @@ public static Optional<TableDecoratorFacet> create(
                         tableDecorator, holder))
         ,
         () -> new TableDecoratorFacetFromConfiguration(
-                holder.getConfiguration().getApplib().getAnnotation().getDomainObjectLayout().getTableDecorator(),
+                holder.getConfiguration().applib().annotation().domainObjectLayout().tableDecorator(),
                 holder));
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java
index 31686fccc1d..8158b1e93c3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java
@@ -44,7 +44,7 @@ public RemoveAnnotatedMethodsFacetFactory(final MetaModelContext mmc) {
     @Override
     public void process(final ProcessClassContext processClassContext) {
 
-        var policy = getMetaModelContext().getConfiguration().getCore().getMetaModel().getIntrospector().getPolicy();
+        var policy = getMetaModelContext().getConfiguration().core().metaModel().introspector().policy();
         switch (policy) {
             case ENCAPSULATION_ENABLED:
                 getClassCache()
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/introspection/IntrospectionPolicyFacetAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/introspection/IntrospectionPolicyFacetAbstract.java
index e926139c00c..573497ad279 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/introspection/IntrospectionPolicyFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/introspection/IntrospectionPolicyFacetAbstract.java
@@ -54,14 +54,14 @@ protected IntrospectionPolicyFacetAbstract(
         super(IntrospectionPolicyFacetAbstract.type(), holder, precedence);
         this.introspection = introspection;
     }
-    
+
     @Override
     public final IntrospectionPolicy getIntrospectionPolicy() {
         return switch(introspection) {
             case ENCAPSULATION_ENABLED -> IntrospectionPolicy.ENCAPSULATION_ENABLED;
             case ANNOTATION_OPTIONAL -> IntrospectionPolicy.ANNOTATION_OPTIONAL;
             case ANNOTATION_REQUIRED -> IntrospectionPolicy.ANNOTATION_REQUIRED;
-            case AS_CONFIGURED -> getConfiguration().getCore().getMetaModel().getIntrospector().getPolicy();
+            case AS_CONFIGURED -> getConfiguration().core().metaModel().introspector().policy();
             case NOT_SPECIFIED -> throw _Exceptions.unexpectedCodeReach(); // there must be no such a facet that returns such a case
         };
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/layout/LayoutPrefixFacetForUiEvent.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/layout/LayoutPrefixFacetForUiEvent.java
index 0453773c734..0093b5e6372 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/layout/LayoutPrefixFacetForUiEvent.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/layout/LayoutPrefixFacetForUiEvent.java
@@ -55,8 +55,8 @@ public static Optional<LayoutPrefixFacetForUiEvent> create(
                         layoutUiEvent,
                         LayoutUiEvent.Noop.class,
                         LayoutUiEvent.Default.class,
-                        facetHolder.getConfiguration().getApplib().getAnnotation()
-                            .getDomainObjectLayout().getLayoutUiEvent().isPostForDefault()))
+                        facetHolder.getConfiguration().applib().annotation()
+                            .domainObjectLayout().layoutUiEvent().postForDefault()))
                 .map(layoutUiEvent -> new LayoutPrefixFacetForUiEvent("DomainObjectLayoutAnnotationWithLayoutUiEvent",
                     _Casts.uncheckedCast(layoutUiEvent), metamodelEventService,
                     facetHolder, Precedence.EVENT));
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/logicaltype/classname/LogicalTypeFacetFromClassNameFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/logicaltype/classname/LogicalTypeFacetFromClassNameFactory.java
index 7ea949625bd..d77c815aa3e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/logicaltype/classname/LogicalTypeFacetFromClassNameFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/logicaltype/classname/LogicalTypeFacetFromClassNameFactory.java
@@ -65,7 +65,7 @@ public void process(final ProcessClassContext processClassContext) {
 
     @Override
     public void refineProgrammingModel(final ProgrammingModel programmingModel) {
-        var shouldCheck = getConfiguration().getCore().getMetaModel().getValidator().isExplicitLogicalTypeNames();
+        var shouldCheck = getConfiguration().core().metaModel().validator().explicitLogicalTypeNames();
         if(!shouldCheck) return;
 
         programmingModel.addValidatorSkipManagedBeans(objectSpec-> {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/promptStyle/PromptStyleFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/promptStyle/PromptStyleFacet.java
index ad91b4910a4..9502993b83c 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/promptStyle/PromptStyleFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/promptStyle/PromptStyleFacet.java
@@ -42,7 +42,7 @@ public static PromptStyleFacet compositeValueEdit(final FacetHolder facetHolder)
        return new PromptStyleFacet("CompositeValueEdit", PromptStyle.INLINE_AS_IF_EDIT, facetHolder, Precedence.SYNTHESIZED, false);
    }
    public static PromptStyleFacet asConfgured(final CausewayConfiguration configuration, final FacetHolder facetHolder) {
-       return new PromptStyleFacet("Configuration", configuration.getViewer().getWicket().getPromptStyle(), facetHolder, Precedence.DEFAULT, false);
+       return new PromptStyleFacet("Configuration", configuration.viewer().wicket().promptStyle(), facetHolder, Precedence.DEFAULT, false);
    }
 
    @Override public Class<? extends Facet> facetType() { return getClass(); }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacetAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacetAbstract.java
index 7cadccfdd19..a6dc01764c0 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/propcoll/accessor/PropertyOrCollectionAccessorFacetAbstract.java
@@ -54,7 +54,7 @@ public void visitAttributes(final BiConsumer<String, Object> visitor) {
     }
 
     protected final boolean isConfiguredToFilterForVisibility() {
-        return getConfiguration().getCore().getMetaModel().isFilterVisibility();
+        return getConfiguration().core().metaModel().filterVisibility();
     }
 
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/value/semantics/ValueSemanticsAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/value/semantics/ValueSemanticsAnnotationFacetFactory.java
index 9d6287ebd93..1901f5b48c6 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/value/semantics/ValueSemanticsAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/value/semantics/ValueSemanticsAnnotationFacetFactory.java
@@ -122,7 +122,7 @@ private void processDigits(
                         MinFractionalDigitsFacetFromValueSemanticsAnnotation
                                 .create(valueSemanticsIfAny, facetHolder),
                         // support for @jakarta.validation.constraints.Digits (if supported)
-                        getConfiguration().getValueTypes().getBigDecimal().isUseScaleForMinFractionalFacet()
+                        getConfiguration().valueTypes().bigDecimal().useScaleForMinFractionalFacet()
                                 ? MinFractionalDigitsFacetFromJavaxValidationDigitsAnnotation
                                             .create(digitsIfAny, facetHolder)
                                 : Optional.empty()
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java
index e149a2eadf4..22915540618 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java
@@ -185,7 +185,7 @@ private CausewayConfiguration.Prototyping.IfHiddenPolicy determineIfHiddenPolicy
         DeploymentType deploymentType = ownerAdapter.getSystemEnvironment().getDeploymentType();
         switch (deploymentType) {
             case PROTOTYPING:
-                return ownerAdapter.getConfiguration().getPrototyping().getIfHiddenPolicy();
+                return ownerAdapter.getConfiguration().prototyping().ifHiddenPolicy();
             case PRODUCTION:
             default:
                 return CausewayConfiguration.Prototyping.IfHiddenPolicy.HIDE;
@@ -196,7 +196,7 @@ private CausewayConfiguration.Prototyping.IfDisabledPolicy determineIfDisabledPo
         DeploymentType deploymentType = ownerAdapter.getSystemEnvironment().getDeploymentType();
         switch (deploymentType) {
             case PROTOTYPING:
-                return ownerAdapter.getConfiguration().getPrototyping().getIfDisabledPolicy();
+                return ownerAdapter.getConfiguration().prototyping().ifDisabledPolicy();
             case PRODUCTION:
             default:
                 return CausewayConfiguration.Prototyping.IfDisabledPolicy.DISABLE;
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEventUtils.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEventUtils.java
index 4a9157fdc8a..1546cd3f3c3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEventUtils.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmEventUtils.java
@@ -50,21 +50,21 @@ public boolean isDomainEventPostable(final CausewayConfiguration config, final C
                     eventType,
                     ActionDomainEvent.Noop.class,
                     ActionDomainEvent.Default.class,
-                    config.getApplib().getAnnotation().getAction().getDomainEvent().isPostForDefault());
+                    config.applib().annotation().action().domainEvent().postForDefault());
         }
         if(PropertyDomainEvent.class.isAssignableFrom(eventType)) {
             return MmEventUtils.eventTypeIsPostable(
                     eventType,
                     PropertyDomainEvent.Noop.class,
                     PropertyDomainEvent.Default.class,
-                    config.getApplib().getAnnotation().getProperty().getDomainEvent().isPostForDefault());
+                    config.applib().annotation().property().domainEvent().postForDefault());
         }
         if(CollectionDomainEvent.class.isAssignableFrom(eventType)) {
             return MmEventUtils.eventTypeIsPostable(
                     eventType,
                     CollectionDomainEvent.Noop.class,
                     CollectionDomainEvent.Default.class,
-                    config.getApplib().getAnnotation().getCollection().getDomainEvent().isPostForDefault());
+                    config.applib().annotation().collection().domainEvent().postForDefault());
         }
         return false;
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectLoaderFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectLoaderFactory.java
index 904f10deb03..00f1a4bed7a 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectLoaderFactory.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectLoaderFactory.java
@@ -22,7 +22,7 @@
 
 import org.apache.causeway.commons.handler.ChainOfResponsibility;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 import org.apache.causeway.core.metamodel.facets.object.value.ValueSerializer.Format;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ProtoObject;
@@ -48,7 +48,7 @@ public ManagedObject handle(final ProtoObject objectLoadRequest) {
                 var logicalType = spec.logicalType();
                 var servicePojo = spec.getServiceRegistry()
                     .lookupRegisteredBeanById(logicalType)
-                    .flatMap(_SingletonBeanProvider::lookupInstance)
+                    .flatMap(SingletonBeanProvider::lookupInstance)
                     .orElseThrow(()->_Exceptions.noSuchElement(
                             "loader: %s loading logicalType %s",
                             this.getClass().getName(), logicalType));
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelAbstract.java
index 2d7b8cdd886..4f9a6ea5d3f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelAbstract.java
@@ -24,33 +24,28 @@
 import java.util.Objects;
 import java.util.stream.Stream;
 
+import org.apache.causeway.applib.services.inject.ServiceInjector;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.collections._Multimaps;
 import org.apache.causeway.commons.internal.collections._Multimaps.SetMultimap;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
 import org.apache.causeway.core.metamodel.facets.FacetFactory;
 import org.apache.causeway.core.metamodel.postprocessors.MetaModelPostProcessor;
 import org.apache.causeway.core.metamodel.specloader.validator.MetaModelValidator;
 
-import lombok.Getter;
-
 public abstract class ProgrammingModelAbstract
 implements
-    ProgrammingModel,
-    HasMetaModelContext {
+    ProgrammingModel {
 
-    @Getter(onMethod_ = {@Override})
-    private final MetaModelContext metaModelContext;
+    private final ServiceInjector serviceInjector;
 
     private List<FacetFactory> unmodifiableFactories;
     private List<MetaModelValidator> unmodifiableValidators;
     private List<MetaModelPostProcessor> unmodifiablePostProcessors;
 
-    protected ProgrammingModelAbstract(final MetaModelContext metaModelContext) {
-        this.metaModelContext = metaModelContext;
+    protected ProgrammingModelAbstract(final ServiceInjector serviceInjector) {
+        this.serviceInjector = serviceInjector;
     }
 
     /**
@@ -61,18 +56,18 @@ public void init(final ProgrammingModelInitFilter filter) {
         if(isInitialized()) return;
 
         // for all registered facet-factories that also implement MetaModelRefiner
-        for (var facetFactory : snapshotFactories(filter, metaModelContext)) {
+        for (var facetFactory : snapshotFactories(filter)) {
             if(facetFactory instanceof MetaModelRefiner metaModelRefiner) {
                 metaModelRefiner.refineProgrammingModel(this);
             }
         }
 
         this.unmodifiableFactories =
-                Collections.unmodifiableList(snapshotFactories(filter, metaModelContext));
+                Collections.unmodifiableList(snapshotFactories(filter));
         this.unmodifiableValidators =
-                Collections.unmodifiableList(snapshotValidators(filter, metaModelContext));
+                Collections.unmodifiableList(snapshotValidators(filter));
         this.unmodifiablePostProcessors =
-                Collections.unmodifiableList(snapshotPostProcessors(filter, metaModelContext));
+                Collections.unmodifiableList(snapshotPostProcessors(filter));
     }
 
     // -- SETUP
@@ -84,7 +79,7 @@ public <T extends FacetFactory> void addFactory(
             final Marker ... markers) {
 
         assertNotInitialized();
-        metaModelContext.getServiceInjector().injectServicesInto(instance);
+        serviceInjector.injectServicesInto(instance);
         var factoryEntry = new ProgrammingModelEntry<>(instance, markers);
         factoryEntriesByOrder.putElement(order, factoryEntry);
     }
@@ -96,7 +91,7 @@ public <T extends MetaModelValidator> void addValidator(
             final Marker... markers) {
 
         assertNotInitialized();
-        metaModelContext.getServiceInjector().injectServicesInto(instance);
+        serviceInjector.injectServicesInto(instance);
         var validatorEntry = new ProgrammingModelEntry<>(instance, markers);
         validatorEntriesByOrder.putElement(order, validatorEntry);
     }
@@ -108,7 +103,7 @@ public <T extends MetaModelPostProcessor> void addPostProcessor(
             final Marker... markers) {
 
         assertNotInitialized();
-        metaModelContext.getServiceInjector().injectServicesInto(instance);
+        serviceInjector.injectServicesInto(instance);
         var postProcessorEntry = new ProgrammingModelEntry<>(instance, markers);
         postProcessorEntriesByOrder.putElement(order, postProcessorEntry);
     }
@@ -156,8 +151,7 @@ public final int hashCode() {
         factoryEntriesByOrder = _Multimaps.newSetMultimap(LinkedHashSet::new);
 
     private List<FacetFactory> snapshotFactories(
-            final ProgrammingModelInitFilter filter,
-            final MetaModelContext metaModelContext) {
+            final ProgrammingModelInitFilter filter) {
 
         var factories = _Lists.<FacetFactory>newArrayList();
         for(var order : FacetProcessingOrder.values()) {
@@ -176,8 +170,7 @@ private List<FacetFactory> snapshotFactories(
         validatorEntriesByOrder = _Multimaps.newSetMultimap(LinkedHashSet::new);
 
     private List<MetaModelValidator> snapshotValidators(
-            final ProgrammingModelInitFilter filter,
-            final MetaModelContext metaModelContext) {
+            final ProgrammingModelInitFilter filter) {
 
         var validators = _Lists.<MetaModelValidator>newArrayList();
         for(var order : ValidationOrder.values()) {
@@ -197,8 +190,7 @@ private List<MetaModelValidator> snapshotValidators(
         postProcessorEntriesByOrder = _Multimaps.newSetMultimap(LinkedHashSet::new);
 
     private List<MetaModelPostProcessor> snapshotPostProcessors(
-            final ProgrammingModelInitFilter filter,
-            final MetaModelContext metaModelContext) {
+            final ProgrammingModelInitFilter filter) {
 
         var postProcessors = _Lists.<MetaModelPostProcessor>newArrayList();
         for(var order : PostProcessingOrder.values()) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelInitFilterDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelInitFilterDefault.java
index 43c7c310624..3adee4dbe62 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelInitFilterDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodel/ProgrammingModelInitFilterDefault.java
@@ -50,7 +50,7 @@ public class ProgrammingModelInitFilterDefault implements ProgrammingModelInitFi
     @PostConstruct
     public void init() {
         var isIgnoreDeprecated =
-                configuration.getCore().getMetaModel().getProgrammingModel().isIgnoreDeprecated();
+                configuration.core().metaModel().programmingModel().ignoreDeprecated();
 
         this.filterOnMarker = isIgnoreDeprecated
                 ? excluding(EnumSet.of(ProgrammingModel.Marker.DEPRECATED))
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
index e0b17c8671c..d286c6f943a 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
@@ -109,7 +109,7 @@ public void onMetamodelLoaded() {
 
     private boolean isEagerInitialize() {
         ApplicationFeaturesInitConfiguration setting =
-                configuration.getCore().getRuntimeServices().getApplicationFeatures().getInit();
+                configuration.core().runtimeServices().applicationFeatures().init();
         return setting == ApplicationFeaturesInitConfiguration.EAGER
                 || setting == ApplicationFeaturesInitConfiguration.EAGERLY;
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/registry/ServiceRegistryDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/registry/ServiceRegistryDefault.java
index 4b0a698a6e7..1c63f466b6f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/registry/ServiceRegistryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/registry/ServiceRegistryDefault.java
@@ -37,7 +37,7 @@
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.internal.base._Lazy;
 import org.apache.causeway.commons.internal.collections._Maps;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 import org.apache.causeway.core.config.beans.CausewayBeanTypeRegistry;
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
@@ -58,23 +58,23 @@ public final class ServiceRegistryDefault implements ServiceRegistry {
     @Inject private CausewayBeanTypeRegistry causewayBeanTypeRegistry;
 
     @Override
-    public Optional<_SingletonBeanProvider> lookupRegisteredBeanById(final LogicalType id) {
+    public Optional<SingletonBeanProvider> lookupRegisteredBeanById(final LogicalType id) {
         return Optional.ofNullable(contributingDomainServicesById.get().get(id.logicalName()));
     }
 
     @Override
     public Optional<?> lookupBeanById(final String id) {
-        return causewaySystemEnvironment.getIocContainer().lookupBean(id);
+        return causewaySystemEnvironment.springContextHolder().lookupBean(id);
     }
 
     @Override
-    public Stream<_SingletonBeanProvider> streamRegisteredBeans() {
+    public Stream<SingletonBeanProvider> streamRegisteredBeans() {
         return contributingDomainServicesById.get().values().stream();
     }
 
     @Override
     public <T> Can<T> select(final Class<T> type, final Annotation[] qualifiers) {
-        var iocContainer = causewaySystemEnvironment.getIocContainer();
+        var iocContainer = causewaySystemEnvironment.springContextHolder();
         return iocContainer
                 .select(type, qualifiers);
     }
@@ -86,22 +86,22 @@ public void clearRegisteredBeans() {
 
     // -- HELPER
 
-    private final _Lazy<Map<String, _SingletonBeanProvider>> contributingDomainServicesById =
+    private final _Lazy<Map<String, SingletonBeanProvider>> contributingDomainServicesById =
             _Lazy.threadSafe(this::enumerateContributingDomainServices);
 
-    private Map<String, _SingletonBeanProvider> enumerateContributingDomainServices() {
-        var managedBeanAdapterByName = _Maps.<String, _SingletonBeanProvider>newHashMap();
+    private Map<String, SingletonBeanProvider> enumerateContributingDomainServices() {
+        var managedBeanAdapterByName = _Maps.<String, SingletonBeanProvider>newHashMap();
 
-        causewaySystemEnvironment.getIocContainer()
-        .streamAllBeans()
-        .filter(contributes())
-        .forEach(singletonProvider->
-            managedBeanAdapterByName.put(singletonProvider.id(), singletonProvider));
+        causewaySystemEnvironment.springContextHolder()
+            .streamAllBeans()
+            .filter(contributes())
+            .forEach(singletonProvider->
+                managedBeanAdapterByName.put(singletonProvider.id(), singletonProvider));
 
         return managedBeanAdapterByName;
     }
 
-    private Predicate<_SingletonBeanProvider> contributes() {
+    private Predicate<SingletonBeanProvider> contributes() {
         return singletonProvider->singletonProvider!=null
                 ? causewayBeanTypeRegistry.containsManagedBeansContributing(singletonProvider.beanClass())
                 // do not register unknown sort
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
index 58c731c2243..8457fe035ca 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
@@ -287,8 +287,8 @@ default PromptStyle getPromptStyle() {
         if(needsFallback) {
             // modal vs side-bar
             var dialogModeAsConfigured = Optional.ofNullable(
-                    getMetaModelContext().getConfiguration().getViewer().getWicket().getDialogMode())
-                    .orElseGet(()->new Wicket().getDialogMode());
+                    getMetaModelContext().getConfiguration().viewer().wicket().dialogMode())
+                    .orElseGet(()->Wicket.defaults().dialogMode());
             switch (dialogModeAsConfigured) {
             case SIDEBAR:
                 return PromptStyle.DIALOG_SIDEBAR;
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/CausewayModuleCoreMetamodelConfigurationDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/CausewayModuleCoreMetamodelConfigurationDefault.java
index a35592fcb68..f5b3c9353cb 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/CausewayModuleCoreMetamodelConfigurationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/CausewayModuleCoreMetamodelConfigurationDefault.java
@@ -33,7 +33,7 @@
 
 import lombok.extern.slf4j.Slf4j;
 
-@Configuration
+@Configuration(proxyBeanMethods = false)
 @Import({
     SpecificationLoaderDefault.class,
 })
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
index 21575ddeea1..49a487cd84c 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
@@ -165,7 +165,7 @@ public ObjectSpecificationDefault(
         // naturally supports attribute inheritance from the type's hierarchy
         this.introspectionPolicy = this.lookupFacet(IntrospectionPolicyFacet.class)
                 .map(introspectionPolicyFacet->introspectionPolicyFacet.getIntrospectionPolicy())
-                .orElseGet(()->mmc.getConfiguration().getCore().getMetaModel().getIntrospector().getPolicy());
+                .orElseGet(()->mmc.getConfiguration().core().metaModel().introspector().policy());
 
         this.facetedMethodsBuilder =
                 new FacetedMethodsBuilder(this, facetProcessor, classSubstitutorRegistry);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ProgrammingModelDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ProgrammingModelDefault.java
index 40a398b1f18..5ce2f3c7c2f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ProgrammingModelDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ProgrammingModelDefault.java
@@ -106,7 +106,7 @@ final class ProgrammingModelDefault
 extends ProgrammingModelAbstract {
 
     ProgrammingModelDefault(final MetaModelContext mmc, final Iterable<MetaModelRefiner> refiners) {
-        super(mmc);
+        super(mmc.getServiceInjector());
 
         // acts on the peer objects (FacetedMethod etc), rather than ObjectMembers etc
         addFacetFactories();
@@ -120,13 +120,13 @@ final class ProgrammingModelDefault
         for (var metaModelRefiner : refiners) {
             metaModelRefiner.refineProgrammingModel(this);
         }
-        
-        this.mixinNamingStrategy = new _MixedInMemberNamingStrategy();    
+
+        this.mixinNamingStrategy = new _MixedInMemberNamingStrategy();
     }
-    
+
     @Getter(onMethod_={@Override}) @Accessors(fluent=true)
     private final MixinNamingStrategy mixinNamingStrategy;
-    
+
     // -- HELPER
 
     private void addFacetFactories() {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java
index 7980f4c1140..99678aeafa3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java
@@ -159,7 +159,7 @@ public SpecificationLoaderDefault(
         this.causewayBeanTypeRegistry = causewayBeanTypeRegistry;
         this.valueSemanticsResolver = valueSemanticsRegistry;
         this.classSubstitutorRegistry = classSubstitutorRegistry;
-        this.parallel = causewayConfiguration.getCore().getMetaModel().getIntrospector().isParallelize();
+        this.parallel = causewayConfiguration.core().metaModel().introspector().parallelize();
     }
 
     /** JUnit Test Support */
@@ -391,7 +391,7 @@ public void validateLater(final ObjectSpecification objectSpec) {
             // getValidationResult() is lazily populated later on first request anyway
             return;
         }
-        if(!causewayConfiguration.getCore().getMetaModel().getIntrospector().isValidateIncrementally()) {
+        if(!causewayConfiguration.core().metaModel().introspector().validateIncrementally()) {
             // re-validation after the initial one can be turned off by means of above config option
             return;
         }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java
index d42cff650f4..25930e7ae98 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java
@@ -129,7 +129,7 @@ public String parseableTextRepresentation(final ValueSemanticsProvider.Context c
 
     @Override
     public BigDecimal parseTextRepresentation(final ValueSemanticsProvider.Context context, final String text) {
-        var parsePolicy = isUseGroupingSeparatorFrom(causewayConfiguration.getValueTypes().getBigDecimal())
+        var parsePolicy = isUseGroupingSeparatorFrom(causewayConfiguration.valueTypes().bigDecimal())
                                 ? GroupingSeparatorPolicy.ALLOW
                                 : GroupingSeparatorPolicy.DISALLOW;
         return super.parseDecimal(context, text, parsePolicy)
@@ -137,7 +137,7 @@ public BigDecimal parseTextRepresentation(final ValueSemanticsProvider.Context c
     }
 
     private boolean isUseGroupingSeparatorFrom(final CausewayConfiguration.ValueTypes.BigDecimal bigDecimalConfig) {
-        return bigDecimalConfig.getEditing().isUseGroupingSeparator() || bigDecimalConfig.getDisplay().isUseGroupingSeparator();
+        return bigDecimalConfig.editing().useGroupingSeparator() || bigDecimalConfig.display().useGroupingSeparator();
     }
 
     @Override
@@ -149,11 +149,11 @@ public int typicalLength() {
     protected void configureDecimalFormat(
             final Context context, final DecimalFormat format, final FormatUsageFor usedFor) {
 
-        var bigDecimalConfig = causewayConfiguration.getValueTypes().getBigDecimal();
+        var bigDecimalConfig = causewayConfiguration.valueTypes().bigDecimal();
         format.setGroupingUsed(
                 usedFor == PARSING
-                    ? bigDecimalConfig.getEditing().isUseGroupingSeparator()
-                    : bigDecimalConfig.getDisplay().isUseGroupingSeparator()
+                    ? bigDecimalConfig.editing().useGroupingSeparator()
+                    : bigDecimalConfig.display().useGroupingSeparator()
         );
 
         if(context==null) return;
@@ -169,7 +169,7 @@ protected void configureDecimalFormat(
         // we skip this when PARSING,
         // because we want to firstly parse any number value into a BigDecimal,
         // no matter the minimumFractionDigits, which can always be filled up with '0' digits later
-        if(usedFor.isRendering() || bigDecimalConfig.getEditing().isPreserveScale()) {
+        if(usedFor.isRendering() || bigDecimalConfig.editing().preserveScale()) {
 
             // if there is a facet specifying minFractionalDigits (ie the scale), then apply it
             OptionalInt optionalInt = Facets.minFractionalDigits(feature);
@@ -184,8 +184,8 @@ protected void configureDecimalFormat(
     }
 
     private static Optional<Integer> minScaleFrom(final CausewayConfiguration.ValueTypes.BigDecimal bigDecimalConfig) {
-        return Optional.ofNullable(bigDecimalConfig.getDisplay().getMinScale())
-                       .or(() -> Optional.ofNullable(bigDecimalConfig.getDisplay().getMinScale()));
+        return Optional.ofNullable(bigDecimalConfig.display().minScale())
+                       .or(() -> Optional.ofNullable(bigDecimalConfig.display().minScale()));
     }
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java
index da0b730acf9..0801e49a24f 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java
@@ -214,8 +214,8 @@ protected Function<T, String> getRenderingFormatter(
 
         var dateAndTimeFormatStyle = DateAndTimeFormatStyle.forContext(mmc, context);
 
-        var datePattern = mmc.getConfiguration().getValueTypes().getTemporal().getDisplay().getDatePattern();
-        var dateTimePattern = mmc.getConfiguration().getValueTypes().getTemporal().getDisplay().getDateTimePattern();
+        var datePattern = mmc.getConfiguration().valueTypes().temporal().display().datePattern();
+        var dateTimePattern = mmc.getConfiguration().valueTypes().temporal().display().dateTimePattern();
         var temporalNoZoneRenderingFormat = getTemporalNoZoneRenderingFormat(
                 context, temporalCharacteristic, offsetCharacteristic,
                 dateAndTimeFormatStyle.dateFormatStyle(),
@@ -388,7 +388,7 @@ static DateAndTimeFormatStyle forContext(
                             .map(ValueSemanticsProvider.Context::featureIdentifier)
                             .orElse(null)));
 
-            // DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedPattern(mmc.getConfiguration().getValueTypes().getTemporal().getDisplay().getDatePattern());
+            // DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedPattern(mmc.getConfiguration().valueTypes().temporal().display().datePattern());
             var dateFormatStyle = featureIfAny
                     .flatMap(feature->feature.lookupFacet(DateFormatStyleFacet.class))
                     .map(DateFormatStyleFacet::getDateFormatStyle)
@@ -417,12 +417,12 @@ static DateAndTimeFormatStyle forContext(
     private org.apache.causeway.core.config.CausewayConfiguration.ValueTypes.Temporal temporalConfig() {
         return Optional.ofNullable(mmc) // nullable .. JUnit support
                 .map(MetaModelContext::getConfiguration)
-                .map(conf->conf.getValueTypes().getTemporal())
-                .orElseGet(CausewayConfiguration.ValueTypes.Temporal::new);
+                .map(conf->conf.valueTypes().temporal())
+                .orElseGet(CausewayConfiguration.ValueTypes.Temporal::defaults);
     }
 
     protected TemporalEditingPattern temporalEditingPattern() {
-        return temporalConfig().getEditing();
+        return temporalConfig().editing();
     }
 
 }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/MetaModelContext_configTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/MetaModelContext_configTest.java
index cfaaba5bfab..ee57f0ef4c8 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/MetaModelContext_configTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/MetaModelContext_configTest.java
@@ -31,7 +31,7 @@
 class MetaModelContext_configTest {
 
     private MetaModelContext mmc;
-    
+
     @BeforeEach
     void setUp() {
         mmc = MetaModelContext_forTesting.buildDefault();
@@ -42,20 +42,6 @@ void shouldReturnEmptyValue() {
         assertEquals(null, environment().getProperty("test"));
     }
 
-    @Test
-    void shouldAllowOverrideForTesting() {
-
-        mmcForTesting()
-        .runWithConfigProperties(
-            map->map.put("test", "Hello World!"),
-            ()->{
-                assertEquals("Hello World!", environment().getProperty("test"));
-            });
-
-        // expected post condition
-        assertEquals(null, environment().getProperty("test"));
-    }
-
     // -- HELPER
 
     private MetaModelContext_forTesting mmcForTesting() {
@@ -63,7 +49,7 @@ private MetaModelContext_forTesting mmcForTesting() {
     }
 
     private ConfigurableEnvironment environment() {
-        return mmcForTesting().getConfiguration().getEnvironment();
+        return mmcForTesting().getConfiguration().environment();
     }
-    
+
 }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/AbstractTestWithMetaModelContext.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/AbstractTestWithMetaModelContext.java
index 41823f5d8c4..244caec6279 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/AbstractTestWithMetaModelContext.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/AbstractTestWithMetaModelContext.java
@@ -48,7 +48,7 @@ protected final void setupWithDefaultProgrammingModel() {
     protected final void setupWithProgrammingModel(final BiConsumer<MetaModelContext, ProgrammingModelAbstract> factory) {
         metaModelContext = MetaModelContext_forTesting.builder()
                 .programmingModelFactory((mmc, refiners)->{
-                    var progModel = new ProgrammingModelAbstract(mmc) {
+                    var progModel = new ProgrammingModelAbstract(mmc.getServiceInjector()) {
                         @Override protected void assertNotInitialized(){}
                         @Override public MixinNamingStrategy mixinNamingStrategy() {
                             throw _Exceptions.notImplemented();
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
index 9163bdf5925..a4ce961137b 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
@@ -21,6 +21,7 @@
 import java.util.Optional;
 import java.util.function.BiConsumer;
 
+import org.jspecify.annotations.NonNull;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.mockito.Mockito;
@@ -56,7 +57,6 @@
 
 import lombok.Builder;
 import lombok.Getter;
-import org.jspecify.annotations.NonNull;
 
 public abstract class FacetFactoryTestAbstract
 implements HasMetaModelContext {
@@ -144,10 +144,10 @@ protected void setUpAll() {
         methodRemover = new MethodRemover_forTesting();
 
         metaModelContext = setUpMmc(MetaModelContext_forTesting.builder()
-                .translationService(mockTranslationService)
-                .interactionService(mockInteractionService)
-                .memberExecutor(mockMemberExecutorService)
-                .valueSemantic(new IntValueSemantics()));
+            .translationService(mockTranslationService)
+            .interactionService(mockInteractionService)
+            .memberExecutor(mockMemberExecutorService)
+            .valueSemantic(new IntValueSemantics()));
 
         Mockito.when(mockInteractionService.currentInteractionContext()).thenReturn(Optional.of(iaContext));
     }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java
index 53e321d96f9..d464825e620 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java
@@ -55,7 +55,7 @@ public UUID getInteractionId() {
     }
 
     void allowingPublishingConfigurationToReturn(final ActionConfigOptions.PublishingPolicy value) {
-        getConfiguration().getApplib().getAnnotation().getAction().setExecutionPublishing(value);
+        getConfiguration().applib().annotation().action().setExecutionPublishing(value);
     }
 
 }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java
index 6a852939f9f..338e64ca9b6 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/ViewModelSemanticCheckingFacetFactoryTest.java
@@ -46,12 +46,12 @@ class ViewModelSemanticCheckingFacetFactoryTest {
     @BeforeEach
     public void setUp() throws Exception {
 
-        var configuration = CausewayConfiguration.builder().build();
-        configuration.getApplib().getAnnotation().getViewModel().getValidation().getSemanticChecking().setEnable(true);
+        var configuration = CausewayConfiguration.defaults();
+        configuration.applib().annotation().viewModel().validation().semanticChecking().setEnable(true);
 
         metaModelContext = MetaModelContext_forTesting.builder()
                 .configuration(configuration)
-                .programmingModelFactory((mmc, refiners)->new ProgrammingModelAbstract(mmc) {
+                .programmingModelFactory((mmc, refiners)->new ProgrammingModelAbstract(mmc.getServiceInjector()) {
                     @Override public MixinNamingStrategy mixinNamingStrategy() {
                         throw _Exceptions.notImplemented();
                     }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
index d888a7fc75b..81fc9ef8a29 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
@@ -92,14 +92,14 @@ public UUID getInteractionId() {
     void allowingEntityChangePublishingToReturn(final DomainObjectConfigOptions.EntityChangePublishingPolicy value) {
         if(value!=null) {
             getConfiguration()
-                .getApplib().getAnnotation().getDomainObject().setEntityChangePublishing(value);
+                .applib().annotation().domainObject().setEntityChangePublishing(value);
         }
     }
 
     void allowingObjectsEditingToReturn(final DomainObjectConfigOptions.EditingObjectsConfiguration value) {
         if(value!=null) {
             getConfiguration()
-                .getApplib().getAnnotation().getDomainObject().setEditing(value);
+                .applib().annotation().domainObject().setEditing(value);
         }
     }
 
@@ -676,7 +676,7 @@ public void testValidationDomainObjectWithAliasesConfigured() {
             metaModelContext = MetaModelContext_forTesting.builder()
                     .refiners(Can.of(__->facetFactory))
                     .build();
-            getConfiguration().getCore().getMetaModel().getValidator().setAllowLogicalTypeNameAsAlias(true);
+            getConfiguration().core().metaModel().validator().setAllowLogicalTypeNameAsAlias(true);
             facetFactory = new DomainObjectAnnotationFacetFactory(getMetaModelContext());
             ((MetaModelContext_forTesting) getMetaModelContext()).getProgrammingModel();//kicks off the programming model factory
 
@@ -690,7 +690,7 @@ public void testValidationDomainServiceWithAliasesConfigured() {
             metaModelContext = MetaModelContext_forTesting.builder()
                     .refiners(Can.of(__->facetFactory))
                     .build();
-            getConfiguration().getCore().getMetaModel().getValidator().setAllowLogicalTypeNameAsAlias(true);
+            getConfiguration().core().metaModel().validator().setAllowLogicalTypeNameAsAlias(true);
             facetFactory = new DomainObjectAnnotationFacetFactory(getMetaModelContext());
             ((MetaModelContext_forTesting) getMetaModelContext()).getProgrammingModel();//kicks off the programming model factory
 
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/properties/propertylayout/PromptStyleFacetFromPropertyAnnotationTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/properties/propertylayout/PromptStyleFacetFromPropertyAnnotationTest.java
index f36ccdb7192..a5f0759f40a 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/properties/propertylayout/PromptStyleFacetFromPropertyAnnotationTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/properties/propertylayout/PromptStyleFacetFromPropertyAnnotationTest.java
@@ -39,7 +39,7 @@
 
 class PromptStyleFacetFromPropertyAnnotationTest {
 
-    CausewayConfiguration stubConfiguration = CausewayConfiguration.builder().build();
+    CausewayConfiguration stubConfiguration = CausewayConfiguration.defaults();
 
     FacetHolder mockFacetHolder;
     PropertyLayout mockPropertyLayout;
@@ -79,7 +79,7 @@ void when_annotated_with_inline() throws Exception {
     @Test
     void when_annotated_with_as_configured() throws Exception {
 
-        stubConfiguration.getViewer().getWicket().setPromptStyle(PromptStyle.INLINE);
+        stubConfiguration.viewer().wicket().setPromptStyle(PromptStyle.INLINE);
 
         Mockito.when(mockPropertyLayout.promptStyle()).thenReturn(PromptStyle.AS_CONFIGURED);
         Mockito.when(mockFacetHolder.containsNonFallbackFacet(PromptStyleFacet.class))
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/BigDecimalValueSemanticsProviderTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/BigDecimalValueSemanticsProviderTest.java
index 4e97d6a2ad1..2f2dff83b59 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/BigDecimalValueSemanticsProviderTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/BigDecimalValueSemanticsProviderTest.java
@@ -73,7 +73,7 @@ void parseInvalidStringWithGroupingSeparator() throws Exception {
 
     @Test
     void parseValidStringWithGroupingSeparatorIfConfiguredToAllow() throws Exception {
-        causewayConfiguration.getValueTypes().getBigDecimal().getEditing().setUseGroupingSeparator(true);
+        causewayConfiguration.valueTypes().bigDecimal().editing().setUseGroupingSeparator(true);
 
         BigDecimal bd = value.parseTextRepresentation(null, "123,999.01");
 
@@ -91,7 +91,7 @@ void demonstrateTheRiskOfAllowingGroupingSeparatorIfConfiguredToAllow() throws E
         }
 
         // but if we allow it...
-        causewayConfiguration.getValueTypes().getBigDecimal().getEditing().setUseGroupingSeparator(true);
+        causewayConfiguration.valueTypes().bigDecimal().editing().setUseGroupingSeparator(true);
 
         BigDecimal bigDecimal = value.parseTextRepresentation(null, "1239,99");
         assertThat(bigDecimal).isEqualTo(new BigDecimal(123999));
@@ -109,7 +109,7 @@ void titleOf() {
 
     @Test
     void titleOfWhenUseGroupingSeparator() {
-        causewayConfiguration.getValueTypes().getBigDecimal().getDisplay().setUseGroupingSeparator(true);
+        causewayConfiguration.valueTypes().bigDecimal().display().setUseGroupingSeparator(true);
 
         assertEquals("34,132.199", value.titlePresentation(null, bigDecimal));
     }
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/ValueSemanticsProviderAbstractTestCase.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/ValueSemanticsProviderAbstractTestCase.java
index 89f452cddf4..0f10501e206 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/ValueSemanticsProviderAbstractTestCase.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/value/ValueSemanticsProviderAbstractTestCase.java
@@ -71,7 +71,7 @@ final void setUpMmc() throws Exception {
         Locale.setDefault(Locale.UK);
 
         mockInteractionService = Mockito.mock(InteractionService.class);
-        causewayConfiguration = new CausewayConfiguration(null, Optional.empty());
+        causewayConfiguration = CausewayConfiguration.defaults();
 
         metaModelContext = MetaModelContext_forTesting.builder()
                 .interactionService(mockInteractionService)
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java
index bb8ec84542b..b52fc56ddab 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java
@@ -29,6 +29,9 @@
 import org.mockito.Mockito;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.util.TestPropertyValues;
+
 import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
@@ -57,8 +60,8 @@ void setUpObjects() throws Exception {
 
         context = new ValueSemanticsProvider.Context(mockIdentifier, null);
 
-        causewayConfiguration = CausewayConfiguration.builder().build();
-        causewayConfiguration.getValueTypes().getBigDecimal().getDisplay().setMinScale(null);
+        causewayConfiguration = CausewayConfiguration.defaults();
+        causewayConfiguration.valueTypes().bigDecimal().display().setMinScale(null);
 
         // expecting
         Mockito.lenient().when(mockSpecificationLoader.loadFeature(mockIdentifier)).thenReturn(Optional.of(mockObjectFeature));
@@ -101,7 +104,7 @@ void min_facets_not_set_but_fallback() {
         Mockito.lenient().when(mockObjectFeature.lookupFacet(MinFractionalDigitsFacet.class))
                .thenReturn(Optional.empty());
 
-        causewayConfiguration.getValueTypes().getBigDecimal().getDisplay().setMinScale(fallbackScale);
+        causewayConfiguration.valueTypes().bigDecimal().display().setMinScale(fallbackScale);
 
         // when
         DecimalFormat format = new DecimalFormat();
@@ -124,7 +127,7 @@ void min_facets_not_set_and_no_fallback() {
         Mockito.lenient().when(mockObjectFeature.lookupFacet(MinFractionalDigitsFacet.class))
                .thenReturn(Optional.empty());
 
-        causewayConfiguration.getValueTypes().getBigDecimal().getDisplay().setMinScale(null);
+        causewayConfiguration.valueTypes().bigDecimal().display().setMinScale(null);
 
         // when
         DecimalFormat format = new DecimalFormat();
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
index d626bb33eeb..579c8a9f3eb 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
@@ -53,7 +53,7 @@ class TemporalValueSemanticsProviderTest {
 
     @BeforeEach
     void setUp() throws Exception {
-        editingPattern = (new CausewayConfiguration.ValueTypes.Temporal()).getEditing();
+        editingPattern = (CausewayConfiguration.ValueTypes.Temporal.defaults()).editing();
     }
 
     @ParameterizedTest
diff --git a/core/pom.xml b/core/pom.xml
index c7791bb52bb..40845efd6a3 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -56,6 +56,7 @@
         <module>config</module>
         <module>interaction</module>
         <module>internaltestsupport</module>
+        <module>configtest</module>
         <module>metamodel</module>
         <module>runtime</module>
         <module>runtimeservices</module>
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandDtoFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandDtoFactoryDefault.java
index 2d0343d0521..302bc2a4c67 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandDtoFactoryDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandDtoFactoryDefault.java
@@ -180,7 +180,8 @@ private CommandDto asCommandDto(final UUID interactionId, final InteractionHead
     }
 
     private boolean isParamIdentifierStrategySetToUseId() {
-        return causewayConfiguration.getSchema().getCommand().getParamIdentifierStrategy() == CausewayConfiguration.Schema.Command.ParamIdentifierStrategy.BY_ID;
+        return causewayConfiguration.schema().command().paramIdentifierStrategy()
+            == CausewayConfiguration.Schema.Command.ParamIdentifierStrategy.BY_ID;
     }
 
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
index 7989550da48..bcf9d58cfb0 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
@@ -341,7 +341,7 @@ private ManagedObject resultFilteredHonoringVisibility(
             return resultAdapter;
         }
 
-        if (!getConfiguration().getCore().getMetaModel().isFilterVisibility()
+        if (!getConfiguration().core().metaModel().filterVisibility()
                 || resultAdapter instanceof PackedManagedObject) {
             return resultAdapter;
         }
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java
index 5bca0ba47c6..076baaefd4e 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java
@@ -75,7 +75,7 @@ public <T> T getOrCreate(final @NonNull Class<T> requiredType) {
 
     @Override
     public <T> T get(final @NonNull Class<T> requiredType) {
-        return causewaySystemEnvironment.getIocContainer()
+        return causewaySystemEnvironment.springContextHolder()
                 .get(requiredType)
                 .orElseThrow(()->_Exceptions.noSuchElement("not an injectable type %s", requiredType));
     }
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/i18n/po/TranslationServicePo.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/i18n/po/TranslationServicePo.java
index 0a98fe09568..ca86fbc947e 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/i18n/po/TranslationServicePo.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/i18n/po/TranslationServicePo.java
@@ -70,7 +70,7 @@ public TranslationServicePo() {
     @PostConstruct
     public void init() {
 
-        final Mode translationMode = configuration.getCore().getRuntimeServices().getTranslation().getPo().getMode();
+        final Mode translationMode = configuration.core().runtimeServices().translation().po().mode();
 
         if(translationMode == Mode.DISABLED) {
             // switch to disabled mode
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java
index 85e94632cd3..2a3ab9786d1 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java
@@ -67,8 +67,8 @@ public class MenuBarsLoaderServiceDefault
     public MenuBarsLoaderServiceDefault(final MetaModelContext mmc) {
         this.supportsReloading = mmc.getSystemEnvironment().isPrototyping();
 
-        var menubarsLayoutFile = mmc.getConfiguration().getViewer().getCommon().getApplication()
-                .getMenubarsLayoutFile();
+        var menubarsLayoutFile = mmc.getConfiguration().viewer().common().application()
+                .menubarsLayoutFile();
         var menubarsLayoutResource = new ClassPathResource(menubarsLayoutFile);
         if(!menubarsLayoutResource.exists()) {
             log.warn("menubarsLayoutFile {} (as configured for Apache Causeway) not found",
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
index 49473b7d306..af7ad6fce90 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
@@ -119,7 +119,8 @@ public void publishChangedProperties() {
                     () -> getCannotPublishReason(uniquePropertyChanges)
             );
 
-            if (uniquePropertyChanges.size() <= causewayConfiguration.getCore().getRuntimeServices().getEntityPropertyChangePublisher().getBulk().getThreshold()) {
+            if (uniquePropertyChanges.size() <= causewayConfiguration.core().runtimeServices()
+                .entityPropertyChangePublisher().bulk().threshold()) {
                 uniquePropertyChanges.forEach(propertyChange -> {
                     for (var subscriber : enabledSubscribers) {
                         subscriber.onChanging(propertyChange);
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessException.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessException.java
index 8e45eebf62b..01e4cad1ccb 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessException.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessException.java
@@ -63,8 +63,8 @@ public class ExceptionRecognizerForDataAccessException
 
     @Inject
     public ExceptionRecognizerForDataAccessException(final CausewayConfiguration conf) {
-        this.disabled = conf.getCore().getRuntimeServices()
-              .getExceptionRecognizer().getDae().isDisable();
+        this.disabled = conf.core().runtimeServices()
+              .exceptionRecognizer().dae().disable();
     }
 
     @Override
diff --git a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
index c526db0d484..fae6dc9eddc 100644
--- a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
+++ b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
@@ -32,7 +32,7 @@
 import org.apache.causeway.applib.services.menu.MenuBarsService;
 import org.apache.causeway.applib.services.message.MessageService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
-import org.apache.causeway.commons.internal.ioc._SingletonBeanProvider;
+import org.apache.causeway.commons.internal.ioc.SingletonBeanProvider;
 import org.apache.causeway.core.metamodel._testing.MetaModelContext_forTesting;
 import org.apache.causeway.core.metamodel._testing.MetaModelContext_forTesting.MetaModelContext_forTestingBuilder;
 import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
@@ -71,7 +71,7 @@ final void setUp() throws Exception {
         onSetUp(mmcBuilder);
 
         mmcBuilder.singletonProvider(
-                _SingletonBeanProvider
+                SingletonBeanProvider
                 .forTestingLazy(MenuBarsMarshallerService.class, ()->{
                     var jaxbService = getServiceRegistry().lookupServiceElseFail(JaxbService.class);
                     return new MenuBarsMarshallerServiceBootstrap(
@@ -79,7 +79,7 @@ final void setUp() throws Exception {
                 }));
 
         mmcBuilder.singletonProvider(
-                _SingletonBeanProvider
+                SingletonBeanProvider
                 .forTestingLazy(MenuBarsLoaderService.class, ()->{
                     return new MenuBarsLoaderServiceDefault(
                             menubarsLayoutXmlResourceRef,
@@ -87,7 +87,7 @@ final void setUp() throws Exception {
                 }));
 
         mmcBuilder.singletonProvider(
-                _SingletonBeanProvider
+                SingletonBeanProvider
                 .forTestingLazy(MenuBarsService.class, ()->{
 
                     var messageService = getServiceRegistry().lookupServiceElseFail(MessageService.class);
@@ -104,7 +104,7 @@ final void setUp() throws Exception {
 
         metaModelContext = mmcBuilder.build();
 
-        getConfiguration().getCore().getMetaModel().getIntrospector().setLockAfterFullIntrospection(false);
+        getConfiguration().core().metaModel().introspector().setLockAfterFullIntrospection(false);
 
         afterSetUp();
     }
diff --git a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
index 9ed35a5e2f4..209ceef6882 100644
--- a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
+++ b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
@@ -57,7 +57,7 @@ protected void onSetUp(final MetaModelContext_forTestingBuilder mmcBuilder) {
     @Override
     protected void afterSetUp() {
 
-        getConfiguration().getCore().getMetaModel().getIntrospector().setValidateIncrementally(false);
+        getConfiguration().core().metaModel().introspector().setValidateIncrementally(false);
 
         layoutService = getServiceRegistry()
                 .lookupServiceElseFail(LayoutService.class);
diff --git a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessExceptionTest.java b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessExceptionTest.java
index 00b2e616e70..0845b02b6f7 100644
--- a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessExceptionTest.java
+++ b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/recognizer/dae/ExceptionRecognizerForDataAccessExceptionTest.java
@@ -42,7 +42,7 @@ class ExceptionRecognizerForDataAccessExceptionTest {
 
     @BeforeEach
     public void setUp() {
-        var conf = CausewayConfiguration.builder().build();
+        var conf = CausewayConfiguration.defaults();
         recognizerForDae = new ExceptionRecognizerForDataAccessException(conf);
     }
 
diff --git a/core/security/src/main/java/org/apache/causeway/core/security/authorization/manager/AuthorizationManager.java b/core/security/src/main/java/org/apache/causeway/core/security/authorization/manager/AuthorizationManager.java
index 1b620285835..7fb74a33cb4 100644
--- a/core/security/src/main/java/org/apache/causeway/core/security/authorization/manager/AuthorizationManager.java
+++ b/core/security/src/main/java/org/apache/causeway/core/security/authorization/manager/AuthorizationManager.java
@@ -65,7 +65,7 @@ public AuthorizationManager(
             final Optional<AuthorizorChooser> authorizorChooserIfAny) {
 
         this.actionsWithSafeSemanticsRequireOnlyViewingPermission =
-                config.getSecurity().isActionsWithSafeSemanticsRequireOnlyViewingPermission();
+                config.security().actionsWithSafeSemanticsRequireOnlyViewingPermission();
         this.actionSemanticsResolver = actionSemanticsResolver;
 
         _Assert.assertTrue(_NullSafe.size(authorizors)>0, ()->
diff --git a/core/webapp/src/main/java/org/apache/causeway/core/webapp/confmenu/ConfigurationViewServiceDefault.java b/core/webapp/src/main/java/org/apache/causeway/core/webapp/confmenu/ConfigurationViewServiceDefault.java
index ca8285038e0..a63ab68ac0b 100644
--- a/core/webapp/src/main/java/org/apache/causeway/core/webapp/confmenu/ConfigurationViewServiceDefault.java
+++ b/core/webapp/src/main/java/org/apache/causeway/core/webapp/confmenu/ConfigurationViewServiceDefault.java
@@ -99,7 +99,7 @@ public String toStringFormatted() {
         var sb = new StringBuilder();
 
         String head = String.format("APACHE CAUSEWAY %s (%s) ",
-                configuration.getViewer().getCommon().getApplication().getVersion(),
+                configuration.viewer().common().application().version(),
                 systemEnvironment.getDeploymentType().name());
 
         final Map<String, ConfigurationProperty> map = scopedConf.get().get(Scope.PRIMARY.ordinal());
@@ -157,7 +157,7 @@ private List<Map<String, ConfigurationProperty>> loadConfiguration() {
 
     private Map<String, ConfigurationProperty> loadEnvironment() {
         final Map<String, ConfigurationProperty> map = _Maps.newTreeMap();
-        add("Causeway Version", configuration.getViewer().getCommon().getApplication().getVersion(), map);
+        add("Causeway Version", configuration.viewer().common().application().version(), map);
         add("Deployment Type", systemEnvironment.getDeploymentType().name(), map);
         //add("Unit Testing", ""+systemEnvironment.isUnitTesting(), map);
 
@@ -233,7 +233,7 @@ private Map<String, ConfigurationProperty> loadSecondary(final Set<String> toBeE
         }
         return map;
     }
-    
+
     private static void logRetrievalFailure(final String propName, Throwable error) {
         log.warn("failed to load configuration property '{}'", propName, error);
     }
@@ -262,9 +262,9 @@ private boolean isShowConfigurationProperties() {
 
     private ConfigurationPropertyVisibilityPolicy getConfigurationPropertyVisibilityPolicy() {
         return Optional.ofNullable(
-                configuration.getCore().getConfig().getConfigurationPropertyVisibilityPolicy())
+                configuration.core().config().configurationPropertyVisibilityPolicy())
                 // fallback to configuration default policy
-                .orElseGet(()->new CausewayConfiguration.Core.Config().getConfigurationPropertyVisibilityPolicy());
+                .orElseGet(()->CausewayConfiguration.Core.Config.defaults().configurationPropertyVisibilityPolicy());
     }
 
 }
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/job/RunBackgroundCommandsJob.java b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/job/RunBackgroundCommandsJob.java
index e4e4479d3ce..e7939680ae7 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/job/RunBackgroundCommandsJob.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/job/RunBackgroundCommandsJob.java
@@ -109,7 +109,7 @@ public void execute(final JobExecutionContext quartzContext) {
             for (CommandDto dto : commandDtos) {
                 Try<?> attempt = executeCommandWithinTransaction(dto, interactionContext);
                 if(attempt.isFailure()) {
-                    var onFailurePolicy = causewayConfiguration.getExtensions().getCommandLog().getRunBackgroundCommands().getOnFailurePolicy();
+                    var onFailurePolicy = causewayConfiguration.extensions().commandLog().runBackgroundCommands().onFailurePolicy();
                     if (onFailurePolicy == CausewayConfiguration.Extensions.CommandLog.RunBackgroundCommands.OnFailurePolicy.STOP_THE_LINE) {
                         break;
                     }
@@ -144,7 +144,7 @@ private Optional<List<CommandDto>> pendingCommandDtos(final InteractionContext i
                 commandLogEntryRepository.findBackgroundAndNotYetStarted()
                         .stream()
                         .map(CommandLogEntry::getCommandDto)
-                        .limit(causewayConfiguration.getExtensions().getCommandLog().getRunBackgroundCommands().getBatchSize())
+                        .limit(causewayConfiguration.extensions().commandLog().runBackgroundCommands().batchSize())
                         .collect(Collectors.toList())
                 )
                 .ifFailureFail()
@@ -196,7 +196,7 @@ private Try<?> executeCommandWithinTransaction(
         }
 
         // a failure has occurred
-        var onFailurePolicy = causewayConfiguration.getExtensions().getCommandLog().getRunBackgroundCommands().getOnFailurePolicy();
+        var onFailurePolicy = causewayConfiguration.extensions().commandLog().runBackgroundCommands().onFailurePolicy();
         switch (onFailurePolicy) {
             case CONTINUE_WITH_NEXT:
                 // the result _will_ contain a failure
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
index 9f398a8c552..362a9233ecd 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
@@ -64,7 +64,7 @@ public class CommandSubscriberForCommandLog implements CommandSubscriber {
 
     @Override
     public boolean isEnabled() {
-        return causewayConfiguration.getExtensions().getCommandLog().getPersist().isEnabled();
+        return causewayConfiguration.extensions().commandLog().persist().isEnabled();
     }
 
     @Override
diff --git a/extensions/core/executionlog/applib/src/main/java/org/apache/causeway/extensions/executionlog/applib/spiimpl/ExecutionSubscriberForExecutionLog.java b/extensions/core/executionlog/applib/src/main/java/org/apache/causeway/extensions/executionlog/applib/spiimpl/ExecutionSubscriberForExecutionLog.java
index 5e3a778ccfa..0e86bc2e809 100644
--- a/extensions/core/executionlog/applib/src/main/java/org/apache/causeway/extensions/executionlog/applib/spiimpl/ExecutionSubscriberForExecutionLog.java
+++ b/extensions/core/executionlog/applib/src/main/java/org/apache/causeway/extensions/executionlog/applib/spiimpl/ExecutionSubscriberForExecutionLog.java
@@ -53,7 +53,7 @@ public class ExecutionSubscriberForExecutionLog implements ExecutionSubscriber {
 
     @Override
     public boolean isEnabled() {
-        return causewayConfiguration.getExtensions().getExecutionLog().getPersist().isEnabled();
+        return causewayConfiguration.extensions().executionLog().persist().isEnabled();
     }
 
     @Override
diff --git a/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/dom/ExecutionOutboxEntryRepositoryAbstract.java b/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/dom/ExecutionOutboxEntryRepositoryAbstract.java
index 4966f2c4c63..bdbe430bc4d 100644
--- a/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/dom/ExecutionOutboxEntryRepositoryAbstract.java
+++ b/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/dom/ExecutionOutboxEntryRepositoryAbstract.java
@@ -87,6 +87,7 @@ protected ExecutionOutboxEntryRepositoryAbstract(final Class<E> executionOutboxE
         this.factoryService = factoryService;
     }
 
+    @Override
     public E createEntryAndPersist(final Execution execution) {
         E e = factoryService.detachedEntity(executionOutboxEntryClass);
         e.init(execution);
@@ -94,6 +95,7 @@ public E createEntryAndPersist(final Execution execution) {
         return e;
     }
 
+    @Override
     public Optional<ExecutionOutboxEntry> findByInteractionIdAndSequence(final UUID interactionId, final int sequence) {
         return _Casts.uncheckedCast(
                 repositoryService().firstMatch(
@@ -104,15 +106,17 @@ public Optional<ExecutionOutboxEntry> findByInteractionIdAndSequence(final UUID
         );
     }
 
+    @Override
     public List<ExecutionOutboxEntry> findOldest() {
         return _Casts.uncheckedCast(
                 repositoryService().allMatches(
                 Query.named(executionOutboxEntryClass, ExecutionOutboxEntry.Nq.FIND_OLDEST)
-                        .withLimit(causewayConfiguration.getExtensions().getExecutionOutbox().getRestApi().getMaxPending())
+                        .withLimit(causewayConfiguration.extensions().executionOutbox().restApi().maxPending())
                 )
         );
     }
 
+    @Override
     public ExecutionOutboxEntry upsert(
             final UUID interactionId,
             final int sequence,
@@ -126,6 +130,7 @@ public ExecutionOutboxEntry upsert(
                 InteractionDtoUtils.dtoMapper().read(xml));
     }
 
+    @Override
     public ExecutionOutboxEntry upsert(
             final UUID interactionId,
             final int sequence,
@@ -160,6 +165,7 @@ public ExecutionOutboxEntry upsert(
 
     protected abstract E newExecutionOutboxEntry();
 
+    @Override
     @Programmatic
     public boolean deleteByInteractionIdAndSequence(final UUID interactionId, final int sequence) {
         Optional<ExecutionOutboxEntry> outboxEventIfAny = findByInteractionIdAndSequence(interactionId, sequence);
@@ -182,6 +188,7 @@ private RepositoryService repositoryService() {
     /**
      * for testing purposes only
      */
+    @Override
     public List<ExecutionOutboxEntry> findAll() {
         if (causewaySystemEnvironment.getDeploymentType().isProduction()) {
             throw new IllegalStateException("Cannot removeAll in production systems");
@@ -192,6 +199,7 @@ public List<ExecutionOutboxEntry> findAll() {
     /**
      * for testing purposes only
      */
+    @Override
     public void removeAll() {
         if (causewaySystemEnvironment.getDeploymentType().isProduction()) {
             throw new IllegalStateException("Cannot removeAll in production systems");
diff --git a/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/spiimpl/ExecutionSubscriberForExecutionOutbox.java b/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/spiimpl/ExecutionSubscriberForExecutionOutbox.java
index 9b2f9143a60..8f99c7bdf9e 100644
--- a/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/spiimpl/ExecutionSubscriberForExecutionOutbox.java
+++ b/extensions/core/executionoutbox/applib/src/main/java/org/apache/causeway/extensions/executionoutbox/applib/spiimpl/ExecutionSubscriberForExecutionOutbox.java
@@ -53,7 +53,7 @@ public class ExecutionSubscriberForExecutionOutbox implements ExecutionSubscribe
 
     @Override
     public boolean isEnabled() {
-        return causewayConfiguration.getExtensions().getExecutionOutbox().getPersist().isEnabled();
+        return causewayConfiguration.extensions().executionOutbox().persist().isEnabled();
     }
 
     @Override
diff --git a/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/CausewayModuleExtLayoutLoadersGithub.java b/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/CausewayModuleExtLayoutLoadersGithub.java
index 27382b6afe8..925d93ed9af 100644
--- a/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/CausewayModuleExtLayoutLoadersGithub.java
+++ b/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/CausewayModuleExtLayoutLoadersGithub.java
@@ -79,7 +79,7 @@ public class CausewayModuleExtLayoutLoadersGithub {
     @Bean(name = "GithubSearch")
     public RestTemplate restTemplateForGithubSearch(final CausewayConfiguration causewayConfiguration) {
 
-        var apiKey = causewayConfiguration.getExtensions().getLayoutLoaders().getGithub().getApiKey();
+        var apiKey = causewayConfiguration.extensions().layoutLoaders().github().apiKey();
 
         return new RestTemplateBuilder()
                 .rootUri("https://api.github.com")
@@ -106,8 +106,8 @@ public RestTemplate restTemplateForGithubSearch(final CausewayConfiguration caus
     @Bean(name = "GithubContent")
     public RestTemplate restTemplateForGithubContent(final CausewayConfiguration causewayConfiguration) {
 
-        var apiKey = causewayConfiguration.getExtensions().getLayoutLoaders().getGithub().getApiKey();
-        var repo = causewayConfiguration.getExtensions().getLayoutLoaders().getGithub().getRepository();
+        var apiKey = causewayConfiguration.extensions().layoutLoaders().github().apiKey();
+        var repo = causewayConfiguration.extensions().layoutLoaders().github().repository();
 
         return new RestTemplateBuilder()
                 .rootUri(String.format("https://api.github.com/repos/%s", repo))
diff --git a/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/menu/LayoutLoadersGitHubMenu.java b/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/menu/LayoutLoadersGitHubMenu.java
index dcdcf9ec697..21efe3ce568 100644
--- a/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/menu/LayoutLoadersGitHubMenu.java
+++ b/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/menu/LayoutLoadersGitHubMenu.java
@@ -110,9 +110,9 @@ public class ActionDomainEvent extends LayoutLoadersGitHubMenu.ActionDomainEvent
     }
 
     boolean isNotConfigured() {
-        var layoutLoadersGitHub = causewayConfiguration.getExtensions().getLayoutLoaders().getGithub();
-        return layoutLoadersGitHub.getRepository() == null ||
-                layoutLoadersGitHub.getApiKey() == null;
+        var layoutLoadersGitHub = causewayConfiguration.extensions().layoutLoaders().github();
+        return layoutLoadersGitHub.repository() == null ||
+                layoutLoadersGitHub.apiKey() == null;
     }
 
 }
diff --git a/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub.java b/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub.java
index c911e339f87..82a8f075c46 100644
--- a/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub.java
+++ b/extensions/core/layoutloaders/github/src/main/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub.java
@@ -94,7 +94,7 @@ private Try<LayoutResource> tryLoadLayoutResource(final String candidateResource
     private Try<String> search(final @NonNull String candidateResourceName) {
 
         try {
-            var repo = causewayConfiguration.getExtensions().getLayoutLoaders().getGithub().getRepository();
+            var repo = causewayConfiguration.extensions().layoutLoaders().github().repository();
             var searchParams = new HashMap<String, String>();
             searchParams.put("q", String.format("%s+in:path+repo:%s", candidateResourceName, repo));
 
diff --git a/extensions/core/layoutloaders/github/src/test/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub_tryLoadLayoutResource_Test.java b/extensions/core/layoutloaders/github/src/test/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub_tryLoadLayoutResource_Test.java
index 7769ed72d6d..fb13f8b9c0f 100644
--- a/extensions/core/layoutloaders/github/src/test/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub_tryLoadLayoutResource_Test.java
+++ b/extensions/core/layoutloaders/github/src/test/java/org/apache/causeway/extensions/layoutloaders/github/spiimpl/LayoutResourceLoaderFromGithub_tryLoadLayoutResource_Test.java
@@ -48,9 +48,9 @@ void preconditions() {
     @BeforeEach
     void setup() {
 
-        var causewayConfiguration = CausewayConfiguration.builder().build();
-        causewayConfiguration.getExtensions().getLayoutLoaders().getGithub().setApiKey(getApiKey());
-        causewayConfiguration.getExtensions().getLayoutLoaders().getGithub().setRepository("apache/causeway-app-simpleapp");
+        var causewayConfiguration = CausewayConfiguration.defaults();
+        causewayConfiguration.extensions().layoutLoaders().github().setApiKey(getApiKey());
+        causewayConfiguration.extensions().layoutLoaders().github().setRepository("apache/causeway-app-simpleapp");
 
         var module = new CausewayModuleExtLayoutLoadersGithub();
         var restTemplateForSearch = module.restTemplateForGithubSearch(causewayConfiguration);
@@ -61,7 +61,8 @@ void setup() {
         layoutLoaderMenu.new enableDynamicLayoutLoading().act();
         Assertions.assertThat(layoutLoaderMenu.isEnabled()).isTrue();
 
-        loader = new LayoutResourceLoaderFromGithub(restTemplateForSearch, restTemplateForContent, causewayConfiguration, layoutLoaderMenu, () -> queryResultsCache);
+        loader = new LayoutResourceLoaderFromGithub(restTemplateForSearch, restTemplateForContent, causewayConfiguration,
+            layoutLoaderMenu, () -> queryResultsCache);
     }
 
     @Test
diff --git a/extensions/core/titlecache/caffeine/src/main/java/org/apache/causeway/extensions/titlecache/caffeine/dom/TitleCacheSubscriber.java b/extensions/core/titlecache/caffeine/src/main/java/org/apache/causeway/extensions/titlecache/caffeine/dom/TitleCacheSubscriber.java
index a6c842105f4..fb942315ed6 100644
--- a/extensions/core/titlecache/caffeine/src/main/java/org/apache/causeway/extensions/titlecache/caffeine/dom/TitleCacheSubscriber.java
+++ b/extensions/core/titlecache/caffeine/src/main/java/org/apache/causeway/extensions/titlecache/caffeine/dom/TitleCacheSubscriber.java
@@ -146,9 +146,9 @@ public void on(final CausewayModuleApplib.TitleUiEvent<?> ev) {
     private Cache addCache(
             final CausewayModuleApplib.TitleUiEvent<?> ev,
             final String logicalTypeName) {
-        var titlecacheConfig = causewayConfiguration.getExtensions().getTitlecache().getCaffeine();
-        int expiryDurationInMinutes = titlecacheConfig.getExpiryDurationInMinutes();
-        int maxSizeInEntries = titlecacheConfig.getMaxSizeInEntries();
+        var titlecacheConfig = causewayConfiguration.extensions().titlecache().caffeine();
+        int expiryDurationInMinutes = titlecacheConfig.expiryDurationInMinutes();
+        int maxSizeInEntries = titlecacheConfig.maxSizeInEntries();
         if(ev instanceof CachedWithCacheSettings) {
             var settings = (CachedWithCacheSettings) ev;
             expiryDurationInMinutes = settings.expiryDurationInMinutes();
diff --git a/extensions/security/audittrail/applib/src/main/java/org/apache/causeway/extensions/audittrail/applib/spiimpl/EntityPropertyChangeSubscriberForAuditTrail.java b/extensions/security/audittrail/applib/src/main/java/org/apache/causeway/extensions/audittrail/applib/spiimpl/EntityPropertyChangeSubscriberForAuditTrail.java
index 79208996104..1dad9ebecc4 100644
--- a/extensions/security/audittrail/applib/src/main/java/org/apache/causeway/extensions/audittrail/applib/spiimpl/EntityPropertyChangeSubscriberForAuditTrail.java
+++ b/extensions/security/audittrail/applib/src/main/java/org/apache/causeway/extensions/audittrail/applib/spiimpl/EntityPropertyChangeSubscriberForAuditTrail.java
@@ -63,7 +63,7 @@ public class EntityPropertyChangeSubscriberForAuditTrail implements EntityProper
 
     @Override
     public boolean isEnabled() {
-        return causewayConfiguration.getExtensions().getAuditTrail().getPersist().isEnabled();
+        return causewayConfiguration.extensions().auditTrail().persist().isEnabled();
     }
 
     @Override
@@ -75,7 +75,7 @@ public void onChanging(final EntityPropertyChange entityPropertyChange) {
     }
 
     @Override
-    public void onChanging(Can<EntityPropertyChange> entityPropertyChanges) {
+    public void onChanging(final Can<EntityPropertyChange> entityPropertyChanges) {
         if (!isEnabled()) {
             return;
         }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
index 0950228eaa2..25bd69b24a0 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
@@ -157,7 +157,7 @@ public void removeRoleFromUser(
 
     @Override
     public boolean isAdminRole(final ApplicationRole genericRole) {
-        var adminRoleName = config.getExtensions().getSecman().getSeed().getAdmin().getRoleName();
+        var adminRoleName = config.extensions().secman().seed().admin().roleName();
         final ApplicationRole adminRole = findByNameCached(adminRoleName).orElse(null);
         return Objects.equals(adminRole, genericRole);
     }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java
index 365debdf388..ec8e963c98c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java
@@ -86,7 +86,7 @@ private boolean canRemove(final ApplicationPermission permission) {
             return false;
         }
         if(applicationRoleRepository.isAdminRole(target)
-                && isStickyAdminNamespace(config.getExtensions().getSecman(), permission.getFeatureFqn())) {
+                && isStickyAdminNamespace(config.extensions().secman(), permission.getFeatureFqn())) {
 
             messageService.warnUser("Cannot remove top-level namespace permissions for the admin role.");
             return false;
@@ -95,8 +95,8 @@ && isStickyAdminNamespace(config.getExtensions().getSecman(), permission.getFeat
     }
 
     private static boolean isStickyAdminNamespace(final Secman secman, final String featureFqn) {
-        var adminNamespacePermissions = secman.getSeed().getAdmin().getNamespacePermissions();
-        return _NullSafe.stream(adminNamespacePermissions.getSticky())
+        var adminNamespacePermissions = secman.seed().admin().namespacePermissions();
+        return _NullSafe.stream(adminNamespacePermissions.sticky())
                 .anyMatch(stickyPackage -> stickyPackage.equals(featureFqn));
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/fixtures/AbstractRoleAndPermissionsFixtureScript.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/fixtures/AbstractRoleAndPermissionsFixtureScript.java
index 436019cecf7..d904162fc52 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/fixtures/AbstractRoleAndPermissionsFixtureScript.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/fixtures/AbstractRoleAndPermissionsFixtureScript.java
@@ -108,7 +108,8 @@ protected void newPermissions(
             }
         }
 
-        if (causewayConfiguration.getExtensions().getSecman().getFixtureScripts().getAbstractRoleAndPermissionsFixtureScript().getUnknownFeatureIdCheckingPolicy().isFailFast()) {
+        if (causewayConfiguration.extensions().secman().fixtureScripts().abstractRoleAndPermissionsFixtureScript()
+            .unknownFeatureIdCheckingPolicy().isFailFast()) {
             // ensure all featureIds specified actually exist.
             var buf = new StringBuilder();
             for(var featureId : missingFeatureIds) {
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanAdminRoleAndPermissions.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanAdminRoleAndPermissions.java
index 8d73f79d81e..44b37ec14bc 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanAdminRoleAndPermissions.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanAdminRoleAndPermissions.java
@@ -35,9 +35,9 @@
 import org.apache.causeway.extensions.secman.applib.role.fixtures.AbstractRoleAndPermissionsFixtureScript;
 
 /**
- * Sets up a role for administrators (whose name is {@link Admin#getRoleName() configurable}
- * with an initial set of permissions being the union of {@link NamespacePermissions#getSticky()}
- * and {@link NamespacePermissions#getAdditional()}.
+ * Sets up a role for administrators (whose name is {@link Admin#roleName() configurable}
+ * with an initial set of permissions being the union of {@link NamespacePermissions#sticky()}
+ * and {@link NamespacePermissions#additional()}.
  *
  * @see Secman
  *
@@ -48,7 +48,7 @@ public class CausewayExtSecmanAdminRoleAndPermissions extends AbstractRoleAndPer
     private final Set<String> adminInitialPackagePermissions;
 
     public CausewayExtSecmanAdminRoleAndPermissions(final Secman config) {
-        super(config.getSeed().getAdmin().getRoleName(), "Administer security");
+        super(config.seed().admin().roleName(), "Administer security");
         this.adminInitialPackagePermissions = streamAdminNamespacePermissions(config)
                 .collect(Collectors.toCollection(LinkedHashSet::new)); // preserve order, discard duplicates
     }
@@ -67,10 +67,10 @@ protected void execute(final ExecutionContext executionContext) {
     // -- HELPER
 
     private static Stream<String> streamAdminNamespacePermissions(final Secman secman) {
-        var adminNamespacePermissions = secman.getSeed().getAdmin().getNamespacePermissions();
+        var adminNamespacePermissions = secman.seed().admin().namespacePermissions();
         return Stream.concat(
-                _NullSafe.stream(adminNamespacePermissions.getSticky()),
-                _NullSafe.stream(adminNamespacePermissions.getAdditional()));
+                _NullSafe.stream(adminNamespacePermissions.sticky()),
+                _NullSafe.stream(adminNamespacePermissions.additional()));
     }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanRegularUserRoleAndPermissions.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanRegularUserRoleAndPermissions.java
index 88844d97af1..d92656199e3 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanRegularUserRoleAndPermissions.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/role/seed/CausewayExtSecmanRegularUserRoleAndPermissions.java
@@ -38,27 +38,21 @@
 /**
  * Sets up a role for regular users of the security module.
  *
- * <p>
- * These permissions are intended to be sufficient for most regular users
+ * <p>These permissions are intended to be sufficient for most regular users
  * to have access to the 'safe' features provided by the core framework
  * (in particular, everything under the {@link CausewayModuleApplib#NAMESPACE causeway.applib}
  * namespace.
- * </p>
  *
- * <p>
- * That said, it does <i>not</i> include the ability to impersonate other users
+ * <p>That said, it does <i>not</i> include the ability to impersonate other users
  * (for this, grant the
  * {@link CausewaySudoImpersonateRoleAndPermissions}
  * role), and also does <i>not</i> include the ability to access the
  * configuration properties (for this, grant the
  * {@link CausewaySudoImpersonateRoleAndPermissions}
  * role).
- * </p>
  *
- * <p>
- * The permissions also provide the ability to lookup their user account using
+ * <p>The permissions also provide the ability to lookup their user account using
  * the {@link MeService}, and for viewing and maintaining their user details.
- * </p>
  *
  * @see CausewaySudoImpersonateRoleAndPermissions
  * @see CausewayConfigurationRoleAndPermissions
@@ -70,7 +64,7 @@ public class CausewayExtSecmanRegularUserRoleAndPermissions extends AbstractRole
     @Inject private CausewaySystemEnvironment env;
 
     public CausewayExtSecmanRegularUserRoleAndPermissions(final Secman config) {
-        super(config.getSeed().getRegularUser().getRoleName(), "Regular user of the security module");
+        super(config.seed().regularUser().roleName(), "Regular user of the security module");
     }
 
     @Override
@@ -99,12 +93,12 @@ protected void execute(final ExecutionContext executionContext) {
                 ApplicationFeatureId.newMember(ApplicationUser.LOGICAL_TYPE_NAME, "updateFaxNumber"),
 
                 // optionally allow access to documentation menu and pages, based on module presence
-                env.getIocContainer().containsBean("org.apache.causeway.extensions.docgen.help.CausewayModuleExtDocgenHelp")
+                env.springContextHolder().containsBean("org.apache.causeway.extensions.docgen.help.CausewayModuleExtDocgenHelp")
                     ? ApplicationFeatureId.newNamespace(CausewayExtDocgenRoleAndPermissions.NAMESPACE)
                     : null,
 
                 // optionally allow access to layout menu and pages, based on module presence
-                env.getIocContainer().containsBean("org.apache.causeway.extensions.layoutloaders.github.CausewayModuleExtLayoutLoadersGithub")
+                env.springContextHolder().containsBean("org.apache.causeway.extensions.layoutloaders.github.CausewayModuleExtLayoutLoadersGithub")
                     ? ApplicationFeatureId.newNamespace(CausewayExtLayoutLoadersRoleAndPermissions.NAMESPACE)
                     : null
         );
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/seed/scripts/SeedUsersAndRolesFixtureScript.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/seed/scripts/SeedUsersAndRolesFixtureScript.java
index e0f056d4636..b1c4804c509 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/seed/scripts/SeedUsersAndRolesFixtureScript.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/seed/scripts/SeedUsersAndRolesFixtureScript.java
@@ -84,16 +84,16 @@ public class SeedUsersAndRolesFixtureScript extends FixtureScript {
     @Override
     protected void execute(final ExecutionContext executionContext) {
 
-        var secmanConfig = config.getExtensions().getSecman();
+        var secmanConfig = config.extensions().secman();
         var persistenceStack = causewayBeanTypeRegistry.persistenceStack();
 
         // used as log message provider below - assuming file was found and is readable
-        final Supplier<String> yamlFilePath = ()->new File(secmanConfig.getSeed().getYamlFile()).getAbsolutePath();
+        final Supplier<String> yamlFilePath = ()->new File(secmanConfig.seed().yamlFile()).getAbsolutePath();
 
         // if a config option ..secman.seed.yamlFile is present,
         // try to use it as alternative seeding strategy,
         // then exit
-        final ApplicationSecurityDto dto = _Strings.nonEmpty(secmanConfig.getSeed().getYamlFile())
+        final ApplicationSecurityDto dto = _Strings.nonEmpty(secmanConfig.seed().yamlFile())
                 .map(File::new)
                 .filter(File::exists)
                 .filter(File::canRead)
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUser.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUser.java
index b2f42299a1b..ca9bca6c637 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUser.java
@@ -133,7 +133,7 @@ public static abstract class CollectionDomainEvent<T> extends CausewayModuleExtS
     }
 
     @Programmatic protected Secman getSecmanConfig() {
-        return config.getExtensions().getSecman();
+        return config.extensions().secman();
     }
 
     @ObjectSupport public String title() {
@@ -633,7 +633,7 @@ class Persistence {
         for (final RoleMemento role : currentUser.roles()) {
             final String roleName = role.name();
             if(adminRoleName.equals(roleName)) return true;
-            
+
             // format could also be realmName:roleName, eg. with Shiro
             // since we don't know what the realm's name is (depends on its configuration in shiro.ini),
             // simply check that the last part matches the role name.
@@ -654,7 +654,7 @@ class Persistence {
     }
 
     @Programmatic private String getAdminRoleName() {
-        var adminRoleName = _Strings.emptyToNull(getSecmanConfig().getSeed().getAdmin().getRoleName());
+        var adminRoleName = _Strings.emptyToNull(getSecmanConfig().seed().admin().roleName());
         // guard against empty admin role name
         _Assert.assertNotNull(adminRoleName, ()->"secman-config.seed.admin.role-name must not be empty");
         return adminRoleName;
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
index 04c8e66c72c..4416f1885cc 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
@@ -201,7 +201,7 @@ public void disable(final ApplicationUser user) {
     @Override
     public boolean isAdminUser(final ApplicationUser user) {
         return Objects.equals(
-                config.getExtensions().getSecman().getSeed().getAdmin().getUserName(),
+                config.extensions().secman().seed().admin().userName(),
                 user.getName());
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java
index 98f3392b2f1..9c8b5c2ea52 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java
@@ -67,7 +67,7 @@ public static class DomainEvent
 
     @MemberSupport public String disableAct() {
         if(applicationUserRepository.isAdminUser(target)) {
-            return String.format("Cannot lock the '%s' user.", config.getExtensions().getSecman().getSeed().getAdmin().getUserName());
+            return String.format("Cannot lock the '%s' user.", config.extensions().secman().seed().admin().userName());
         }
         return ApplicationUserStatus.canLock(target.getStatus())
                 ? null
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newDelegateUser.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newDelegateUser.java
index 911cbe21f29..648b867d56c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newDelegateUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newDelegateUser.java
@@ -93,7 +93,7 @@ public static class DomainEvent
     }
 
     @MemberSupport public ApplicationRole defaultInitialRole() {
-        var regularUserRoleName = config.getExtensions().getSecman().getSeed().getRegularUser().getRoleName();
+        var regularUserRoleName = config.extensions().secman().seed().regularUser().roleName();
         return applicationRoleRepository
                 .findByNameCached(regularUserRoleName)
                 .orElse(null);
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newLocalUser.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newLocalUser.java
index 440ad510181..9b94b9c1f06 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newLocalUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/man/mixins/ApplicationUserManager_newLocalUser.java
@@ -129,7 +129,7 @@ public static class DomainEvent
     }
 
     @MemberSupport public ApplicationRole defaultInitialRole() {
-        var regularUserRoleName = config.getExtensions().getSecman().getSeed().getRegularUser().getRoleName();
+        var regularUserRoleName = config.extensions().secman().seed().regularUser().roleName();
         return applicationRoleRepository
                 .findByNameCached(regularUserRoleName)
                 .orElse(null);
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/menu/MeService.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/menu/MeService.java
index 18960b8293e..70acfa1a668 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/menu/MeService.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/menu/MeService.java
@@ -113,7 +113,7 @@ public static class UserMenuMeActionAdvisor {
 
         @EventListener(UserMenu.me.ActionDomainEvent.class)
         public void on(final UserMenu.me.ActionDomainEvent event) {
-            switch (causewayConfiguration.getExtensions().getSecman().getUserMenuMeActionPolicy()) {
+            switch (causewayConfiguration.extensions().secman().userMenuMeActionPolicy()) {
                 case HIDE:
                     event.hide();
                     break;
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/seed/CausewayExtSecmanAdminUser.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/seed/CausewayExtSecmanAdminUser.java
index b4caad79f5b..d5754d66637 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/seed/CausewayExtSecmanAdminUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/user/seed/CausewayExtSecmanAdminUser.java
@@ -30,16 +30,16 @@
  */
 public class CausewayExtSecmanAdminUser extends AbstractUserAndRolesFixtureScript {
 
-    public CausewayExtSecmanAdminUser(final Secman config, String... roleNames) {
+    public CausewayExtSecmanAdminUser(final Secman config, final String... roleNames) {
         super(
-                config.getSeed().getAdmin().getUserName(),
-                config.getSeed().getAdmin().getPassword(),
+                config.seed().admin().userName(),
+                config.seed().admin().password(),
                 null,
                 GlobalTenancy.TENANCY_PATH,
                 AccountType.LOCAL,
                 Can.of(
-                        config.getSeed().getAdmin().getRoleName(),
-                        config.getSeed().getRegularUser().getRoleName()
+                        config.seed().admin().roleName(),
+                        config.seed().regularUser().roleName()
                 ).addAll(
                         Can.of(roleNames)
                 )
diff --git a/extensions/security/secman/delegated-springoauth2/src/main/java/org/apache/causeway/extensions/secman/delegated/springoauth2/dom/ApplicationUserAutoCreationService.java b/extensions/security/secman/delegated-springoauth2/src/main/java/org/apache/causeway/extensions/secman/delegated/springoauth2/dom/ApplicationUserAutoCreationService.java
index 1d404999871..46c6472e032 100644
--- a/extensions/security/secman/delegated-springoauth2/src/main/java/org/apache/causeway/extensions/secman/delegated/springoauth2/dom/ApplicationUserAutoCreationService.java
+++ b/extensions/security/secman/delegated-springoauth2/src/main/java/org/apache/causeway/extensions/secman/delegated/springoauth2/dom/ApplicationUserAutoCreationService.java
@@ -47,11 +47,10 @@
 import lombok.RequiredArgsConstructor;
 
 /**
- * This service automatically creates an {@link ApplicationUser} if an end-user successfully logged in via Oauth.
+ * This service automatically creates an {@link ApplicationUser} if an end-user successfully logged in via OAuth.
  *
- * <p>
- *     The initial set of rules are as per {@link CausewayConfiguration.Extensions.Secman.DelegatedUsers.AutoCreatePolicy}
- * </p>
+ * <p>The initial set of rules are as per
+ * {@link org.apache.causeway.core.config.CausewayConfiguration.Extensions.Secman.DelegatedUsers.AutoCreatePolicy}
  *
  * @since 2.0 {@index}
  */
@@ -80,15 +79,15 @@ public void onApplicationEvent(final InteractiveAuthenticationSuccessEvent event
         var username = oidcUser.getPreferredUsername();
         var email = oidcUser.getEmail();
 
-        var secmanConfig = causewayConfiguration.getExtensions().getSecman().getDelegatedUsers();
-        switch (secmanConfig.getAutoCreatePolicy()) {
+        var secmanConfig = causewayConfiguration.extensions().secman().delegatedUsers();
+        switch (secmanConfig.autoCreatePolicy()) {
             case DO_NOT_AUTO_CREATE:
                 break;
             case AUTO_CREATE_AS_LOCKED:
-                create(username, email, secmanConfig.getInitialRoleNames(), ApplicationUserStatus.LOCKED);
+                create(username, email, secmanConfig.initialRoleNames(), ApplicationUserStatus.LOCKED);
                 break;
             case AUTO_CREATE_AS_UNLOCKED:
-                create(username, email, secmanConfig.getInitialRoleNames(), ApplicationUserStatus.UNLOCKED);
+                create(username, email, secmanConfig.initialRoleNames(), ApplicationUserStatus.UNLOCKED);
                 break;
         }
     }
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java
index ed7b928b695..baa75a06ccd 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java
@@ -79,8 +79,8 @@ private PermissionsEvaluationServiceForSecman(
     @Inject
     public PermissionsEvaluationServiceForSecman(final CausewayConfiguration causewayConfiguration) {
         this.policy = Optional.ofNullable(
-                causewayConfiguration.getExtensions().getSecman().getPermissionsEvaluationPolicy())
-                .orElseGet(()->new Secman().getPermissionsEvaluationPolicy()); // use config defaults as fallback
+                causewayConfiguration.extensions().secman().permissionsEvaluationPolicy())
+                .orElseGet(()->Secman.defaults().permissionsEvaluationPolicy()); // use config defaults as fallback
         _Assert.assertNotNull(policy);
     }
 
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/userreg/UserRegistrationServiceForSecman.java b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/userreg/UserRegistrationServiceForSecman.java
index bbe245c7ccd..282aa71bdce 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/userreg/UserRegistrationServiceForSecman.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/userreg/UserRegistrationServiceForSecman.java
@@ -78,7 +78,7 @@ public void registerUser(
             applicationUser.setEmailAddress(emailAddress);
         }
 
-        causewayConfiguration.getExtensions().getSecman().getUserRegistration().getInitialRoleNames().stream()
+        causewayConfiguration.extensions().secman().userRegistration().initialRoleNames().stream()
                 .map(applicationRoleRepository::findByName)
                 .filter(Optional::isPresent)
                 .map(Optional::get)
diff --git a/extensions/security/sessionlog/applib/src/main/java/org/apache/causeway/extensions/sessionlog/applib/spiimpl/SessionLogServiceInitializer.java b/extensions/security/sessionlog/applib/src/main/java/org/apache/causeway/extensions/sessionlog/applib/spiimpl/SessionLogServiceInitializer.java
index ea0ff2c19bc..03c494a875d 100644
--- a/extensions/security/sessionlog/applib/src/main/java/org/apache/causeway/extensions/sessionlog/applib/spiimpl/SessionLogServiceInitializer.java
+++ b/extensions/security/sessionlog/applib/src/main/java/org/apache/causeway/extensions/sessionlog/applib/spiimpl/SessionLogServiceInitializer.java
@@ -53,7 +53,7 @@ public class SessionLogServiceInitializer implements MetamodelListener {
 
     @Override
     public void onMetamodelLoaded() {
-        if (!causewayConfiguration.getExtensions().getSessionLog().isAutoLogoutOnRestart()) {
+        if (!causewayConfiguration.extensions().sessionLog().autoLogoutOnRestart()) {
             return;
         }
 
diff --git a/extensions/vro/cors/impl/src/main/java/org/apache/causeway/extensions/cors/impl/CausewayModuleExtCors.java b/extensions/vro/cors/impl/src/main/java/org/apache/causeway/extensions/cors/impl/CausewayModuleExtCors.java
index 2ad73f7f3e0..a05b2dcc130 100644
--- a/extensions/vro/cors/impl/src/main/java/org/apache/causeway/extensions/cors/impl/CausewayModuleExtCors.java
+++ b/extensions/vro/cors/impl/src/main/java/org/apache/causeway/extensions/cors/impl/CausewayModuleExtCors.java
@@ -56,7 +56,7 @@ public FilterRegistrationBean<Filter> createCorsFilterRegistration(
         }
         log.info("Setting up CORS to filter resteasy-base at '{}' with {}",
                 restfulBase,
-                causewayConfiguration.getExtensions().getCors());
+                causewayConfiguration.extensions().cors());
 
         final FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
         filterRegistrationBean.setFilter(createCorsFilter(causewayConfiguration));
@@ -68,14 +68,14 @@ public FilterRegistrationBean<Filter> createCorsFilterRegistration(
 
     private CorsFilter createCorsFilter(final CausewayConfiguration configuration) {
 
-        var causewayCorsConfig = configuration.getExtensions().getCors();
+        var causewayCorsConfig = configuration.extensions().cors();
 
         var corsConfiguration = new CorsConfiguration();
-        corsConfiguration.setAllowCredentials(causewayCorsConfig.isAllowCredentials());
-        corsConfiguration.setAllowedHeaders(causewayCorsConfig.getAllowedHeaders());
... 2314 lines suppressed ...